diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 2be92a9..1730ccb 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -7,12 +7,26 @@ body: value: | Hopefully you never need this form. - If you do: how much of this you fill in really does decide what happens next. A - report I can act on gets worked on. A vague one usually doesn't, because I can't - do anything with it. If it isn't worth describing properly, it's probably not - worth fixing. + If you do: start by running this, and attach the file it writes. - Most of these take ten seconds each. + ``` + meshterm diagnostics --out bug.md + ``` + + It answers which MeshTerm, on which machine, in which terminal, talking to which + radio over which transport and firmware — most of what I would otherwise ask one + question at a time, and the details people most often end up guessing at. Nothing + in it is private: no keys, no pairing PINs, no positions, no contacts. That is a + tested property, not a promise. + + No clipboard on your handheld? The **Diagnostics** page saves the same file with + `s`, next to the log. + + **`diagnostics` not recognised?** You are on 0.3.7 or older, where the command does + not exist yet. Say which version you have in the block below and fill in what you + can — that is a complete answer, not a failure. + + The rest of this form asks only for what that file cannot know. - type: textarea id: problem @@ -49,118 +63,63 @@ body: validations: required: true - - type: textarea - id: workaround - attributes: - label: Is there a way around it - description: Anything that avoids it, or gets you unstuck. Say so even if it's ugly. - - - type: input - id: version - attributes: - label: MeshTerm version - description: It's on the splash screen when you start, and on the About page. - placeholder: "0.2.0" - validations: - required: true - - - type: input - id: hardware - attributes: - label: What are you running MeshTerm on - description: > - Desktop, laptop, Mac, or a handheld — and if it's a handheld, which one. - placeholder: "ClockworkPi uConsole (CM4), or: desktop PC" - validations: - required: true - - - type: input - id: os - attributes: - label: Which OS, exact version - placeholder: "Windows 11 24H2 / macOS 15.3 / Debian 12" - validations: - required: true - - - type: input - id: terminal - attributes: - label: Which terminal, and how big - description: > - PowerShell, Windows Terminal, iTerm2, the PicoCalc console, ssh from something. - Size in columns x rows if you know it — a lot of layout bugs are really terminal - bugs. - placeholder: "Windows Terminal, 120x40" - validations: - required: true - - - type: input - id: companion - attributes: - label: Which companion device - description: > - Be precise. There are a lot of these and they don't behave the same. "A Xiao" - doesn't narrow it down — "Seeed XIAO nRF52840 + Wio-SX1262" does. Leave blank if - no radio was involved. - placeholder: "Heltec V3 / RAK4631 / XIAO nRF52840 + Wio-SX1262" - - - type: input - id: firmware - attributes: - label: Which firmware version is on it - placeholder: "MeshCore 1.15.0" - - - type: dropdown - id: transport - attributes: - label: How is it connected - options: - - USB / serial - - Bluetooth - - TCP - - SPI-to-TCP bridge - - Something else (say so below) - - No radio — simulator / --mock - validations: - required: true - - - type: dropdown - id: flavour - attributes: - label: Regular or PicoCalc - description: > - MeshTerm draws itself differently on a PicoCalc — narrower, fewer colours, a - different font. A bug in one often doesn't exist in the other, so this saves a - round of guessing. - options: - - Regular (desktop, laptop, ssh) - - PicoCalc handheld - - Not sure - validations: - required: true - - type: dropdown id: mock attributes: label: Does it still happen with `meshterm --mock` description: > - `--mock` runs the whole app against a fake radio. If the bug survives that, it's - in MeshTerm and I can chase it without hardware. If it doesn't, it's in the link - or the radio, which is a different hunt. Skip it if the bug is about connecting - in the first place. + `--mock` runs the whole app against a fake radio. If the bug survives that, it is + in MeshTerm and I can chase it without owning your hardware. If it doesn't, it is + in the link or the radio, which is a different hunt. This one question decides + which of those two jobs this is, so it is worth the minute it takes. options: - "Yes, still happens with --mock" - "No, --mock is fine" - "Haven't tried" - - "Doesn't apply" + - "Doesn't apply — the problem is connecting" + validations: + required: true + + - type: textarea + id: diagnostics + attributes: + label: The diagnostics block + description: > + Attach `bug.md` by dragging it in, or paste its contents here. If you genuinely + cannot produce one — an older build, or MeshTerm will not start at all — write down + why instead, along with your version, and I will work with that. + render: markdown + validations: + required: true + + - type: input + id: handheld + attributes: + label: Which handheld, if it is a handheld + description: > + Only if you are running on something the diagnostics file cannot name. It knows a + PicoCalc from a regular terminal, but a uConsole and a laptop look much the same to + it. Leave blank on a desktop, laptop or Mac. + placeholder: "ClockworkPi uConsole (CM4) / M5Stack Cardputer" + + - type: input + id: radio + attributes: + label: Which radio and firmware, if MeshTerm could not reach it + description: > + The diagnostics file reads the model and firmware off the radio itself, so when the + problem *is* connecting, it has nothing to read. That is the one case where I need + you to tell me. Be precise — "a Xiao" covers several boards that do not behave + alike. + placeholder: "Seeed XIAO nRF52840 + Wio-SX1262, MeshCore 1.15.0" - type: textarea id: error attributes: label: The exact error, if there was one description: > - Copy it as text rather than photographing the screen — text can be searched, and - a photo of a terminal is hard to read. Paste the whole traceback if it crashed. + Copy it as text rather than photographing the screen — text can be searched, and a + photo of a terminal is hard to read. Paste the whole traceback if it crashed. render: text - type: textarea @@ -170,10 +129,16 @@ body: description: > MeshTerm keeps one at `~/.meshterm/meshterm.log` (or wherever `MESHTERM_HOME` points). Attach it, or paste the last twenty lines. If the problem is hard to - catch, set **Log detail** to `DEBUG` on the Preferences page, do it once more, - and send that instead — it records a great deal more. + catch, set **Log detail** to `DEBUG` on the Preferences page, do it once more, and + send that instead — it records a great deal more. render: text + - type: textarea + id: workaround + attributes: + label: Is there a way around it + description: Anything that avoids it, or gets you unstuck. Say so even if it's ugly. + - type: textarea id: extra attributes: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 24495df..d951483 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -4,10 +4,12 @@ contact_links: url: https://discord.gg/AZwe5Uvb3S about: > Setup trouble, "is it supposed to do this", or anything you want a second opinion - on. The support forum is the place. Someone running your radio has probably hit it. + on. The #support forum is the place. Someone running your radio has probably hit it. - - name: No GitHub account? Report it on Discord instead + - name: No GitHub account? Report it in the #bugs forum instead url: https://discord.gg/AZwe5Uvb3S about: > - Drop reproducible bugs in #bugs and they'll get moved here. Being told beats + One post per bug, tagged, with the diagnostics file attached. Real ones get opened + here and your post is renamed with the issue number, so you can follow it without an + account — and something comes back to tell you when it ships. Being told beats filing it tidily. diff --git a/.github/workflows/issue-trailers.yml b/.github/workflows/issue-trailers.yml new file mode 100644 index 0000000..03bfde7 --- /dev/null +++ b/.github/workflows/issue-trailers.yml @@ -0,0 +1,84 @@ +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