UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

134 lines (133 loc) 5.09 kB
import { logger } from "../../../../logger/index.js"; import { fixShortHours } from "../../../../config/migration.js"; import { isArray } from "@sindresorhus/is"; import { DateTime } from "luxon"; import later from "@breejs/later"; import { Cron, CronPattern } from "croner"; import cronstrue from "cronstrue"; //#region lib/workers/repository/update/branch/schedule.ts const scheduleMappings = { "every month": "before 5am on the first day of the month", monthly: "before 5am on the first day of the month" }; const minutesChar = "*"; function parseCron(scheduleText) { try { return new CronPattern(scheduleText); } catch { return; } } function hasValidTimezone(timezone) { if (!DateTime.local().setZone(timezone).isValid) return [false, `Invalid schedule: Unsupported timezone ${timezone}`]; return [true]; } function hasValidSchedule(schedule) { let message = ""; if (!schedule || schedule === "at any time" || schedule[0] === "at any time") return [true]; if (schedule.some((scheduleText) => { const parsedCron = parseCron(scheduleText); if (parsedCron !== void 0) { if (parsedCron.minute.filter((v) => v !== 1).length !== 0 || !scheduleText.startsWith(minutesChar)) { message = `Invalid schedule: "${scheduleText}" has cron syntax, but doesn't have * as minutes`; return true; } return false; } const massagedText = fixShortHours(scheduleMappings[scheduleText] || scheduleText); const parsedSchedule = later.parse.text(massagedText); if (parsedSchedule.error !== -1) { message = `Invalid schedule: Failed to parse "${scheduleText}"`; return true; } if (parsedSchedule.schedules.some((s) => s.m)) { message = `Invalid schedule: "${scheduleText}" should not specify minutes`; return true; } if (!parsedSchedule.schedules.some((s) => !!s.M || s.d !== void 0 || !!s.D || s.t_a !== void 0 || !!s.t_b)) { message = `Invalid schedule: "${scheduleText}" has no months, days of week or time of day`; return true; } return false; })) return [false, message]; return [true]; } function cronMatches(cron, now, timezone) { const parsedCron = new Cron(cron, { ...timezone && { timezone }, domAndDow: true }); // istanbul ignore if if (!parsedCron) return false; const nextRun = parsedCron.nextRun(); // istanbul ignore if: should not happen if (!nextRun) { logger.warn({ schedule: cron }, "Invalid cron schedule. No next run is possible"); return false; } let nextDate = DateTime.fromJSDate(nextRun); if (timezone) nextDate = nextDate.setZone(timezone); return nextDate.hour === now.hour && nextDate.day === now.day && nextDate.month === now.month; } function isScheduledNow(config, scheduleKey = "schedule") { let configSchedule = config[scheduleKey]; logger.debug(`Checking schedule(schedule=${String(configSchedule)}, tz=${config.timezone}, now=${(/* @__PURE__ */ new Date()).toISOString()})`); if (!configSchedule || configSchedule.length === 0 || configSchedule[0] === "" || configSchedule[0] === "at any time") { logger.debug("No schedule defined"); return true; } if (!isArray(configSchedule)) { logger.warn({ schedule: configSchedule }, "config schedule is not an array"); configSchedule = [configSchedule]; } const validSchedule = hasValidSchedule(configSchedule); if (!validSchedule[0]) { logger.warn(validSchedule[1]); return true; } let now = DateTime.local(); logger.trace(`now=${now.toISO()}`); if (config.timezone) { logger.debug(`Found timezone: ${config.timezone}`); const validTimezone = hasValidTimezone(config.timezone); if (!validTimezone[0]) { logger.warn(validTimezone[1]); return true; } logger.debug("Adjusting now for timezone"); now = now.setZone(config.timezone); logger.trace(`now=${now.toISO()}`); } const currentDay = now.weekday; logger.trace(`currentDay=${currentDay}`); const currentSeconds = now.startOf("second").diff(now.startOf("day"), "seconds").seconds; logger.trace(`currentSeconds=${currentSeconds}`); logger.debug(`Checking ${configSchedule.length} schedule(s)`); const jsNow = now.setZone("utc", { keepLocalTime: true }).toJSDate(); if (!configSchedule.some((scheduleText) => { if (parseCron(scheduleText)) { const cronScheduleSummary = cronstrue.toString(scheduleText, { throwExceptionOnParseError: false }); logger.debug(`Human-readable summary for cron:: ${cronScheduleSummary}`); if (cronMatches(scheduleText, now, config.timezone)) { logger.debug(`Matches schedule ${scheduleText}`); return true; } } else { const massagedText = scheduleMappings[scheduleText] || scheduleText; const parsedSchedule = later.parse.text(fixShortHours(massagedText)); logger.debug({ parsedSchedule }, `Checking schedule "${scheduleText}"`); if (later.schedule(parsedSchedule).isValid(jsNow)) { logger.debug(`Matches schedule ${scheduleText}`); return true; } } return false; })) { logger.debug("Package not scheduled"); return false; } return true; } //#endregion export { hasValidSchedule, hasValidTimezone, isScheduledNow }; //# sourceMappingURL=schedule.js.map