import { evaluate, stringify } from "./expressions";
import type { ExprContext } from "./expressions";
/** A piece of interpolated text; non-text segments are resolved by the job script at runtime. */
export type Segment =
| { kind: "text"; value: string }
| { kind: "secret"; name: string }
| { kind: "step"; variable: string }
| { kind: "hash"; patterns: string[] };
const EXPRESSION = /\$\{\{([\s\S]*?)\}\}/g;
const SECRET_REFERENCE = /^\s*secrets\.([A-Za-z_][A-Za-z0-9_]*)\s*$/;
const STEP_REFERENCE =
/^\s*steps\.([A-Za-z_][A-Za-z0-9_-]*)\.(?:outputs\.([A-Za-z_][A-Za-z0-9_-]*)|(outcome|conclusion))\s*$/;
const HASH_FILES = /^\s*hashFiles\(([\s\S]*)\)\s*$/i;
const QUOTED = /'((?:[^']|'')*)'/g;
export function shellName(value: string): string {
return value.replace(/[^A-Za-z0-9_]/g, "_");
}
/** Shell variable holding a step output (`field` is an output name) or result (`outcome`/`conclusion`). */
export function stepVariable(stepId: string, field: string, kind: "output" | "result"): string {
return kind === "output"
? `GITFOR_STEPOUT_${shellName(stepId)}__${shellName(field)}`
: `GITFOR_STEP${field.toUpperCase()}_${shellName(stepId)}`;
}
function special(expression: string): Segment | null {
const secret = SECRET_REFERENCE.exec(expression);
if (secret) return { kind: "secret", name: secret[1] };
const step = STEP_REFERENCE.exec(expression);
if (step) {
return {
kind: "step",
variable: step[2] ? stepVariable(step[1], step[2], "output") : stepVariable(step[1], step[3] ?? "", "result"),
};
}
const hash = HASH_FILES.exec(expression);
if (hash) {