@synergenius/flow-weaver 0.17.6 → 0.17.8
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/api/parse.d.ts +5 -0
- package/dist/api/parse.js +4 -0
- package/dist/ast/types.d.ts +2 -0
- package/dist/cli/commands/compile.js +2 -1
- package/dist/cli/commands/init.js +15 -9
- package/dist/cli/commands/validate.js +1 -1
- package/dist/cli/exports.d.ts +17 -0
- package/dist/cli/exports.js +23 -0
- package/dist/cli/flow-weaver.mjs +59021 -62127
- package/dist/cli/templates/index.js +8 -1
- package/dist/extensions/index.d.ts +10 -6
- package/dist/extensions/index.js +11 -6
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/dist/generator/index.d.ts +11 -0
- package/dist/generator/index.js +11 -0
- package/dist/parser.d.ts +7 -0
- package/dist/parser.js +29 -0
- package/package.json +11 -7
- package/dist/extensions/cicd/base-target.d.ts +0 -110
- package/dist/extensions/cicd/base-target.js +0 -397
- package/dist/extensions/cicd/detection.d.ts +0 -33
- package/dist/extensions/cicd/detection.js +0 -88
- package/dist/extensions/cicd/docs/cicd.md +0 -395
- package/dist/extensions/cicd/index.d.ts +0 -15
- package/dist/extensions/cicd/index.js +0 -15
- package/dist/extensions/cicd/register.d.ts +0 -11
- package/dist/extensions/cicd/register.js +0 -62
- package/dist/extensions/cicd/rules.d.ts +0 -30
- package/dist/extensions/cicd/rules.js +0 -288
- package/dist/extensions/cicd/tag-handler.d.ts +0 -14
- package/dist/extensions/cicd/tag-handler.js +0 -504
- package/dist/extensions/cicd/templates/cicd-docker.d.ts +0 -9
- package/dist/extensions/cicd/templates/cicd-docker.js +0 -110
- package/dist/extensions/cicd/templates/cicd-matrix.d.ts +0 -9
- package/dist/extensions/cicd/templates/cicd-matrix.js +0 -112
- package/dist/extensions/cicd/templates/cicd-multi-env.d.ts +0 -9
- package/dist/extensions/cicd/templates/cicd-multi-env.js +0 -126
- package/dist/extensions/cicd/templates/cicd-test-deploy.d.ts +0 -11
- package/dist/extensions/cicd/templates/cicd-test-deploy.js +0 -156
- package/dist/extensions/inngest/dev-mode.d.ts +0 -9
- package/dist/extensions/inngest/dev-mode.js +0 -213
- package/dist/extensions/inngest/generator.d.ts +0 -53
- package/dist/extensions/inngest/generator.js +0 -1176
- package/dist/extensions/inngest/index.d.ts +0 -2
- package/dist/extensions/inngest/index.js +0 -2
- package/dist/extensions/inngest/register.d.ts +0 -6
- package/dist/extensions/inngest/register.js +0 -23
- package/dist/extensions/inngest/templates/ai-agent-durable.d.ts +0 -8
- package/dist/extensions/inngest/templates/ai-agent-durable.js +0 -334
- package/dist/extensions/inngest/templates/ai-pipeline-durable.d.ts +0 -8
- package/dist/extensions/inngest/templates/ai-pipeline-durable.js +0 -326
|
@@ -1,504 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CI/CD tag handler for the TagHandlerRegistry.
|
|
3
|
-
*
|
|
4
|
-
* Handles all CI/CD annotation tags: @secret, @runner, @cache, @artifact,
|
|
5
|
-
* @environment, @matrix, @service, @concurrency, @job, @stage, @variables,
|
|
6
|
-
* @before_script, @tags, @includes, plus the synthetic _cicdTrigger tag
|
|
7
|
-
* for CI/CD trigger variants of @trigger.
|
|
8
|
-
*
|
|
9
|
-
* Each parser writes to ctx.deploy (the 'cicd' namespace slot) using the
|
|
10
|
-
* same structures as the former jsdoc-parser.ts private methods.
|
|
11
|
-
*/
|
|
12
|
-
export const cicdTagHandler = (tagName, comment, ctx) => {
|
|
13
|
-
const d = ctx.deploy;
|
|
14
|
-
const warnings = ctx.warnings;
|
|
15
|
-
const text = comment.trim();
|
|
16
|
-
switch (tagName) {
|
|
17
|
-
case 'secret':
|
|
18
|
-
parseSecret(text, d, warnings);
|
|
19
|
-
break;
|
|
20
|
-
case 'runner': {
|
|
21
|
-
const val = text.replace(/^["']|["']$/g, '');
|
|
22
|
-
if (val)
|
|
23
|
-
d['runner'] = val;
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
case 'cache':
|
|
27
|
-
parseCache(text, d, warnings);
|
|
28
|
-
break;
|
|
29
|
-
case 'artifact':
|
|
30
|
-
parseArtifact(text, d, warnings);
|
|
31
|
-
break;
|
|
32
|
-
case 'environment':
|
|
33
|
-
parseEnvironment(text, d, warnings);
|
|
34
|
-
break;
|
|
35
|
-
case 'matrix':
|
|
36
|
-
parseMatrix(text, d, warnings);
|
|
37
|
-
break;
|
|
38
|
-
case 'service':
|
|
39
|
-
parseService(text, d, warnings);
|
|
40
|
-
break;
|
|
41
|
-
case 'concurrency':
|
|
42
|
-
parseConcurrency(text, d, warnings);
|
|
43
|
-
break;
|
|
44
|
-
case 'job':
|
|
45
|
-
parseJob(text, d, warnings);
|
|
46
|
-
break;
|
|
47
|
-
case 'stage':
|
|
48
|
-
parseStage(text, d, warnings);
|
|
49
|
-
break;
|
|
50
|
-
case 'variables':
|
|
51
|
-
parseVariables(text, d, warnings);
|
|
52
|
-
break;
|
|
53
|
-
case 'before_script':
|
|
54
|
-
parseBeforeScript(text, d, warnings);
|
|
55
|
-
break;
|
|
56
|
-
case 'tags':
|
|
57
|
-
parseTags(text, d, warnings);
|
|
58
|
-
break;
|
|
59
|
-
case 'includes':
|
|
60
|
-
parseIncludes(text, d, warnings);
|
|
61
|
-
break;
|
|
62
|
-
case '_cicdTrigger':
|
|
63
|
-
parseCicdTrigger(text, d, warnings);
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
function parseSecret(text, d, warnings) {
|
|
68
|
-
if (!text) {
|
|
69
|
-
warnings.push('Empty @secret tag. Expected: @secret SECRET_NAME - description');
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
const match = text.match(/^(\S+)(.*?)(?:\s+-\s+(.*))?$/);
|
|
73
|
-
if (!match) {
|
|
74
|
-
warnings.push(`Invalid @secret format: @secret ${text}`);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const name = match[1];
|
|
78
|
-
const attrs = match[2] || '';
|
|
79
|
-
const description = match[3]?.trim();
|
|
80
|
-
const secret = { name };
|
|
81
|
-
if (description)
|
|
82
|
-
secret.description = description;
|
|
83
|
-
const scopeMatch = attrs.match(/scope\s*=\s*"([^"]+)"/);
|
|
84
|
-
if (scopeMatch)
|
|
85
|
-
secret.scope = scopeMatch[1];
|
|
86
|
-
const platformMatch = attrs.match(/platform\s*=\s*"([^"]+)"/);
|
|
87
|
-
if (platformMatch) {
|
|
88
|
-
const validPlatforms = ['github', 'gitlab', 'all'];
|
|
89
|
-
const p = platformMatch[1];
|
|
90
|
-
if (validPlatforms.includes(p)) {
|
|
91
|
-
secret.platform = p;
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
warnings.push(`Invalid @secret platform "${platformMatch[1]}". Must be: github, gitlab, or all`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const secrets = d['secrets'] ?? [];
|
|
98
|
-
secrets.push(secret);
|
|
99
|
-
d['secrets'] = secrets;
|
|
100
|
-
}
|
|
101
|
-
function parseCache(text, d, warnings) {
|
|
102
|
-
if (!text) {
|
|
103
|
-
warnings.push('Empty @cache tag. Expected: @cache npm key="package-lock.json"');
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const parts = text.match(/^(\S+)(.*)?$/);
|
|
107
|
-
if (!parts) {
|
|
108
|
-
warnings.push(`Invalid @cache format: @cache ${text}`);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
const cache = { strategy: parts[1] };
|
|
112
|
-
const rest = parts[2] || '';
|
|
113
|
-
const keyMatch = rest.match(/key\s*=\s*"([^"]+)"/);
|
|
114
|
-
if (keyMatch)
|
|
115
|
-
cache.key = keyMatch[1];
|
|
116
|
-
const pathMatch = rest.match(/path\s*=\s*"([^"]+)"/);
|
|
117
|
-
if (pathMatch)
|
|
118
|
-
cache.path = pathMatch[1];
|
|
119
|
-
const caches = d['caches'] ?? [];
|
|
120
|
-
caches.push(cache);
|
|
121
|
-
d['caches'] = caches;
|
|
122
|
-
}
|
|
123
|
-
function parseArtifact(text, d, warnings) {
|
|
124
|
-
if (!text) {
|
|
125
|
-
warnings.push('Empty @artifact tag. Expected: @artifact name path="dist/"');
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
const nameMatch = text.match(/^(\S+)/);
|
|
129
|
-
if (!nameMatch) {
|
|
130
|
-
warnings.push(`Invalid @artifact format: @artifact ${text}`);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
const pathMatch = text.match(/path\s*=\s*"([^"]+)"/);
|
|
134
|
-
if (!pathMatch) {
|
|
135
|
-
warnings.push(`@artifact requires path="...": @artifact ${text}`);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
const artifact = {
|
|
139
|
-
name: nameMatch[1],
|
|
140
|
-
path: pathMatch[1],
|
|
141
|
-
};
|
|
142
|
-
const retentionMatch = text.match(/retention\s*=\s*(\d+)/);
|
|
143
|
-
if (retentionMatch)
|
|
144
|
-
artifact.retention = parseInt(retentionMatch[1], 10);
|
|
145
|
-
const artifacts = d['artifacts'] ?? [];
|
|
146
|
-
artifacts.push(artifact);
|
|
147
|
-
d['artifacts'] = artifacts;
|
|
148
|
-
}
|
|
149
|
-
function parseEnvironment(text, d, warnings) {
|
|
150
|
-
if (!text) {
|
|
151
|
-
warnings.push('Empty @environment tag. Expected: @environment production url="https://app.com"');
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
const nameMatch = text.match(/^(\S+)/);
|
|
155
|
-
if (!nameMatch) {
|
|
156
|
-
warnings.push(`Invalid @environment format: @environment ${text}`);
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
const env = { name: nameMatch[1] };
|
|
160
|
-
const urlMatch = text.match(/url\s*=\s*"([^"]+)"/);
|
|
161
|
-
if (urlMatch)
|
|
162
|
-
env.url = urlMatch[1];
|
|
163
|
-
const reviewersMatch = text.match(/reviewers\s*=\s*(\d+)/);
|
|
164
|
-
if (reviewersMatch)
|
|
165
|
-
env.reviewers = parseInt(reviewersMatch[1], 10);
|
|
166
|
-
const environments = d['environments'] ?? [];
|
|
167
|
-
environments.push(env);
|
|
168
|
-
d['environments'] = environments;
|
|
169
|
-
}
|
|
170
|
-
function parseMatrix(text, d, warnings) {
|
|
171
|
-
if (!text) {
|
|
172
|
-
warnings.push('Empty @matrix tag. Expected: @matrix node="18,20,22" os="ubuntu-latest"');
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
const matrix = d['matrix'] ?? { dimensions: {} };
|
|
176
|
-
d['matrix'] = matrix;
|
|
177
|
-
const isInclude = text.startsWith('include ');
|
|
178
|
-
const isExclude = text.startsWith('exclude ');
|
|
179
|
-
if (isInclude || isExclude) {
|
|
180
|
-
const rest = text.slice(isInclude ? 8 : 8);
|
|
181
|
-
const entry = {};
|
|
182
|
-
const kvRegex = /(\w[\w-]*)\s*=\s*"([^"]+)"/g;
|
|
183
|
-
let m;
|
|
184
|
-
while ((m = kvRegex.exec(rest)) !== null) {
|
|
185
|
-
entry[m[1]] = m[2];
|
|
186
|
-
}
|
|
187
|
-
if (Object.keys(entry).length > 0) {
|
|
188
|
-
if (isInclude) {
|
|
189
|
-
matrix.include = matrix.include || [];
|
|
190
|
-
matrix.include.push(entry);
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
matrix.exclude = matrix.exclude || [];
|
|
194
|
-
matrix.exclude.push(entry);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
const kvRegex = /(\w[\w-]*)\s*=\s*"([^"]+)"/g;
|
|
200
|
-
let m;
|
|
201
|
-
while ((m = kvRegex.exec(text)) !== null) {
|
|
202
|
-
matrix.dimensions[m[1]] = m[2].split(',').map(v => v.trim());
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
function parseService(text, d, warnings) {
|
|
206
|
-
if (!text) {
|
|
207
|
-
warnings.push('Empty @service tag. Expected: @service postgres image="postgres:16"');
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
const nameMatch = text.match(/^(\S+)/);
|
|
211
|
-
if (!nameMatch) {
|
|
212
|
-
warnings.push(`Invalid @service format: @service ${text}`);
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
const imageMatch = text.match(/image\s*=\s*"([^"]+)"/);
|
|
216
|
-
if (!imageMatch) {
|
|
217
|
-
warnings.push(`@service requires image="...": @service ${text}`);
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
const svc = {
|
|
221
|
-
name: nameMatch[1],
|
|
222
|
-
image: imageMatch[1],
|
|
223
|
-
};
|
|
224
|
-
const envMatch = text.match(/env\s*=\s*"([^"]+)"/);
|
|
225
|
-
if (envMatch) {
|
|
226
|
-
svc.env = {};
|
|
227
|
-
for (const pair of envMatch[1].split(',')) {
|
|
228
|
-
const [k, ...vParts] = pair.split('=');
|
|
229
|
-
if (k && vParts.length > 0)
|
|
230
|
-
svc.env[k.trim()] = vParts.join('=').trim();
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
const portsMatch = text.match(/ports\s*=\s*"([^"]+)"/);
|
|
234
|
-
if (portsMatch) {
|
|
235
|
-
svc.ports = portsMatch[1].split(',').map(p => p.trim());
|
|
236
|
-
}
|
|
237
|
-
const services = d['services'] ?? [];
|
|
238
|
-
services.push(svc);
|
|
239
|
-
d['services'] = services;
|
|
240
|
-
}
|
|
241
|
-
function parseConcurrency(text, d, warnings) {
|
|
242
|
-
if (!text) {
|
|
243
|
-
warnings.push('Empty @concurrency tag. Expected: @concurrency group="deploy"');
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
const groupMatch = text.match(/group\s*=\s*"([^"]+)"/);
|
|
247
|
-
if (!groupMatch) {
|
|
248
|
-
const bareMatch = text.match(/^(\S+)/);
|
|
249
|
-
if (bareMatch) {
|
|
250
|
-
d['concurrency'] = { group: bareMatch[1], cancelInProgress: false };
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
warnings.push(`Invalid @concurrency format: @concurrency ${text}`);
|
|
254
|
-
}
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
const cancelMatch = text.match(/cancel-in-progress\s*=\s*(true|false)/);
|
|
258
|
-
d['concurrency'] = {
|
|
259
|
-
group: groupMatch[1],
|
|
260
|
-
cancelInProgress: cancelMatch ? cancelMatch[1] === 'true' : false,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
function parseJob(text, d, warnings) {
|
|
264
|
-
if (!text) {
|
|
265
|
-
warnings.push('Empty @job tag. Expected: @job <name> key=value ...');
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
const spaceIdx = text.indexOf(' ');
|
|
269
|
-
const jobId = spaceIdx === -1 ? text : text.substring(0, spaceIdx);
|
|
270
|
-
const rest = spaceIdx === -1 ? '' : text.substring(spaceIdx + 1);
|
|
271
|
-
const jobs = d['jobs'] ?? [];
|
|
272
|
-
d['jobs'] = jobs;
|
|
273
|
-
let jc = jobs.find(j => j.id === jobId);
|
|
274
|
-
if (!jc) {
|
|
275
|
-
jc = { id: jobId };
|
|
276
|
-
jobs.push(jc);
|
|
277
|
-
}
|
|
278
|
-
if (!rest)
|
|
279
|
-
return;
|
|
280
|
-
const kvRegex = /(\w[\w-]*)\s*=\s*(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)'|([\S]+))/g;
|
|
281
|
-
let match;
|
|
282
|
-
while ((match = kvRegex.exec(rest)) !== null) {
|
|
283
|
-
const key = match[1];
|
|
284
|
-
const value = match[2] !== undefined ? match[2] : match[3] !== undefined ? match[3] : match[4];
|
|
285
|
-
switch (key) {
|
|
286
|
-
case 'retry': {
|
|
287
|
-
const n = parseInt(value, 10);
|
|
288
|
-
if (!isNaN(n) && n >= 0)
|
|
289
|
-
jc.retry = n;
|
|
290
|
-
else
|
|
291
|
-
warnings.push(`Invalid retry value "${value}" in @job ${jobId}`);
|
|
292
|
-
break;
|
|
293
|
-
}
|
|
294
|
-
case 'allow_failure':
|
|
295
|
-
jc.allowFailure = value === 'true';
|
|
296
|
-
break;
|
|
297
|
-
case 'timeout':
|
|
298
|
-
jc.timeout = value;
|
|
299
|
-
break;
|
|
300
|
-
case 'runner':
|
|
301
|
-
jc.runner = value;
|
|
302
|
-
break;
|
|
303
|
-
case 'tags':
|
|
304
|
-
jc.tags = value.split(',').map(t => t.trim()).filter(Boolean);
|
|
305
|
-
break;
|
|
306
|
-
case 'coverage':
|
|
307
|
-
jc.coverage = value;
|
|
308
|
-
break;
|
|
309
|
-
case 'extends':
|
|
310
|
-
jc.extends = value;
|
|
311
|
-
break;
|
|
312
|
-
case 'variables': {
|
|
313
|
-
jc.variables = jc.variables || {};
|
|
314
|
-
for (const pair of value.split(',')) {
|
|
315
|
-
const eqIdx = pair.indexOf('=');
|
|
316
|
-
if (eqIdx > 0) {
|
|
317
|
-
jc.variables[pair.substring(0, eqIdx).trim()] = pair.substring(eqIdx + 1).trim();
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
break;
|
|
321
|
-
}
|
|
322
|
-
case 'before_script':
|
|
323
|
-
jc.beforeScript = value.split(',').map(s => s.trim()).filter(Boolean);
|
|
324
|
-
break;
|
|
325
|
-
case 'rules':
|
|
326
|
-
jc.rules = jc.rules || [];
|
|
327
|
-
jc.rules.push({ if: value });
|
|
328
|
-
break;
|
|
329
|
-
case 'when': {
|
|
330
|
-
// Modifier: sets `when` on the last rule, or creates a standalone rule
|
|
331
|
-
jc.rules = jc.rules || [];
|
|
332
|
-
if (jc.rules.length === 0)
|
|
333
|
-
jc.rules.push({});
|
|
334
|
-
jc.rules[jc.rules.length - 1].when = value;
|
|
335
|
-
break;
|
|
336
|
-
}
|
|
337
|
-
case 'changes': {
|
|
338
|
-
// Modifier: sets `changes` on the last rule
|
|
339
|
-
jc.rules = jc.rules || [];
|
|
340
|
-
if (jc.rules.length === 0)
|
|
341
|
-
jc.rules.push({});
|
|
342
|
-
jc.rules[jc.rules.length - 1].changes = value.split(',').map(s => s.trim()).filter(Boolean);
|
|
343
|
-
break;
|
|
344
|
-
}
|
|
345
|
-
case 'reports': {
|
|
346
|
-
jc.reports = jc.reports || [];
|
|
347
|
-
for (const pair of value.split(',')) {
|
|
348
|
-
const eqIdx = pair.indexOf('=');
|
|
349
|
-
if (eqIdx > 0) {
|
|
350
|
-
jc.reports.push({
|
|
351
|
-
type: pair.substring(0, eqIdx).trim(),
|
|
352
|
-
path: pair.substring(eqIdx + 1).trim(),
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
break;
|
|
357
|
-
}
|
|
358
|
-
default:
|
|
359
|
-
warnings.push(`Unknown @job attribute "${key}" in @job ${jobId}`);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
function parseStage(text, d, warnings) {
|
|
364
|
-
if (!text) {
|
|
365
|
-
warnings.push('Empty @stage tag. Expected: @stage <name>');
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
const stages = d['stages'] ?? [];
|
|
369
|
-
d['stages'] = stages;
|
|
370
|
-
const name = text.split(/\s+/)[0];
|
|
371
|
-
if (!stages.some(s => s.name === name)) {
|
|
372
|
-
stages.push({ name });
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
function parseVariables(text, d, warnings) {
|
|
376
|
-
if (!text) {
|
|
377
|
-
warnings.push('Empty @variables tag. Expected: @variables KEY=VALUE');
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
const variables = d['variables'] ?? {};
|
|
381
|
-
d['variables'] = variables;
|
|
382
|
-
const kvRegex = /(\w[\w-]*)\s*=\s*(?:"((?:[^"\\]|\\.)*)"|(\S+))/g;
|
|
383
|
-
let match;
|
|
384
|
-
while ((match = kvRegex.exec(text)) !== null) {
|
|
385
|
-
variables[match[1]] = match[2] !== undefined ? match[2] : match[3];
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
function parseBeforeScript(text, d, warnings) {
|
|
389
|
-
if (!text) {
|
|
390
|
-
warnings.push('Empty @before_script tag. Expected: @before_script "cmd1" "cmd2"');
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
const beforeScript = d['beforeScript'] ?? [];
|
|
394
|
-
d['beforeScript'] = beforeScript;
|
|
395
|
-
const strRegex = /"((?:[^"\\]|\\.)*)"/g;
|
|
396
|
-
let match;
|
|
397
|
-
const commands = [];
|
|
398
|
-
while ((match = strRegex.exec(text)) !== null) {
|
|
399
|
-
commands.push(match[1]);
|
|
400
|
-
}
|
|
401
|
-
if (commands.length > 0) {
|
|
402
|
-
beforeScript.push(...commands);
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
beforeScript.push(text);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
function parseTags(text, d, warnings) {
|
|
409
|
-
if (!text) {
|
|
410
|
-
warnings.push('Empty @tags tag. Expected: @tags tag1 tag2');
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
d['tags'] = text.split(/[\s,]+/).filter(Boolean);
|
|
414
|
-
}
|
|
415
|
-
function parseIncludes(text, d, warnings) {
|
|
416
|
-
if (!text) {
|
|
417
|
-
warnings.push('Empty @includes tag. Expected: @includes local="path"');
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
const includes = d['includes'] ?? [];
|
|
421
|
-
d['includes'] = includes;
|
|
422
|
-
const localMatch = text.match(/local\s*=\s*"([^"]+)"/);
|
|
423
|
-
const templateMatch = text.match(/template\s*=\s*"([^"]+)"/);
|
|
424
|
-
const remoteMatch = text.match(/remote\s*=\s*"([^"]+)"/);
|
|
425
|
-
const projectMatch = text.match(/project\s*=\s*"([^"]+)"/);
|
|
426
|
-
if (localMatch) {
|
|
427
|
-
includes.push({ type: 'local', file: localMatch[1] });
|
|
428
|
-
}
|
|
429
|
-
else if (templateMatch) {
|
|
430
|
-
includes.push({ type: 'template', file: templateMatch[1] });
|
|
431
|
-
}
|
|
432
|
-
else if (remoteMatch) {
|
|
433
|
-
includes.push({ type: 'remote', file: remoteMatch[1] });
|
|
434
|
-
}
|
|
435
|
-
else if (projectMatch) {
|
|
436
|
-
const fileMatch = text.match(/file\s*=\s*"([^"]+)"/);
|
|
437
|
-
const refMatch = text.match(/ref\s*=\s*"([^"]+)"/);
|
|
438
|
-
if (fileMatch) {
|
|
439
|
-
includes.push({
|
|
440
|
-
type: 'project',
|
|
441
|
-
file: fileMatch[1],
|
|
442
|
-
project: projectMatch[1],
|
|
443
|
-
...(refMatch && { ref: refMatch[1] }),
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
else {
|
|
447
|
-
warnings.push(`@includes project requires file="...": @includes ${text}`);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
else {
|
|
451
|
-
warnings.push(`Invalid @includes format: @includes ${text}. Expected: @includes local="path" or @includes template="name"`);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
function parseCicdTrigger(text, d, warnings) {
|
|
455
|
-
// Check for CI/CD trigger types: push, pull_request, dispatch, tag, schedule
|
|
456
|
-
const cicdMatch = text.match(/^(push|pull_request|dispatch|tag|schedule)\b(.*)?$/);
|
|
457
|
-
if (!cicdMatch) {
|
|
458
|
-
// Also detect standalone cron="..." as schedule trigger
|
|
459
|
-
const cronOnlyMatch = text.match(/^cron\s*=\s*"([^"]+)"/);
|
|
460
|
-
if (cronOnlyMatch) {
|
|
461
|
-
const triggers = d['triggers'] ?? [];
|
|
462
|
-
d['triggers'] = triggers;
|
|
463
|
-
triggers.push({ type: 'schedule', cron: cronOnlyMatch[1] });
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
warnings.push(`Invalid CI/CD trigger format: @trigger ${text}`);
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
const type = cicdMatch[1];
|
|
470
|
-
const rest = (cicdMatch[2] || '').trim();
|
|
471
|
-
const triggerType = type;
|
|
472
|
-
const trigger = { type: triggerType };
|
|
473
|
-
const branchesMatch = rest.match(/branches\s*=\s*"([^"]+)"/);
|
|
474
|
-
if (branchesMatch)
|
|
475
|
-
trigger.branches = branchesMatch[1].split(',').map(b => b.trim());
|
|
476
|
-
const pathsMatch = rest.match(/(?<![a-z])paths\s*=\s*"([^"]+)"/);
|
|
477
|
-
if (pathsMatch)
|
|
478
|
-
trigger.paths = pathsMatch[1].split(',').map(p => p.trim());
|
|
479
|
-
const pathsIgnoreMatch = rest.match(/paths-ignore\s*=\s*"([^"]+)"/);
|
|
480
|
-
if (pathsIgnoreMatch)
|
|
481
|
-
trigger.pathsIgnore = pathsIgnoreMatch[1].split(',').map(p => p.trim());
|
|
482
|
-
const typesMatch = rest.match(/types\s*=\s*"([^"]+)"/);
|
|
483
|
-
if (typesMatch)
|
|
484
|
-
trigger.types = typesMatch[1].split(',').map(t => t.trim());
|
|
485
|
-
const patternMatch = rest.match(/pattern\s*=\s*"([^"]+)"/);
|
|
486
|
-
if (patternMatch)
|
|
487
|
-
trigger.pattern = patternMatch[1];
|
|
488
|
-
const cronMatch = rest.match(/cron\s*=\s*"([^"]+)"/);
|
|
489
|
-
if (cronMatch)
|
|
490
|
-
trigger.cron = cronMatch[1];
|
|
491
|
-
const inputsMatch = rest.match(/inputs\s*=\s*"([^"]+)"/);
|
|
492
|
-
if (inputsMatch) {
|
|
493
|
-
trigger.inputs = {};
|
|
494
|
-
for (const input of inputsMatch[1].split(',')) {
|
|
495
|
-
const name = input.trim();
|
|
496
|
-
if (name)
|
|
497
|
-
trigger.inputs[name] = {};
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
const triggers = d['triggers'] ?? [];
|
|
501
|
-
d['triggers'] = triggers;
|
|
502
|
-
triggers.push(trigger);
|
|
503
|
-
}
|
|
504
|
-
//# sourceMappingURL=tag-handler.js.map
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CI/CD: Docker Pipeline template
|
|
3
|
-
*
|
|
4
|
-
* Generates a Flow Weaver workflow for building and pushing Docker images:
|
|
5
|
-
* checkout → docker-login → docker-build → docker-push
|
|
6
|
-
*/
|
|
7
|
-
import type { WorkflowTemplate } from '../../../cli/templates/index.js';
|
|
8
|
-
export declare const cicdDockerTemplate: WorkflowTemplate;
|
|
9
|
-
//# sourceMappingURL=cicd-docker.d.ts.map
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CI/CD: Docker Pipeline template
|
|
3
|
-
*
|
|
4
|
-
* Generates a Flow Weaver workflow for building and pushing Docker images:
|
|
5
|
-
* checkout → docker-login → docker-build → docker-push
|
|
6
|
-
*/
|
|
7
|
-
const configSchema = {
|
|
8
|
-
platform: {
|
|
9
|
-
type: 'select',
|
|
10
|
-
label: 'CI/CD Platform',
|
|
11
|
-
default: 'github-actions',
|
|
12
|
-
options: [
|
|
13
|
-
{ value: 'github-actions', label: 'GitHub Actions' },
|
|
14
|
-
{ value: 'gitlab-ci', label: 'GitLab CI' },
|
|
15
|
-
],
|
|
16
|
-
},
|
|
17
|
-
registry: {
|
|
18
|
-
type: 'select',
|
|
19
|
-
label: 'Container Registry',
|
|
20
|
-
default: 'ghcr',
|
|
21
|
-
options: [
|
|
22
|
-
{ value: 'ghcr', label: 'GitHub Container Registry (ghcr.io)' },
|
|
23
|
-
{ value: 'dockerhub', label: 'Docker Hub' },
|
|
24
|
-
{ value: 'ecr', label: 'AWS ECR' },
|
|
25
|
-
{ value: 'gcr', label: 'Google Container Registry' },
|
|
26
|
-
],
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
export const cicdDockerTemplate = {
|
|
30
|
-
id: 'cicd-docker',
|
|
31
|
-
name: 'CI/CD: Docker Pipeline',
|
|
32
|
-
description: 'Build and push Docker images to a container registry',
|
|
33
|
-
category: 'automation',
|
|
34
|
-
configSchema,
|
|
35
|
-
generate: (opts) => {
|
|
36
|
-
const name = opts.workflowName || 'dockerPipeline';
|
|
37
|
-
const registry = opts.config?.registry || 'ghcr';
|
|
38
|
-
const registrySecrets = {
|
|
39
|
-
ghcr: ' * @secret GITHUB_TOKEN - GitHub token for ghcr.io',
|
|
40
|
-
dockerhub: ' * @secret DOCKER_USERNAME - Docker Hub username\n * @secret DOCKER_PASSWORD - Docker Hub password',
|
|
41
|
-
ecr: ' * @secret AWS_ACCESS_KEY_ID - AWS access key\n * @secret AWS_SECRET_ACCESS_KEY - AWS secret key',
|
|
42
|
-
gcr: ' * @secret GCR_KEY - Google Cloud service account key',
|
|
43
|
-
};
|
|
44
|
-
const registryConnect = {
|
|
45
|
-
ghcr: ' * @connect secret:GITHUB_TOKEN -> login.token',
|
|
46
|
-
dockerhub: ' * @connect secret:DOCKER_USERNAME -> login.username\n * @connect secret:DOCKER_PASSWORD -> login.password',
|
|
47
|
-
ecr: ' * @connect secret:AWS_ACCESS_KEY_ID -> login.accessKey\n * @connect secret:AWS_SECRET_ACCESS_KEY -> login.secretKey',
|
|
48
|
-
gcr: ' * @connect secret:GCR_KEY -> login.serviceAccountKey',
|
|
49
|
-
};
|
|
50
|
-
return `/** @flowWeaver nodeType
|
|
51
|
-
* @expression
|
|
52
|
-
* @label Checkout code
|
|
53
|
-
*/
|
|
54
|
-
function checkout(): { repo: string } { return { repo: '.' }; }
|
|
55
|
-
|
|
56
|
-
/** @flowWeaver nodeType
|
|
57
|
-
* @expression
|
|
58
|
-
* @label Docker login
|
|
59
|
-
*/
|
|
60
|
-
function dockerLogin(${registry === 'dockerhub' ? 'username: string = \'\', password: string = \'\'' : registry === 'ecr' ? 'accessKey: string = \'\', secretKey: string = \'\'' : registry === 'gcr' ? 'serviceAccountKey: string = \'\'' : 'token: string = \'\''}): { loggedIn: boolean } { return { loggedIn: true }; }
|
|
61
|
-
|
|
62
|
-
/** @flowWeaver nodeType
|
|
63
|
-
* @expression
|
|
64
|
-
* @label Docker build
|
|
65
|
-
*/
|
|
66
|
-
function dockerBuild(): { imageTag: string } { return { imageTag: 'latest' }; }
|
|
67
|
-
|
|
68
|
-
/** @flowWeaver nodeType
|
|
69
|
-
* @expression
|
|
70
|
-
* @label Docker push
|
|
71
|
-
*/
|
|
72
|
-
function dockerPush(): { digest: string } { return { digest: 'sha256:abc123' }; }
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* @flowWeaver workflow
|
|
76
|
-
* @trigger push branches="main" paths="Dockerfile,src/**"
|
|
77
|
-
* @trigger tag pattern="v*"
|
|
78
|
-
* @runner ubuntu-latest
|
|
79
|
-
${registrySecrets[registry]}
|
|
80
|
-
*
|
|
81
|
-
* @node co checkout [job: "build"] [position: 270 0]
|
|
82
|
-
* @node login dockerLogin [job: "build"] [position: 540 0]
|
|
83
|
-
* @node build dockerBuild [job: "build"] [position: 810 0]
|
|
84
|
-
* @node push dockerPush [job: "build"] [position: 1080 0]
|
|
85
|
-
*
|
|
86
|
-
* @path Start -> co -> login -> build -> push -> Exit
|
|
87
|
-
* @position Start 0 0
|
|
88
|
-
* @position Exit 1350 0
|
|
89
|
-
${registryConnect[registry]}
|
|
90
|
-
* @connect push.digest -> Exit.imageDigest
|
|
91
|
-
*
|
|
92
|
-
* @param execute [order:-1] - Execute
|
|
93
|
-
* @param params [order:0] - Params
|
|
94
|
-
* @returns onSuccess [order:-2] - On Success
|
|
95
|
-
* @returns onFailure [order:-1] - On Failure
|
|
96
|
-
* @returns imageDigest [order:0] - Docker image digest
|
|
97
|
-
*/
|
|
98
|
-
export async function ${name}(
|
|
99
|
-
execute: boolean,
|
|
100
|
-
params: Record<string, never> = {},
|
|
101
|
-
): Promise<{ onSuccess: boolean; onFailure: boolean; imageDigest: string | null }> {
|
|
102
|
-
// @flow-weaver-body-start
|
|
103
|
-
throw new Error('Compile with: npx flow-weaver compile <this-file>');
|
|
104
|
-
// @flow-weaver-body-end
|
|
105
|
-
return { onSuccess: false, onFailure: true, imageDigest: null };
|
|
106
|
-
}
|
|
107
|
-
`;
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
//# sourceMappingURL=cicd-docker.js.map
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CI/CD: Matrix Testing template
|
|
3
|
-
*
|
|
4
|
-
* Generates a pipeline with matrix strategy for testing across
|
|
5
|
-
* multiple Node.js versions and/or operating systems.
|
|
6
|
-
*/
|
|
7
|
-
import type { WorkflowTemplate } from '../../../cli/templates/index.js';
|
|
8
|
-
export declare const cicdMatrixTemplate: WorkflowTemplate;
|
|
9
|
-
//# sourceMappingURL=cicd-matrix.d.ts.map
|