diff --git a/lib/workers/repository/update/pr/body/config-description.spec.ts b/lib/workers/repository/update/pr/body/config-description.spec.ts index 25e835bde61f04..47a8956c8314fd 100644 --- a/lib/workers/repository/update/pr/body/config-description.spec.ts +++ b/lib/workers/repository/update/pr/body/config-description.spec.ts @@ -53,9 +53,31 @@ describe('workers/repository/update/pr/body/config-description', () => { it('renders UTC as the default timezone', () => { const res = getPrConfigDescription({ ...config, - schedule: ['* 1 * * * *'], + schedule: ['* 1 * * *'], }); - expect(res).toContain(`"* 1 * * * *" (UTC)`); + expect(res).toContain( + 'Between 01:00 AM and 01:59 AM ( * 1 * * * ) (UTC)', + ); + }); + + it('summarizes cron schedules', () => { + const res = getPrConfigDescription({ + ...config, + schedule: ['* 1 * * *', '* * 2 * 1'], + }); + expect(res).toContain( + 'Between 01:00 AM and 01:59 AM ( * 1 * * * ), On day 2 of the month, and on Monday ( * * 2 * 1 ) (UTC)', + ); + }); + + it('displays later schedules', () => { + const res = getPrConfigDescription({ + ...config, + schedule: ['before 6am on Monday', 'after 3pm on Tuesday'], + }); + expect(res).toContain( + '"before 6am on Monday,after 3pm on Tuesday" (UTC)', + ); }); it('renders undefined schedule', () => { diff --git a/lib/workers/repository/update/pr/body/config-description.ts b/lib/workers/repository/update/pr/body/config-description.ts index 9249247ad25696..8cea8e4d68df10 100644 --- a/lib/workers/repository/update/pr/body/config-description.ts +++ b/lib/workers/repository/update/pr/body/config-description.ts @@ -1,3 +1,6 @@ +import { CronPattern } from 'croner'; +import cronstrue from 'cronstrue'; +import { capitalize } from '../../../../../../tools/docs/utils'; import { emojify } from '../../../../../util/emoji'; import type { BranchConfig } from '../../../../types'; @@ -51,7 +54,8 @@ function scheduleToString( ): string { let scheduleString = ''; if (schedule && schedule[0] !== 'at any time') { - scheduleString += `"${String(schedule)}"`; + scheduleString = + getReadableCronSchedule(schedule) ?? `"${String(schedule)}"`; if (timezone) { scheduleString += ` in timezone ${timezone}`; } else { @@ -62,3 +66,28 @@ function scheduleToString( } return scheduleString; } + +/** + * Return human-readable cron schedule summary if the schedule is a valid cron + * else return null + */ +function getReadableCronSchedule(scheduleText: string[]): string | null { + // assuming if one schedule is cron the others in the array will be cron too + try { + new CronPattern(scheduleText[0]); // validate cron + return scheduleText + .map( + (cron) => + capitalize( + cronstrue + .toString(cron, { + throwExceptionOnParseError: false, + }) + .replace('Every minute, ', ''), + ) + ` ( ${cron} )`, + ) + .join(', '); + } catch { + return null; + } +}