@lchase/yak 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +37 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -488,6 +488,7 @@ async function validateWorkflow(workflow, cwd = process.cwd(), runIsolation = "n
|
|
|
488
488
|
checkReservedArtifactNames(flatSteps);
|
|
489
489
|
checkNeedsSatisfied(workflow.steps, producerOf);
|
|
490
490
|
checkNoCycles(workflow.steps, producerOf);
|
|
491
|
+
checkExprArtifactIdentifiers(workflow.steps, producerOf);
|
|
491
492
|
checkExitCodeReads(workflow.steps, producerOf);
|
|
492
493
|
checkAgentToolNames(flatSteps);
|
|
493
494
|
await checkAgentSchemaKeys(flatSteps, cwd);
|
|
@@ -621,6 +622,42 @@ function checkNoCycles(steps, producerOf) {
|
|
|
621
622
|
}
|
|
622
623
|
for (const step of steps) visit(step, []);
|
|
623
624
|
}
|
|
625
|
+
var JEXL_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
626
|
+
function checkExprArtifactIdentifiers(steps, producerOf) {
|
|
627
|
+
for (const step of steps) {
|
|
628
|
+
if (step.skipIf !== void 0) checkExprRefs(step.id, "skipIf", step.skipIf, producerOf);
|
|
629
|
+
if (step.kind === "loop") {
|
|
630
|
+
const localProducerOf = new Map([...producerOf, ...buildProducerMap(step.body)]);
|
|
631
|
+
checkExprRefs(step.id, "until", step.until, localProducerOf);
|
|
632
|
+
if (step.budget.noProgress) {
|
|
633
|
+
checkExprRefs(step.id, "noProgress.signal", step.budget.noProgress.signal, localProducerOf);
|
|
634
|
+
}
|
|
635
|
+
checkExprArtifactIdentifiers(step.body, localProducerOf);
|
|
636
|
+
}
|
|
637
|
+
if (step.kind === "map") checkExprArtifactIdentifiers([step.step], producerOf);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
function checkExprRefs(stepId, field, expr, producerOf) {
|
|
641
|
+
if (typeof expr !== "string") return;
|
|
642
|
+
for (const name of producerOf.keys()) {
|
|
643
|
+
if (JEXL_IDENTIFIER.test(name)) continue;
|
|
644
|
+
const bounded = new RegExp(`(?<![A-Za-z0-9_$])${escapeRegExp(name)}(?![A-Za-z0-9_$])`);
|
|
645
|
+
if (!bounded.test(expr)) continue;
|
|
646
|
+
throw new WorkflowValidationError(
|
|
647
|
+
`step "${stepId}": ${field} references artifact "${name}", but "${name}" is not a valid jexl identifier \u2014 jexl reads it as an expression ("a-b" parses as "a minus b"), not a name, so the artifact is invisible to ${field}. Rename it to an identifier (e.g. "${toIdentifier(name)}").`
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
function escapeRegExp(s) {
|
|
652
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
653
|
+
}
|
|
654
|
+
function toIdentifier(name) {
|
|
655
|
+
const camel = name.replace(
|
|
656
|
+
/[^A-Za-z0-9]+([A-Za-z0-9])?/g,
|
|
657
|
+
(_, c) => c ? c.toUpperCase() : ""
|
|
658
|
+
);
|
|
659
|
+
return JEXL_IDENTIFIER.test(camel) ? camel : `a${camel}`;
|
|
660
|
+
}
|
|
624
661
|
function checkExitCodeReads(steps, producerOf) {
|
|
625
662
|
const stepsById = new Map(steps.map((s) => [s.id, s]));
|
|
626
663
|
for (const step of steps) {
|