From b86367585d57d0a0b9d5ed64a3aa8cbd66e5bd2b Mon Sep 17 00:00:00 2001 From: "Joshua J. Bouw" Date: Wed, 10 Jun 2026 20:59:15 +0400 Subject: [PATCH] chore(ci): generalize PR template Test Plan to Verification The 'PR template filled in' check hard-required a non-empty '## Test Plan' section, and the template's scaffolding (cargo-test checkboxes) assumes a code-change PR. Release PRs (version bump + changelog roll), docs-only, and CI-only PRs either failed the check or filled in checkboxes that were not true. - Template: '## Test Plan' becomes a generic '## Verification' section with guidance per PR kind (code / release / docs-CI-chore); the Checklist CHANGELOG line covers the release-roll and not-applicable cases. - Check: section extraction is factored into section_content(), and a new check_section_any() accepts '## Verification' or the legacy '## Test Plan' so in-flight PRs keep passing. - Fixes a latent extraction bug: the old sed '1d;$d' deleted the LAST line of the range positionally, which ate a content line whenever the checked section was the final section of the PR body; the closing header is now removed by pattern. Verified locally by running the check function against four bodies: Verification-as-last-section (passes, previously bitten by the $d bug), legacy Test Plan (passes), comment-placeholder-only (fails), section missing (fails). Closes #888 --- .github/pull_request_template.md | 19 +++++++------------ .github/workflows/pr-checks.yml | 28 +++++++++++++++++++++------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f18f40d16..5356914a0 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,20 +14,15 @@ Closes # - -## Test Plan +## Verification - - -### Automated - -- [ ] `cargo test --workspace` passes -- [ ] No new clippy warnings - -### Manual (optional) - - + ## Checklist - [ ] Linked to an issue -- [ ] CHANGELOG.md updated under `[Unreleased]` +- [ ] CHANGELOG.md updated (entry under `[Unreleased]` — or `[Unreleased]` rolled into a version section for a release PR; not applicable to docs/CI-only changes) diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 6838d06a5..c4135e244 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -17,20 +17,34 @@ jobs: ERRORS="" # Check required sections exist and have content beyond the template placeholder + section_content() { + # Extract text between this section header and the next ## header. + # The closing header is removed by pattern (not `$d`) so a section that + # happens to be the last one in the body keeps its final content line. + echo "$PR_BODY" | sed -n "/^## $1/,/^## /p" | sed '1d' | sed '/^## /d' | sed '/^$/d' | sed '/^\s*$/d' + } + check_section() { - local section="$1" - local content - # Extract text between this section header and the next ## header - content=$(echo "$PR_BODY" | sed -n "/^## $section/,/^## /p" | sed '1d;$d' | sed '/^$/d' | sed '/^\s*$/d') - if [ -z "$content" ]; then - ERRORS="${ERRORS}Missing or empty section: ## $section\n" + if [ -z "$(section_content "$1")" ]; then + ERRORS="${ERRORS}Missing or empty section: ## $1\n" fi } + # Accept any one of the named sections (e.g. the current name and a legacy alias) + check_section_any() { + local section + for section in "$@"; do + if [ -n "$(section_content "$section")" ]; then + return 0 + fi + done + ERRORS="${ERRORS}Missing or empty section: ## $1 (or: $(printf '## %s ' "${@:2}"))\n" + } + check_section "Linked Issue" check_section "Summary" check_section "Changes" - check_section "Test Plan" + check_section_any "Verification" "Test Plan" # Check that Linked Issue has an actual issue number, not just the placeholder if ! echo "$PR_BODY" | grep -qE '#[0-9]+'; then