Nine of the bug form's fourteen fields were transcription: version, host, OS, terminal, size, platform, device model, firmware, transport. `meshterm diagnostics --out` writes every one of them, correctly, from the machine that has the problem, and the form went on asking a tired person to copy them out by hand. A field that demands precision from someone who does not have it does not produce precision. It produces an abandoned form, or an invented answer -- a reporter guessing at their radio's chip name because the form would not let them past without one. A guess is worse than a blank, because it reads as a fact. So the required set drops from nine fields to five, and not one of the five asks for something the reporter has to look up: what happened, how often, how to repeat it, whether `--mock` changes it, and the diagnostics block itself. Two of the cut fields came back as optional, because the block genuinely cannot know them. It reads the model and firmware off the radio, so a report about a radio that will not connect -- the most common first-run report there is -- has nothing to read them from. And `platform` only separates PicoCalc from regular, so a uConsole and a laptop look alike to it. Both fields now say exactly when they apply, which is rarely. The command itself lands in 0.9.0 and the released build is 0.3.7, so both the form and the forum say what to do when it is not recognised: name your version and send what you can. That line comes out once nobody is running an older one. The trailer workflow inverts the obvious design. It does not nag about commits with no issue trailer, because most commits here legitimately close nothing and a workflow that complains about all of them gets muted within a week. It checks the trailers that are there: `Fixes: #42` against an issue that does not exist is not an error in GitHub's eyes, it is silence -- the issue stays open, the reporter is never told, and the commit looks like it did the job. That is the failure worth a comment. Four labels join the stock set for the states a report actually passes through: needs-diagnostics, confirmed, needs-hardware, cannot-reproduce. They say what state a thing is in. The hardware goes in the title, where it does not multiply. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
85 lines
3.3 KiB
YAML
85 lines
3.3 KiB
YAML
name: Issue trailers
|
|
|
|
# A commit that names an issue is the only thing connecting a fix to the person
|
|
# who reported it. GitHub resolves that link silently: `Fixes: #42` against an
|
|
# issue that does not exist is not an error, it is nothing at all -- the issue
|
|
# stays open, the reporter is never told, and the commit looks like it did the
|
|
# job. A typo costs a whole loop and announces itself nowhere.
|
|
#
|
|
# So this checks the trailers that ARE there rather than nagging about the ones
|
|
# that are not. Most commits here are internal work that legitimately closes
|
|
# nothing, and a workflow that complains about all of them gets muted in a week.
|
|
#
|
|
# Cross-repo trailers (`Fixes: owner/repo#12`) are reported but not verified:
|
|
# GITHUB_TOKEN only reaches this repository.
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
|
|
permissions:
|
|
contents: write # to leave a comment on the offending commit
|
|
issues: read
|
|
|
|
jobs:
|
|
check:
|
|
runs-on: ubuntu-latest
|
|
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
|
steps:
|
|
- name: Verify every issue named in a trailer exists
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
REPO: ${{ github.repository }}
|
|
COMMITS: ${{ toJSON(github.event.commits) }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
echo "$COMMITS" > /tmp/commits.json
|
|
count=$(jq 'length' /tmp/commits.json)
|
|
echo "checking $count commit(s)"
|
|
|
|
problems=0
|
|
|
|
for i in $(seq 0 $((count - 1))); do
|
|
sha=$(jq -r ".[$i].id" /tmp/commits.json)
|
|
msg=$(jq -r ".[$i].message" /tmp/commits.json)
|
|
short="${sha:0:8}"
|
|
|
|
# Trailers only: a line that starts with the keyword. A bare "#12"
|
|
# in prose is a reference someone wrote on purpose, not a claim
|
|
# that this commit closes anything.
|
|
refs=$(printf '%s\n' "$msg" \
|
|
| grep -oiE '^(fixes|closes|resolves|refs|see):[[:space:]]*([A-Za-z0-9._-]+/[A-Za-z0-9._-]+)?#[0-9]+' \
|
|
|| true)
|
|
|
|
[ -z "$refs" ] && continue
|
|
|
|
while IFS= read -r ref; do
|
|
[ -z "$ref" ] && continue
|
|
target=$(printf '%s' "$ref" | sed -E 's/^[A-Za-z]+:[[:space:]]*//')
|
|
num="${target##*#}"
|
|
|
|
if [ "$target" != "#${num}" ]; then
|
|
repo="${target%%#*}"
|
|
echo " $short -> $target (other repository, not verified here)"
|
|
continue
|
|
fi
|
|
|
|
if state=$(gh api "repos/$REPO/issues/$num" --jq .state 2>/dev/null); then
|
|
echo " $short -> #$num ($state)"
|
|
else
|
|
echo "::error::commit $short names #$num, which does not exist in $REPO"
|
|
problems=$((problems + 1))
|
|
gh api "repos/$REPO/commits/$sha/comments" \
|
|
-f body="This commit's trailer names **#$num**, which does not exist in \`$REPO\`.
|
|
|
|
GitHub resolves a closing keyword silently, so nothing failed and nothing was linked — the issue this was meant to close is still open, and whoever reported it has not been told. Worth a follow-up commit with the right number, or a note on the issue itself." \
|
|
>/dev/null
|
|
fi
|
|
done <<< "$refs"
|
|
done
|
|
|
|
if [ "$problems" -gt 0 ]; then
|
|
echo "::warning::$problems trailer(s) pointed at issues that do not exist"
|
|
fi
|