import { matchesFilters } from "~/lib/glob"; import type { ExprValue } from "./expressions"; import type { RefFilters, WorkflowSpec } from "./spec"; import type { RepoRow } from "~/lib/types"; const DEFAULT_PR_TYPES = ["opened", "synchronize", "reopened"]; const DEFAULT_RELEASE_TYPES = ["published"]; export type Trigger = { event: "push" | "pull_request" | "release" | "workflow_dispatch"; action: string | null; ref: string; sha: string; actorId: string | null; actorName: string; title: string; prNumber: number | null; headRef: string | null; baseRef: string | null; changedPaths: string[] | null; inputs: Record; workflowPath: string | null; /** Repository whose objects are checked out (differs from `repo` for fork pull requests). */ sourceRepo: RepoRow; }; function pathsAllowed(filters: RefFilters, changed: string[] | null): boolean { if (changed === null) return true; if (filters.paths && !changed.some((path) => matchesFilters(filters.paths ?? [], path))) return false; if (filters["paths-ignore"] && changed.every((path) => matchesFilters(filters["paths-ignore"] ?? [], path))) { return false; } return true; } function refAllowed(include: string[] | undefined, exclude: string[] | undefined, name: string): boolean { if (include && !matchesFilters(include, name)) return false; if (exclude && matchesFilters(exclude, name)) return false; return true; } export function workflowMatches(spec: WorkflowSpec, trigger: Trigger, path: string): boolean { const on = spec.on; switch (trigger.event) { case "push": { const filters = on.push; if (!filters) return false; const isTag = trigger.ref.startsWith("refs/tags/"); const name = trigger.ref.replace(/^refs\/(heads|tags)\//, ""); const hasBranchFilter = Boolean(filters.branches || filters["branches-ignore"]); const hasTagFilter = Boolean(filters.tags || filters["tags-ignore"]); if (isTag) { if (!hasTagFilter && hasBranchFilter) return false; return refAllowed(filters.tags, filters["tags-ignore"], name); } if (!hasBranchFilter && hasTagFilter) return false; return ( refAllowed(filters.branches, filters["branches-ignore"], name) && pathsAllowed(filters, trigger.changedPaths) ); } case "pull_request": { const filters = on.pull_request; if (!filters) return false; if (!(filters.types ?? DEFAULT_PR_TYPES).includes(trigger.action ?? "")) return false; return ( refAllowed(filters.branches, filters["branches-ignore"], trigger.baseRef ?? "") && pathsAllowed(filters, trigger.changedPaths) ); } case "release": { const filters = on.release; return Boolean(filters) && (filters?.types ?? DEFAULT_RELEASE_TYPES).includes(trigger.action ?? ""); } case "workflow_dispatch": return Boolean(on.workflow_dispatch) && trigger.workflowPath === path; } }