111 lines · 4.6 KB
import { interpolate, plainText } from "./segments";
import type { Segment } from "./segments";
import type { ExprContext } from "./expressions";
import type { StepSpec } from "./spec";

export type CacheMode = "both" | "restore" | "save";

export type CacheSpec = {
  mode: CacheMode;
  key: Segment[];
  restoreKeys: Segment[][];
  paths: string[];
  version: string;
};

export type BuiltinStep =
  | { kind: "noop"; name: string }
  | { kind: "upload-artifact"; name: string; artifact: { name: string; path: string } }
  | { kind: "cache"; name: string; cache: CacheSpec };

const CHECKOUT_ACTION = /^(actions\/)?checkout(@.*)?$/;
const UPLOAD_ACTION = /^(actions\/)?upload-artifact(@.*)?$/;
const SETUP_ACTION = /^(actions\/setup-node|oven-sh\/setup-bun)(@.*)?$/;
const CACHE_ACTION = /^(actions\/)?cache(\/(restore|save))?(@.*)?$/;
/** Same pattern the runners accept in artifact markers. */
const ARTIFACT_NAME = /^[A-Za-z0-9._-]{1,100}$/;
const MAX_CACHE_PATHS = 20;
const MAX_RESTORE_KEYS = 10;
const MAX_KEY_LENGTH = 512;
const FNV_OFFSET = 0x811c9dc5;
const FNV_PRIME = 0x01000193;

function lines(value: string | undefined): string[] {
  return (value ?? "")
    .split("\n")
    .map((line) => line.trim())
    .filter(Boolean);
}

/** Cache entries are only compatible when they were saved from the same set of paths. */