From 4cd9500523bf9c5c09efdcb4c49b6e31acfb9e88 Mon Sep 17 00:00:00 2001 From: Jacob Wallraff Date: Mon, 9 Jan 2023 16:52:53 -0800 Subject: [PATCH] Port over josh's changes --- .../src/model/converter/cron.ts | 95 +++++++++++++++++++ .../src/model/converter/events.ts | 5 +- 2 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 actions-workflow-parser/src/model/converter/cron.ts diff --git a/actions-workflow-parser/src/model/converter/cron.ts b/actions-workflow-parser/src/model/converter/cron.ts new file mode 100644 index 0000000..5af4803 --- /dev/null +++ b/actions-workflow-parser/src/model/converter/cron.ts @@ -0,0 +1,95 @@ +const MONTHS = { + jan: 1, + feb: 2, + mar: 3, + apr: 4, + may: 5, + jun: 6, + jul: 7, + aug: 8, + sep: 9, + oct: 10, + nov: 11, + dec: 12, + } + + const DAYS = { + sun: 0, + mon: 1, + tue: 2, + wed: 3, + thu: 4, + fri: 5, + sat: 6, + } + + // TODO: make this parseCron + export function isValidCron(cron: string): boolean { + // https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule + + const parts = cron.split(" ") + if (parts.length != 5) { + return false + } + + const [minutes, hours, dom, months, dow] = parts + + return ( + validateRange(minutes, { min: 0, max: 59 }) && + validateRange(hours, { min: 0, max: 23 }) && + validateRange(dom, { min: 1, max: 31 }) && + validateRange(months, { min: 1, max: 12, names: MONTHS }) && + validateRange(dow, { min: 0, max: 6, names: DAYS }) + ) + } + + type Range = { + min: number + max: number + names?: Record + } + + function validateRange( + value: string, + range: Range, + allowSeparators = true + ): boolean { + if (range.names && range.names[value.toLowerCase()] !== undefined) { + return true + } + + if (value === "*") { + return true + } + + if (value.includes(",")) { + if (!allowSeparators) { + return false + } + return value.split(",").every((v) => validateRange(v, range, false)) + } + + if (value.includes("/")) { + if (!allowSeparators) { + return false + } + + const [start, step, ...rest] = value.split("/") + if (rest.length > 0) { + return false + } + return ( + validateRange(start, range, false) && validateRange(step, range, false) + ) + } + + if (value.includes("-")) { + const [start, end] = value.split("-") + return ( + validateRange(start, range, false) && validateRange(end, range, false) + ) + } + + const number = parseInt(value, 10) + return !isNaN(number) && number >= range.min && number <= range.max + } \ No newline at end of file diff --git a/actions-workflow-parser/src/model/converter/events.ts b/actions-workflow-parser/src/model/converter/events.ts index 4504e2d..719c31c 100644 --- a/actions-workflow-parser/src/model/converter/events.ts +++ b/actions-workflow-parser/src/model/converter/events.ts @@ -18,6 +18,7 @@ import { TypesFilterConfig, WorkflowFilterConfig, } from "../workflow-template" +import { isValidCron } from "./cron" import { convertStringList } from "./string-list" import { convertEventWorkflowDispatchInputs } from "./workflow-dispatch" @@ -160,9 +161,7 @@ function convertSchedule( if (scheduleKey.value == "cron") { const cron = schedule.value.assertString(`schedule cron`) // Validate the cron string - if ( - !cron.value.match(/((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})/) - ) { + if (!isValidCron(cron.value)) { context.error(cron, "Invalid cron string") } result.push({ cron: cron.value })