feat(release): warn in Slack about PRs in a release that lack QA verdict#35762
feat(release): warn in Slack about PRs in a release that lack QA verdict#35762nollymar wants to merge 3 commits into
Conversation
Add a standalone TypeScript tool at .github/scripts/release-qa-status that, given a release tag, inspects every PR in the range and reports a QA verdict per PR by reading the labels of its closing-issue references (sourced from GitHub's GraphQL closingIssuesReferences — covers body keywords AND the Development panel). Buckets: - failed: any linked issue carries `QA : Failed` - missing: linked but no recognized QA label - unlinked: no closing-issue reference at all - external: only cross-repo closing refs (cannot verify QA from here) - passed: every linked issue is `QA : Passed` or `QA : Not Needed` - excluded: bots / dependency bumps / release machinery Wire the tool into the release pipeline's Report step (cicd_6-release.yml). The QA section is appended to the existing Slack release announcement only when there is something to flag, and the step is non-blocking — any failure leaves the announcement intact without a QA section. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Claude finished @nollymar's task in 3m 33s —— View job ReviewA few things worth a second look. Bugs / correctness1. Stale "more than 50" warning text — 2. Closing-issue refs are not paginated — 3. Commit pagination + if (response.data.commits.length < perPage) break;fires after the first page (because 100 < 250), even when This mirrors Minor4. 5. 6. Asymmetric error handling between batches — Looks clean
|
Failure isolation: every QA-coverage step in the Report job is now continue-on-error: true so a transient `actions/setup-node` or `npm ci` failure cannot block the Slack release announcement. LTS / CLI skip: the QA steps now bail early on non-standard tags (`_lts_`, `dotcms-cli-`, anything not v-prefixed), matching the existing AI release-notes phase. GraphQL failure handling: closingIssuesReferences batch errors are re-thrown instead of swallowed — silently returning empty refs caused every PR in the failing batch to be misclassified as `unlinked` and spammed the Slack channel with bogus orphan-PR rows. The outer continue-on-error still drops the QA section if anything throws. Bot detection: tightened login matching from .includes() to .startsWith() on a known-prefix list, so accounts like `fan-of-github-actions` are no longer treated as bots. Slack message polish: header emoji escalates to 🚨 when any PR failed QA; titles longer than 140 chars end with an ellipsis; the workflow no longer emits a trailing blank line on healthy releases (empty `qa_warning` round-trips cleanly through the quoted SLACK_MESSAGE). Text output: excluded PRs now render with their reason for easier debugging. Documentation: added comments calling out the squash-merge assumption on `extractPRNumbers` and the GraphQL alias safety constraint. Tests: added jest + ts-jest with unit coverage for `classifyExclusion`, `computePRQA`, and `renderSlack` (26 cases). The pure aggregation rules, bot exclusion edge cases, and Slack rendering are now verified. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thanks for the thorough review. Addressed in 4750742: Fixed
Tests all green (26/26). |
Tag-list race (#1): when the just-cut release tag isn't yet visible in the GitHub releases API (eventual consistency), fall back to the newest indexed tag as the predecessor instead of exiting 1 silently. The listReleases response is sorted newest-first, so tags[0] is the previous standard release. Also added a guard for an empty tag list. allSettled on issue fetch (#3): switched fetchIssueInfos from Promise.all to Promise.allSettled so a single transient 5xx on an issue lookup can no longer drop the entire QA section. Failed entries are treated the same as notFound — qa.ts already ignores both. Closing-refs page size (#2): bumped first: 50 to first: 100 in closingIssuesReferences. PRs with >100 closing refs still emit the hasNextPage stderr warning but the cap is now generous enough that no real PR will hit it. Squash-merge silent failure (#4): if extractPRNumbers returns zero for a non-empty commit list, emit an explicit stderr warning naming the likely cause (merge strategy change on `main`). Keeps the failure mode visible if dotCMS ever moves off squash-merge. GraphQL alias safety (#5): added a Number.isInteger guard in the alias builder. Type-system-wise nothing stopped a future caller from passing NaN or a negative integer; the guard enforces the trust boundary at fetchClosingIssueRefs rather than relying on the comment. Backtick escape in Slack output (#6): `escapeSlack` now replaces backticks with apostrophes so PR titles like "bump deps to `v1.2.3`" don't render the version as monospace in Slack. Excluded blurb (#9): renderText excluded-section blurb now lists all four exclusion reasons (bot / dependency-bump / version-bump / release-machinery) so it matches what classifyExclusion actually returns. Tests: added cases for same-repo missing winning over coexistent externalRefs (#10) and for backtick replacement in Slack titles. 28/28 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Second round addressed in 37c1f69:
Not addressed:
Tests: 28/28 pass. |
Summary
Adds a QA-coverage warning to the Slack notification we send when a release is generated. For every PR in the release, the new tool inspects its closing-issue references and reports whether the linked issue carries a recognized QA label.
Driven by the question: "which changes shipped in this release haven't been tested?"
How it works
New standalone tool at
.github/scripts/release-qa-status/(TypeScript, mirrors the shape ofgather-release-dataso the two can be consolidated later).For each PR between the previous and current release tag:
closingIssuesReferences(catches body keywords and Development-panel links) and bucket:QA : FaileddotCMS/private-issues#N) — cannot verify QA from hereQA : PassedorQA : Not NeededThe release workflow (
.github/workflows/cicd_6-release.yml) Report job now installs and runs the tool with--format slack, capturing the output intoSLACK_MESSAGE. The step iscontinue-on-error: true— any failure leaves the announcement intact without a QA section, never blocking a release.Slack message (when something is flagged)
Healthy releases keep the current short message. Otherwise the announcement gains a section like:
Validation
Ran the tool locally against recent releases:
v26.05.19-01v26.05.18-01v26.05.11-01Spot-checked every verdict against
gh issue view— labels and statuses line up.Dry-ran the new bash step locally with the same logic the workflow will execute; the multi-line
GITHUB_OUTPUTcapture round-trips correctly into the Slack action's env var.Follow-up
Filed #35763 to migrate
gather-release-datato the same GraphQL approach so release-notes bullets stop missing their issue cross-links. Scoped separately to keep this PR focused.Test plan
cicd_6-release.ymlwithnotify_slack=falseagainst a recent release tag — verifies the new steps are correctly gated.notify_slack=trueagainst a release whose previous tag is auto-resolvable — verify the QA section renders in #release Slack channel.🤖 Generated with Claude Code