diff --git a/.github/scripts/validate-pr-description.js b/.github/scripts/validate-pr-description.js index 5c05522..866e338 100644 --- a/.github/scripts/validate-pr-description.js +++ b/.github/scripts/validate-pr-description.js @@ -2,14 +2,29 @@ * Validate that a PR description has a filled Overview section. * Intended for use with actions/github-script. * + * Revert PRs and bot authors whose login matches EXEMPT_BOT_LOGIN_PATTERN are + * exempt; detection lives here rather than in a caller/workflow `if` condition + * so the job still completes successfully instead of showing as skipped, which + * can block merges when this check is required. + * * @param {{ core: import('@actions/core'), context: import('@actions/github').Context }} params * @param {{ minOverviewLength?: number }} options */ -/** - * Revert PRs are exempt from description validation. Detection lives here rather than - * in a workflow `if` condition so the job still completes successfully instead of - * showing as skipped, which can block merges when this check is required. - */ + +/** Case-insensitive: "alloy" anywhere, or "ai" as a hyphen/underscore-delimited token. */ +const EXEMPT_BOT_LOGIN_PATTERN = /alloy|(?:^|[-_])ai(?:[-_]|$)/i; + +function normalizeBotLogin(login) { + return (login ?? '').toLowerCase().replace(/\[bot\]$/, ''); +} + +function isExemptBotPr(pr) { + if (pr?.user?.type !== 'Bot') { + return false; + } + return EXEMPT_BOT_LOGIN_PATTERN.test(normalizeBotLogin(pr?.user?.login)); +} + function isRevertPr(pr) { const title = pr?.title ?? ''; const headRef = pr?.head?.ref ?? ''; @@ -20,6 +35,11 @@ module.exports = async function validatePrDescription({ core, context }, options const minOverviewLength = Number(options.minOverviewLength) || 40; const pr = context.payload.pull_request; + if (isExemptBotPr(pr)) { + core.info('Skipping PR description check for exempt bot-authored PR.'); + return; + } + if (isRevertPr(pr)) { core.info('Skipping PR description check for revert PR.'); return; diff --git a/.github/workflows/callable.pr-description-check.yaml b/.github/workflows/callable.pr-description-check.yaml index c443e37..09918b4 100644 --- a/.github/workflows/callable.pr-description-check.yaml +++ b/.github/workflows/callable.pr-description-check.yaml @@ -11,6 +11,16 @@ on: GH_TOKEN: required: true +# The actual use of the workflow should have the following: +#on: +# pull_request: +# types: [opened, edited, reopened] +# +# Exempt bot and revert PRs are handled in validate-pr-description.js so this job +# still passes. Other bot PRs must fill Overview. Callers must always invoke this +# workflow — do not add a job-level if on the caller, or the required check stays +# pending for bot-authored PRs. + jobs: pr-description-check: name: PR description check @@ -37,7 +47,7 @@ jobs: const script = require('${{ github.workspace }}/.github/scripts/validate-pr-description.js'); // github, context, and core are injected by github-script from this // job's runtime context (the caller's workflow run / PR payload). - // Revert PRs (title "Revert ..." or revert-* branch) are exempt — see validate-pr-description.js. + // Exempt bot and revert PRs are handled in validate-pr-description.js. await script( { github, context, core }, { minOverviewLength: Number(process.env.MIN_OVERVIEW_LENGTH) }