@sverka/compiler 0.1.13 → 0.1.15
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/index.d.mts +381 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +595 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { stringify } from "yaml";
|
|
1
|
+
import { parse, stringify } from "yaml";
|
|
2
2
|
import { expandPipelineCalls } from "@sverka/workflow";
|
|
3
3
|
//#region src/plugin/errors.ts
|
|
4
4
|
var PluginError = class extends Error {
|
|
@@ -3233,6 +3233,599 @@ function compileGitlab(graph) {
|
|
|
3233
3233
|
return new GitlabTarget().compile(graph);
|
|
3234
3234
|
}
|
|
3235
3235
|
//#endregion
|
|
3236
|
-
|
|
3236
|
+
//#region src/github/importer.ts
|
|
3237
|
+
/**
|
|
3238
|
+
* Import a GitHub Actions workflow YAML string into a Definition Graph.
|
|
3239
|
+
* Lossy: unmappable constructs produce diagnostics.
|
|
3240
|
+
*/
|
|
3241
|
+
function importGithub(source) {
|
|
3242
|
+
return importGithubWithDiagnostics(source).graph;
|
|
3243
|
+
}
|
|
3244
|
+
/**
|
|
3245
|
+
* Import a GitHub Actions workflow YAML string with diagnostics.
|
|
3246
|
+
*/
|
|
3247
|
+
function importGithubWithDiagnostics(source) {
|
|
3248
|
+
const diagnostics = [];
|
|
3249
|
+
let doc;
|
|
3250
|
+
try {
|
|
3251
|
+
doc = parse(source);
|
|
3252
|
+
} catch (err) {
|
|
3253
|
+
throw new GithubTargetError(`failed to parse GitHub workflow YAML: ${err instanceof Error ? err.message : String(err)}`, "IMPORT_FAILED");
|
|
3254
|
+
}
|
|
3255
|
+
if (!doc || typeof doc !== "object") throw new GithubTargetError("GitHub workflow YAML is not a valid object", "IMPORT_FAILED");
|
|
3256
|
+
const projectId = "imported";
|
|
3257
|
+
const steps = [];
|
|
3258
|
+
const entries = [];
|
|
3259
|
+
const trigger = parseTrigger(doc.on, diagnostics);
|
|
3260
|
+
const jobs = doc.jobs;
|
|
3261
|
+
if (jobs) for (const [jobId, job] of Object.entries(jobs)) {
|
|
3262
|
+
if (job === null || typeof job !== "object") {
|
|
3263
|
+
diagnostics.push({
|
|
3264
|
+
severity: "warn",
|
|
3265
|
+
message: `job '${jobId}' is not a valid object, skipping`
|
|
3266
|
+
});
|
|
3267
|
+
continue;
|
|
3268
|
+
}
|
|
3269
|
+
steps.push(parseJob(jobId, job, diagnostics));
|
|
3270
|
+
}
|
|
3271
|
+
if (steps.length > 0) entries.push({
|
|
3272
|
+
id: "ci/default",
|
|
3273
|
+
trigger,
|
|
3274
|
+
roots: steps.map((s) => s.id)
|
|
3275
|
+
});
|
|
3276
|
+
return {
|
|
3277
|
+
graph: { project: {
|
|
3278
|
+
id: projectId,
|
|
3279
|
+
pipelines: [{
|
|
3280
|
+
id: "ci",
|
|
3281
|
+
inputs: {},
|
|
3282
|
+
entries,
|
|
3283
|
+
steps,
|
|
3284
|
+
outputs: []
|
|
3285
|
+
}]
|
|
3286
|
+
} },
|
|
3287
|
+
diagnostics
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
/**
|
|
3291
|
+
* Parse the `on:` field of a GitHub workflow into a Sverka Trigger.
|
|
3292
|
+
*/
|
|
3293
|
+
function parseTrigger(onField, diagnostics) {
|
|
3294
|
+
if (typeof onField === "string") return parseStringTrigger(onField);
|
|
3295
|
+
if (onField && typeof onField === "object") return parseObjectTrigger(onField, diagnostics);
|
|
3296
|
+
return { kind: "push" };
|
|
3297
|
+
}
|
|
3298
|
+
function parseStringTrigger(onField) {
|
|
3299
|
+
if (onField === "push") return { kind: "push" };
|
|
3300
|
+
if (onField === "pull_request") return { kind: "changeRequest" };
|
|
3301
|
+
return { kind: "manual" };
|
|
3302
|
+
}
|
|
3303
|
+
function parseObjectTrigger(on, diagnostics) {
|
|
3304
|
+
if ("push" in on) return { kind: "push" };
|
|
3305
|
+
if ("pull_request" in on) return { kind: "changeRequest" };
|
|
3306
|
+
if ("workflow_dispatch" in on) return { kind: "manual" };
|
|
3307
|
+
if ("schedule" in on) {
|
|
3308
|
+
diagnostics.push({
|
|
3309
|
+
severity: "info",
|
|
3310
|
+
message: "schedule trigger imported as manual"
|
|
3311
|
+
});
|
|
3312
|
+
return { kind: "manual" };
|
|
3313
|
+
}
|
|
3314
|
+
return { kind: "manual" };
|
|
3315
|
+
}
|
|
3316
|
+
/**
|
|
3317
|
+
* Parse a single GitHub job into a Sverka StepDefinition.
|
|
3318
|
+
*/
|
|
3319
|
+
function parseJob(jobId, job, diagnostics) {
|
|
3320
|
+
const stepId = `ci/${jobId}`;
|
|
3321
|
+
const operations = [];
|
|
3322
|
+
const dependencies = [];
|
|
3323
|
+
parseJobNeeds(job, dependencies);
|
|
3324
|
+
const jobSteps = job.steps;
|
|
3325
|
+
if (jobSteps) for (const step of jobSteps) parseJobStep(jobId, step, operations, diagnostics);
|
|
3326
|
+
recordJobDiagnostics(jobId, job, diagnostics);
|
|
3327
|
+
const runtime = {};
|
|
3328
|
+
const runner = parseRunsOn(job["runs-on"]);
|
|
3329
|
+
return {
|
|
3330
|
+
id: stepId,
|
|
3331
|
+
runtime,
|
|
3332
|
+
operations,
|
|
3333
|
+
inputs: [],
|
|
3334
|
+
outputs: [],
|
|
3335
|
+
dependencies,
|
|
3336
|
+
...runner !== void 0 ? { runner } : {}
|
|
3337
|
+
};
|
|
3338
|
+
}
|
|
3339
|
+
/**
|
|
3340
|
+
* Parse a job's `needs` field into dependencies.
|
|
3341
|
+
*/
|
|
3342
|
+
function parseJobNeeds(job, dependencies) {
|
|
3343
|
+
if (typeof job.needs === "string") dependencies.push({
|
|
3344
|
+
kind: "control",
|
|
3345
|
+
producer: `ci/${job.needs}`
|
|
3346
|
+
});
|
|
3347
|
+
else if (Array.isArray(job.needs)) {
|
|
3348
|
+
for (const need of job.needs) if (typeof need === "string") dependencies.push({
|
|
3349
|
+
kind: "control",
|
|
3350
|
+
producer: `ci/${need}`
|
|
3351
|
+
});
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
/**
|
|
3355
|
+
* Parse a single GitHub step into one or more operations.
|
|
3356
|
+
*/
|
|
3357
|
+
function parseJobStep(jobId, step, operations, diagnostics) {
|
|
3358
|
+
if (typeof step.run === "string") {
|
|
3359
|
+
operations.push({
|
|
3360
|
+
kind: "shell",
|
|
3361
|
+
command: step.run
|
|
3362
|
+
});
|
|
3363
|
+
return;
|
|
3364
|
+
}
|
|
3365
|
+
if (typeof step.uses === "string") parseUsesStep(jobId, step, step.uses, operations, diagnostics);
|
|
3366
|
+
}
|
|
3367
|
+
/**
|
|
3368
|
+
* Parse a `uses:` step by dispatching to the appropriate action handler.
|
|
3369
|
+
*/
|
|
3370
|
+
function parseUsesStep(jobId, step, uses, operations, diagnostics) {
|
|
3371
|
+
if (uses.includes("action-gh-release")) operations.push(parseReleaseAction(step));
|
|
3372
|
+
else if (uses.includes("upload-pages-artifact")) operations.push(parseDeployPagesAction(step));
|
|
3373
|
+
else if (uses.includes("actions/checkout")) {} else if (uses.includes("actions/upload-artifact")) operations.push(parseUploadArtifactAction(step));
|
|
3374
|
+
else if (uses.includes("actions/download-artifact")) parseDownloadArtifactAction(jobId, step, operations, diagnostics);
|
|
3375
|
+
else diagnostics.push({
|
|
3376
|
+
severity: "info",
|
|
3377
|
+
message: `unmapped action '${uses}' in job '${jobId}' — skipped`,
|
|
3378
|
+
path: jobId
|
|
3379
|
+
});
|
|
3380
|
+
}
|
|
3381
|
+
/**
|
|
3382
|
+
* Parse a release action (softprops/action-gh-release) into a release operation.
|
|
3383
|
+
*/
|
|
3384
|
+
function parseReleaseAction(step) {
|
|
3385
|
+
const withMap = step.with ?? {};
|
|
3386
|
+
return {
|
|
3387
|
+
kind: "release",
|
|
3388
|
+
tag: typeof withMap.tag_name === "string" ? withMap.tag_name : "unknown",
|
|
3389
|
+
...typeof withMap.name === "string" ? { name: withMap.name } : {},
|
|
3390
|
+
...typeof withMap.body === "string" ? { description: withMap.body } : {},
|
|
3391
|
+
...typeof withMap.assets === "string" ? { assets: withMap.assets.split("\n").filter((s) => s.length > 0) } : {},
|
|
3392
|
+
...typeof withMap.draft === "boolean" ? { draft: withMap.draft } : {},
|
|
3393
|
+
...typeof withMap.prerelease === "boolean" ? { prerelease: withMap.prerelease } : {}
|
|
3394
|
+
};
|
|
3395
|
+
}
|
|
3396
|
+
/**
|
|
3397
|
+
* Parse an upload-pages-artifact action into a deployPages operation.
|
|
3398
|
+
*/
|
|
3399
|
+
function parseDeployPagesAction(step) {
|
|
3400
|
+
const withMap = step.with ?? {};
|
|
3401
|
+
return {
|
|
3402
|
+
kind: "deployPages",
|
|
3403
|
+
path: typeof withMap.path === "string" ? withMap.path : "dist/"
|
|
3404
|
+
};
|
|
3405
|
+
}
|
|
3406
|
+
/**
|
|
3407
|
+
* Parse an actions/upload-artifact action into an exportArtifact operation.
|
|
3408
|
+
*/
|
|
3409
|
+
function parseUploadArtifactAction(step) {
|
|
3410
|
+
const withMap = step.with ?? {};
|
|
3411
|
+
return {
|
|
3412
|
+
kind: "exportArtifact",
|
|
3413
|
+
name: typeof withMap.name === "string" ? withMap.name : "artifact",
|
|
3414
|
+
path: typeof withMap.path === "string" ? withMap.path : "."
|
|
3415
|
+
};
|
|
3416
|
+
}
|
|
3417
|
+
/**
|
|
3418
|
+
* Parse an actions/download-artifact action into an importArtifact operation.
|
|
3419
|
+
*/
|
|
3420
|
+
function parseDownloadArtifactAction(jobId, step, operations, diagnostics) {
|
|
3421
|
+
const withMap = step.with ?? {};
|
|
3422
|
+
const artifactName = typeof withMap.name === "string" ? withMap.name : "artifact";
|
|
3423
|
+
const from = resolveArtifactProducer(artifactName);
|
|
3424
|
+
operations.push({
|
|
3425
|
+
kind: "importArtifact",
|
|
3426
|
+
name: artifactName,
|
|
3427
|
+
from,
|
|
3428
|
+
output: artifactName
|
|
3429
|
+
});
|
|
3430
|
+
if (from === "ci/unknown") diagnostics.push({
|
|
3431
|
+
severity: "warn",
|
|
3432
|
+
message: `download-artifact in job '${jobId}' — producer could not be resolved from name '${artifactName}', may need manual correction`,
|
|
3433
|
+
path: jobId
|
|
3434
|
+
});
|
|
3435
|
+
}
|
|
3436
|
+
/**
|
|
3437
|
+
* Try to resolve the producing job from an artifact name.
|
|
3438
|
+
* Artifact names follow the pattern `<stepId>-<outputName>`, so the producer
|
|
3439
|
+
* is `ci/<stepId>`.
|
|
3440
|
+
*/
|
|
3441
|
+
function resolveArtifactProducer(artifactName) {
|
|
3442
|
+
const dashIdx = artifactName.lastIndexOf("-");
|
|
3443
|
+
if (dashIdx > 0) return `ci/${artifactName.slice(0, dashIdx)}`;
|
|
3444
|
+
return "ci/unknown";
|
|
3445
|
+
}
|
|
3446
|
+
/**
|
|
3447
|
+
* Parse `runs-on` into a RunnerSpec.
|
|
3448
|
+
* Supports string, array, and object forms.
|
|
3449
|
+
*/
|
|
3450
|
+
function parseRunsOn(runsOn) {
|
|
3451
|
+
if (typeof runsOn === "string") return { labels: [runsOn] };
|
|
3452
|
+
if (Array.isArray(runsOn)) {
|
|
3453
|
+
const labels = runsOn.filter((v) => typeof v === "string");
|
|
3454
|
+
if (labels.length > 0) return { labels };
|
|
3455
|
+
}
|
|
3456
|
+
if (runsOn && typeof runsOn === "object") {
|
|
3457
|
+
const obj = runsOn;
|
|
3458
|
+
const group = typeof obj.group === "string" ? obj.group : void 0;
|
|
3459
|
+
const labels = Array.isArray(obj.labels) ? obj.labels.filter((v) => typeof v === "string") : [];
|
|
3460
|
+
if (labels.length > 0 || group !== void 0) return {
|
|
3461
|
+
labels,
|
|
3462
|
+
...group !== void 0 ? { group } : {}
|
|
3463
|
+
};
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
/**
|
|
3467
|
+
* Record diagnostics for unmappable job-level constructs.
|
|
3468
|
+
*/
|
|
3469
|
+
function recordJobDiagnostics(jobId, job, diagnostics) {
|
|
3470
|
+
if (job.strategy) diagnostics.push({
|
|
3471
|
+
severity: "warn",
|
|
3472
|
+
message: `job '${jobId}' has strategy/matrix — not imported`,
|
|
3473
|
+
path: jobId
|
|
3474
|
+
});
|
|
3475
|
+
if (job.services) diagnostics.push({
|
|
3476
|
+
severity: "info",
|
|
3477
|
+
message: `job '${jobId}' has services — not imported`,
|
|
3478
|
+
path: jobId
|
|
3479
|
+
});
|
|
3480
|
+
if (job.environment) diagnostics.push({
|
|
3481
|
+
severity: "info",
|
|
3482
|
+
message: `job '${jobId}' has environment — not imported`,
|
|
3483
|
+
path: jobId
|
|
3484
|
+
});
|
|
3485
|
+
}
|
|
3486
|
+
//#endregion
|
|
3487
|
+
//#region src/gitlab/importer.ts
|
|
3488
|
+
/**
|
|
3489
|
+
* Import a GitLab CI YAML string into a Definition Graph.
|
|
3490
|
+
* Lossy: unmappable constructs produce diagnostics.
|
|
3491
|
+
*/
|
|
3492
|
+
function importGitlab(source) {
|
|
3493
|
+
return importGitlabWithDiagnostics(source).graph;
|
|
3494
|
+
}
|
|
3495
|
+
/**
|
|
3496
|
+
* Import a GitLab CI YAML string with diagnostics for unmappable constructs.
|
|
3497
|
+
*/
|
|
3498
|
+
function importGitlabWithDiagnostics(source) {
|
|
3499
|
+
const diagnostics = [];
|
|
3500
|
+
const doc = parseGitlabDoc(source);
|
|
3501
|
+
const projectId = "imported";
|
|
3502
|
+
const steps = [];
|
|
3503
|
+
const entries = [];
|
|
3504
|
+
const pipelineRules = [];
|
|
3505
|
+
extractWorkflowRules(doc, pipelineRules);
|
|
3506
|
+
const reservedKeys = /* @__PURE__ */ new Set([
|
|
3507
|
+
"stages",
|
|
3508
|
+
"variables",
|
|
3509
|
+
"workflow",
|
|
3510
|
+
"include",
|
|
3511
|
+
"image",
|
|
3512
|
+
"before_script",
|
|
3513
|
+
"after_script",
|
|
3514
|
+
"cache",
|
|
3515
|
+
"services",
|
|
3516
|
+
"default"
|
|
3517
|
+
]);
|
|
3518
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
3519
|
+
if (reservedKeys.has(key)) continue;
|
|
3520
|
+
if (key.startsWith(".")) continue;
|
|
3521
|
+
if (!value || typeof value !== "object") continue;
|
|
3522
|
+
const step = convertJobToStep(key, value, diagnostics);
|
|
3523
|
+
steps.push(step);
|
|
3524
|
+
}
|
|
3525
|
+
if (steps.length > 0 && entries.length === 0) entries.push({
|
|
3526
|
+
id: "ci/default",
|
|
3527
|
+
trigger: { kind: "push" },
|
|
3528
|
+
roots: steps.map((s) => s.id)
|
|
3529
|
+
});
|
|
3530
|
+
return {
|
|
3531
|
+
graph: { project: {
|
|
3532
|
+
id: projectId,
|
|
3533
|
+
pipelines: [{
|
|
3534
|
+
id: "ci",
|
|
3535
|
+
inputs: {},
|
|
3536
|
+
entries,
|
|
3537
|
+
steps,
|
|
3538
|
+
outputs: [],
|
|
3539
|
+
...pipelineRules.length > 0 ? { rules: pipelineRules } : {}
|
|
3540
|
+
}]
|
|
3541
|
+
} },
|
|
3542
|
+
diagnostics
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
/**
|
|
3546
|
+
* Parse and validate the GitLab CI YAML source into a record.
|
|
3547
|
+
*/
|
|
3548
|
+
function parseGitlabDoc(source) {
|
|
3549
|
+
let doc;
|
|
3550
|
+
try {
|
|
3551
|
+
doc = parse(source);
|
|
3552
|
+
} catch (err) {
|
|
3553
|
+
throw new GitlabTargetError(`failed to parse GitLab CI YAML: ${err instanceof Error ? err.message : String(err)}`, "IMPORT_FAILED");
|
|
3554
|
+
}
|
|
3555
|
+
if (!doc || typeof doc !== "object") throw new GitlabTargetError("GitLab CI YAML is not a valid object", "IMPORT_FAILED");
|
|
3556
|
+
return doc;
|
|
3557
|
+
}
|
|
3558
|
+
/**
|
|
3559
|
+
* Extract workflow:rules into pipeline rules.
|
|
3560
|
+
*/
|
|
3561
|
+
function extractWorkflowRules(doc, pipelineRules) {
|
|
3562
|
+
const workflow = doc.workflow;
|
|
3563
|
+
if (!workflow?.rules) return;
|
|
3564
|
+
for (const rule of workflow.rules) pipelineRules.push(convertWorkflowRule(rule));
|
|
3565
|
+
}
|
|
3566
|
+
/**
|
|
3567
|
+
* Extract string variables from a rule's `variables` field.
|
|
3568
|
+
*/
|
|
3569
|
+
function extractRuleVariables(rule) {
|
|
3570
|
+
if (rule.variables && typeof rule.variables === "object") return Object.fromEntries(Object.entries(rule.variables).filter(([, v]) => typeof v === "string"));
|
|
3571
|
+
}
|
|
3572
|
+
function convertWorkflowRule(rule) {
|
|
3573
|
+
const pr = {};
|
|
3574
|
+
if (typeof rule.if === "string") pr.if = rule.if;
|
|
3575
|
+
if (Array.isArray(rule.changes)) pr.changes = rule.changes;
|
|
3576
|
+
if (Array.isArray(rule.exists)) pr.exists = rule.exists;
|
|
3577
|
+
const variables = extractRuleVariables(rule);
|
|
3578
|
+
if (variables) pr.variables = variables;
|
|
3579
|
+
if (rule.when === "always" || rule.when === "never") pr.when = rule.when;
|
|
3580
|
+
return pr;
|
|
3581
|
+
}
|
|
3582
|
+
/**
|
|
3583
|
+
* Convert a single GitLab job into a StepDefinition.
|
|
3584
|
+
*/
|
|
3585
|
+
function convertJobToStep(key, job, diagnostics) {
|
|
3586
|
+
const stepId = `ci/${key}`;
|
|
3587
|
+
const operations = [];
|
|
3588
|
+
const dependencies = [];
|
|
3589
|
+
convertScript(job, operations);
|
|
3590
|
+
convertNeeds(job, dependencies);
|
|
3591
|
+
convertTrigger(key, job, operations, diagnostics);
|
|
3592
|
+
convertRelease(job, operations);
|
|
3593
|
+
convertPages(job, operations);
|
|
3594
|
+
convertArtifacts(job, operations);
|
|
3595
|
+
recordUnmappableConstructs(key, job, diagnostics);
|
|
3596
|
+
const runtime = typeof job.image === "string" ? {
|
|
3597
|
+
mode: "container",
|
|
3598
|
+
image: job.image
|
|
3599
|
+
} : {};
|
|
3600
|
+
const rules = convertJobRules(job);
|
|
3601
|
+
const beforeScript = convertScriptArray(job, "before_script");
|
|
3602
|
+
const afterScript = convertScriptArray(job, "after_script");
|
|
3603
|
+
const delay = convertDelay(job);
|
|
3604
|
+
return {
|
|
3605
|
+
id: stepId,
|
|
3606
|
+
runtime,
|
|
3607
|
+
operations,
|
|
3608
|
+
inputs: [],
|
|
3609
|
+
outputs: [],
|
|
3610
|
+
dependencies,
|
|
3611
|
+
...rules.length > 0 ? { rules } : {},
|
|
3612
|
+
...beforeScript !== void 0 ? { beforeScript } : {},
|
|
3613
|
+
...afterScript !== void 0 ? { afterScript } : {},
|
|
3614
|
+
...delay !== void 0 ? { delay } : {}
|
|
3615
|
+
};
|
|
3616
|
+
}
|
|
3617
|
+
/**
|
|
3618
|
+
* Convert `script` into shell operations.
|
|
3619
|
+
*/
|
|
3620
|
+
function convertScript(job, operations) {
|
|
3621
|
+
if (typeof job.script === "string") operations.push({
|
|
3622
|
+
kind: "shell",
|
|
3623
|
+
command: job.script
|
|
3624
|
+
});
|
|
3625
|
+
else if (Array.isArray(job.script)) {
|
|
3626
|
+
for (const line of job.script) if (typeof line === "string") operations.push({
|
|
3627
|
+
kind: "shell",
|
|
3628
|
+
command: line
|
|
3629
|
+
});
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
/**
|
|
3633
|
+
* Convert `needs` into dependencies.
|
|
3634
|
+
*/
|
|
3635
|
+
function convertNeeds(job, dependencies) {
|
|
3636
|
+
if (Array.isArray(job.needs)) {
|
|
3637
|
+
for (const need of job.needs) if (typeof need === "string") dependencies.push({
|
|
3638
|
+
kind: "control",
|
|
3639
|
+
producer: `ci/${need}`
|
|
3640
|
+
});
|
|
3641
|
+
else if (need && typeof need === "object" && typeof need.job === "string") {
|
|
3642
|
+
const needJob = need.job;
|
|
3643
|
+
dependencies.push({
|
|
3644
|
+
kind: "control",
|
|
3645
|
+
producer: `ci/${needJob}`
|
|
3646
|
+
});
|
|
3647
|
+
}
|
|
3648
|
+
} else if (typeof job.needs === "string") dependencies.push({
|
|
3649
|
+
kind: "control",
|
|
3650
|
+
producer: `ci/${job.needs}`
|
|
3651
|
+
});
|
|
3652
|
+
}
|
|
3653
|
+
/**
|
|
3654
|
+
* Convert `trigger` into downstream/child pipeline diagnostics.
|
|
3655
|
+
*/
|
|
3656
|
+
function convertTrigger(key, job, operations, diagnostics) {
|
|
3657
|
+
if (!job.trigger || typeof job.trigger !== "object") return;
|
|
3658
|
+
const trigger = job.trigger;
|
|
3659
|
+
if (typeof trigger.project === "string") {
|
|
3660
|
+
operations.push({
|
|
3661
|
+
kind: "diagnostic",
|
|
3662
|
+
message: `imported downstream trigger to project: ${trigger.project}`,
|
|
3663
|
+
severity: "info"
|
|
3664
|
+
});
|
|
3665
|
+
diagnostics.push({
|
|
3666
|
+
severity: "warn",
|
|
3667
|
+
message: `job '${key}' has a downstream project trigger — imported as diagnostic. Manual reconstruction needed.`,
|
|
3668
|
+
path: key
|
|
3669
|
+
});
|
|
3670
|
+
} else if (Array.isArray(trigger.include)) diagnostics.push({
|
|
3671
|
+
severity: "warn",
|
|
3672
|
+
message: `job '${key}' has a child pipeline trigger — imported as diagnostic. Manual reconstruction needed.`,
|
|
3673
|
+
path: key
|
|
3674
|
+
});
|
|
3675
|
+
}
|
|
3676
|
+
/**
|
|
3677
|
+
* Convert `release` into a release operation.
|
|
3678
|
+
*/
|
|
3679
|
+
function convertRelease(job, operations) {
|
|
3680
|
+
if (!job.release || typeof job.release !== "object") return;
|
|
3681
|
+
const release = job.release;
|
|
3682
|
+
const assets = release.assets;
|
|
3683
|
+
operations.push({
|
|
3684
|
+
kind: "release",
|
|
3685
|
+
tag: typeof release.tag_name === "string" ? release.tag_name : "unknown",
|
|
3686
|
+
...typeof release.name === "string" ? { name: release.name } : {},
|
|
3687
|
+
...typeof release.description === "string" ? { description: release.description } : {},
|
|
3688
|
+
...assets?.links ? { assets: assets.links.map((l) => l.url ?? l.name ?? "unknown") } : {}
|
|
3689
|
+
});
|
|
3690
|
+
}
|
|
3691
|
+
/**
|
|
3692
|
+
* Convert `pages` into a deployPages operation.
|
|
3693
|
+
*/
|
|
3694
|
+
function convertPages(job, operations) {
|
|
3695
|
+
if (!job.pages || typeof job.pages !== "object") return;
|
|
3696
|
+
const pages = job.pages;
|
|
3697
|
+
operations.push({
|
|
3698
|
+
kind: "deployPages",
|
|
3699
|
+
path: typeof pages.publish === "string" ? pages.publish : "public/",
|
|
3700
|
+
...typeof pages.path_prefix === "string" ? { prefix: pages.path_prefix } : {}
|
|
3701
|
+
});
|
|
3702
|
+
}
|
|
3703
|
+
/**
|
|
3704
|
+
* Record diagnostics for constructs that cannot be mapped to the IR.
|
|
3705
|
+
*/
|
|
3706
|
+
function recordUnmappableConstructs(key, job, diagnostics) {
|
|
3707
|
+
if (job.cache) diagnostics.push({
|
|
3708
|
+
severity: "info",
|
|
3709
|
+
message: `job '${key}' has cache — not imported`,
|
|
3710
|
+
path: key
|
|
3711
|
+
});
|
|
3712
|
+
if (job.services) diagnostics.push({
|
|
3713
|
+
severity: "info",
|
|
3714
|
+
message: `job '${key}' has services — not imported`,
|
|
3715
|
+
path: key
|
|
3716
|
+
});
|
|
3717
|
+
if (job.matrix) diagnostics.push({
|
|
3718
|
+
severity: "warn",
|
|
3719
|
+
message: `job '${key}' has matrix — not imported`,
|
|
3720
|
+
path: key
|
|
3721
|
+
});
|
|
3722
|
+
}
|
|
3723
|
+
/**
|
|
3724
|
+
* Valid `when` values for job-level rules.
|
|
3725
|
+
*/
|
|
3726
|
+
const JOB_RULE_WHEN_VALUES = /* @__PURE__ */ new Set([
|
|
3727
|
+
"on_success",
|
|
3728
|
+
"on_failure",
|
|
3729
|
+
"always",
|
|
3730
|
+
"never",
|
|
3731
|
+
"manual"
|
|
3732
|
+
]);
|
|
3733
|
+
/**
|
|
3734
|
+
* Convert a single raw rule object into a StepDefinition rule entry.
|
|
3735
|
+
*/
|
|
3736
|
+
function convertJobRuleEntry(r) {
|
|
3737
|
+
const ruleEntry = {};
|
|
3738
|
+
if (typeof r.if === "string") ruleEntry.if = r.if;
|
|
3739
|
+
if (Array.isArray(r.changes)) ruleEntry.changes = r.changes;
|
|
3740
|
+
if (Array.isArray(r.exists)) ruleEntry.exists = r.exists;
|
|
3741
|
+
if (typeof r.when === "string" && JOB_RULE_WHEN_VALUES.has(r.when)) ruleEntry.when = r.when;
|
|
3742
|
+
const variables = extractRuleVariables(r);
|
|
3743
|
+
if (variables) ruleEntry.variables = variables;
|
|
3744
|
+
return ruleEntry;
|
|
3745
|
+
}
|
|
3746
|
+
/**
|
|
3747
|
+
* Convert job-level `rules` into StepDefinition rules.
|
|
3748
|
+
*/
|
|
3749
|
+
function convertJobRules(job) {
|
|
3750
|
+
if (!Array.isArray(job.rules)) return [];
|
|
3751
|
+
const rules = [];
|
|
3752
|
+
for (const rule of job.rules) {
|
|
3753
|
+
if (!rule || typeof rule !== "object") continue;
|
|
3754
|
+
rules.push(convertJobRuleEntry(rule));
|
|
3755
|
+
}
|
|
3756
|
+
return rules;
|
|
3757
|
+
}
|
|
3758
|
+
/**
|
|
3759
|
+
* Convert a script array field (before_script/after_script) from a job.
|
|
3760
|
+
*/
|
|
3761
|
+
function convertScriptArray(job, field) {
|
|
3762
|
+
const value = job[field];
|
|
3763
|
+
if (typeof value === "string") return [value];
|
|
3764
|
+
if (Array.isArray(value)) {
|
|
3765
|
+
const lines = value.filter((v) => typeof v === "string");
|
|
3766
|
+
if (lines.length > 0) return lines;
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
/**
|
|
3770
|
+
* Convert `when: delayed` + `start_in` to a delay string.
|
|
3771
|
+
*/
|
|
3772
|
+
function convertDelay(job) {
|
|
3773
|
+
if (job.when === "delayed" && typeof job.start_in === "string") return job.start_in;
|
|
3774
|
+
}
|
|
3775
|
+
/**
|
|
3776
|
+
* Convert artifact `paths` into exportArtifact operations.
|
|
3777
|
+
*/
|
|
3778
|
+
function convertArtifactPaths(artifacts, operations) {
|
|
3779
|
+
const paths = artifacts.paths;
|
|
3780
|
+
if (!Array.isArray(paths)) return;
|
|
3781
|
+
for (const path of paths) if (typeof path === "string") operations.push({
|
|
3782
|
+
kind: "exportArtifact",
|
|
3783
|
+
name: path.split("/").pop() ?? path,
|
|
3784
|
+
path
|
|
3785
|
+
});
|
|
3786
|
+
}
|
|
3787
|
+
/**
|
|
3788
|
+
* Convert artifact `reports` into report operations.
|
|
3789
|
+
* Reports are noted but not fully mapped — they require spec types.
|
|
3790
|
+
* Unsupported report keys are silently skipped to avoid producing invalid
|
|
3791
|
+
* graph nodes that violate the CDK report-type contract.
|
|
3792
|
+
*/
|
|
3793
|
+
const SUPPORTED_REPORT_TYPES = /* @__PURE__ */ new Set([
|
|
3794
|
+
"junit",
|
|
3795
|
+
"coverage",
|
|
3796
|
+
"dotenv",
|
|
3797
|
+
"sast",
|
|
3798
|
+
"dast",
|
|
3799
|
+
"dependencyScanning",
|
|
3800
|
+
"containerScanning",
|
|
3801
|
+
"licenseScanning",
|
|
3802
|
+
"performance",
|
|
3803
|
+
"metrics",
|
|
3804
|
+
"terraform",
|
|
3805
|
+
"quality",
|
|
3806
|
+
"sarif"
|
|
3807
|
+
]);
|
|
3808
|
+
function convertArtifactReports(artifacts, operations) {
|
|
3809
|
+
if (!artifacts.reports || typeof artifacts.reports !== "object") return;
|
|
3810
|
+
const reports = artifacts.reports;
|
|
3811
|
+
for (const [type, value] of Object.entries(reports)) if (typeof value === "string" && SUPPORTED_REPORT_TYPES.has(type)) operations.push({
|
|
3812
|
+
kind: "report",
|
|
3813
|
+
spec: {
|
|
3814
|
+
type,
|
|
3815
|
+
path: value
|
|
3816
|
+
}
|
|
3817
|
+
});
|
|
3818
|
+
}
|
|
3819
|
+
/**
|
|
3820
|
+
* Convert `artifacts` into exportArtifact operations.
|
|
3821
|
+
*/
|
|
3822
|
+
function convertArtifacts(job, operations) {
|
|
3823
|
+
if (!job.artifacts || typeof job.artifacts !== "object") return;
|
|
3824
|
+
const artifacts = job.artifacts;
|
|
3825
|
+
convertArtifactPaths(artifacts, operations);
|
|
3826
|
+
convertArtifactReports(artifacts, operations);
|
|
3827
|
+
}
|
|
3828
|
+
//#endregion
|
|
3829
|
+
export { GithubTarget, GithubTargetError, GitlabTarget, GitlabTargetError, PluginError, analyzeCapabilities, compileGithub, compileGitlab, createPluginRegistry, defineSverkaPlugin, detectCapabilities, githubCapabilities, gitlabCapabilities, importGithub, importGithubWithDiagnostics, importGitlab, importGitlabWithDiagnostics, loadBundledRegistry, pinActionRef };
|
|
3237
3830
|
|
|
3238
3831
|
//# sourceMappingURL=index.mjs.map
|