Skip to content

test: Phase 4B GitHub API Integration Tests (118 tests) #3981

test: Phase 4B GitHub API Integration Tests (118 tests)

test: Phase 4B GitHub API Integration Tests (118 tests) #3981

name: Validate PR Template
on:
# Safe use of `pull_request_target`:
# - the workflow needs write access so it can comment on PRs, including forks
# - it only inspects the PR body and labels; it does not check out or execute user code
# - any future step that executes repository code must preserve this trust boundary
pull_request_target:
types: [opened, edited, reopened, synchronize, ready_for_review]
permissions:
contents: read
issues: write
pull-requests: write
jobs:
validate-pr-template:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Validate pull request body against required template sections
uses: actions/github-script@v7
with:
script: |
const { validatePullRequestBody } = require('./scripts/validation/template-helpers.cjs');
const marker = '<!-- template-enforcement -->';
const pr = context.payload.pull_request;
const author = pr.user?.login || '';
const isDependabot = author === 'dependabot[bot]' || author === 'app/dependabot';
const isImgbot = author === 'imgbot[bot]' || author === 'app/imgbot';
if (isDependabot || isImgbot) {
core.info(`Skipping PR template validation for bot author ${author}.`);
return;
}
const validation = validatePullRequestBody(pr.body || '', pr.labels || [], pr.head?.ref || '');
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
per_page: 100
});
const previous = comments.find((comment) =>
comment.user?.type === 'Bot' && comment.body?.includes(marker)
);
if (validation.missing.length === 0) {
if (previous) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: previous.id,
body: `${marker}\n✅ Template check passed after update. Thanks for fixing the PR description.`
});
}
return;
}
const message = [
marker,
'🚫 This PR description is missing required template content.',
'',
`Missing required section(s): ${validation.missing.join(', ')}`,
'',
'Please update the PR body using one of the repository PR templates:',
'- https://github.com/lightspeedwp/.github/blob/develop/.github/pull_request_template.md',
'- https://github.com/lightspeedwp/.github/tree/develop/.github/PULL_REQUEST_TEMPLATE',
'',
'Empty placeholders, unchecked checklist boxes, and stub issue references do not count.'
].join('\n');
if (previous) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: previous.id,
body: message
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: message
});
}
core.setFailed(`PR #${pr.number} is missing required template content: ${validation.missing.join(', ')}`);