diff --git a/CMakeLists.txt b/CMakeLists.txt index a0b3ce73b..a90354cea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,6 +200,7 @@ add_test(NAME serve_hangup COMMAND ${CMAKE_SOURCE_DIR}/tests/serve_hangup_test.s # engine#1945: scripts/bump-hrx-sync.sh tells AMD's own commit from a line AMD rebased apart from a commit that # is not AMD's. Local git repositories only: no network, no GPU. add_test(NAME bump_hrx_sync COMMAND bash ${CMAKE_SOURCE_DIR}/tests/bump_hrx_sync_test.sh ${CMAKE_SOURCE_DIR}/scripts/bump-hrx-sync.sh) +add_test(NAME bump_hrx_report COMMAND bash ${CMAKE_SOURCE_DIR}/tests/bump_hrx_report_test.sh ${CMAKE_SOURCE_DIR}/scripts/bump-hrx-report.sh) add_test(NAME metrics_serve COMMAND ${CMAKE_SOURCE_DIR}/tests/metrics_serve.sh $) # serve --print-backend: the llama-server Lemonade starts itself, one hop (docs/serve.md): no GPU. add_test(NAME print_backend COMMAND ${CMAKE_SOURCE_DIR}/tests/print_backend.sh $) diff --git a/scripts/bump-hrx-report.sh b/scripts/bump-hrx-report.sh new file mode 100755 index 000000000..a75443953 --- /dev/null +++ b/scripts/bump-hrx-report.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# Copyright 2026 bong-water-water-bong +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# bump-hrx-report.sh -- build the body of the issue a failed scheduled sync files. +# +# Why this exists (engine#1945): the report step already files an issue so a broken sync cannot sit unnoticed +# for five days, but its body was a static template -- the run URL and instructions, never the failing step or +# the files in conflict. So the reader was told where to look and not what broke. This turns the log the run +# already has into the body, and lives in scripts/ so it is testable without running the workflow. +# +# usage: bump-hrx-report.sh --run [--step ] [--log |-] [--attempt ] +# stdout = the issue body; rc 0 unless an argument is missing. +# --log - reads stdin. No --log means "the log was not available": the body says so rather than claiming +# there were no conflicts, which is the difference between "nothing conflicted" and "nobody looked". +set -uo pipefail + +run="" step="" log="" attempt="" +while [ $# -gt 0 ]; do + case "$1" in + --run) run=${2:?--run needs a URL}; shift 2 ;; + --step) step=${2:?--step needs a name}; shift 2 ;; + --log) log=${2:?--log needs a path or -}; shift 2 ;; + --attempt) attempt=${2:?--attempt needs a number}; shift 2 ;; + -h|--help) sed -n '3,17p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + *) echo "bump-hrx-report: unknown argument: $1" >&2; exit 2 ;; + esac +done +[ -n "$run" ] || { echo "bump-hrx-report: --run is required" >&2; exit 2; } + +text="" +if [ "$log" = "-" ]; then + text=$(cat) +elif [ -n "$log" ]; then + if [ -r "$log" ]; then text=$(cat "$log"); else + echo "bump-hrx-report: cannot read log: $log" >&2; exit 2 + fi +fi + +# git names every conflicting path in a line of the form "CONFLICT (content): Merge conflict in "; +# "CONFLICT (modify/delete)" and friends use the same prefix. Paths can repeat across retries, so dedupe. +# git spells the same event two ways, and a report that knows only one of them silently lists half the conflict: +# CONFLICT (content): Merge conflict in +# CONFLICT (modify/delete): deleted in and modified in +# CONFLICT (rename/delete): renamed ... (path is the source side) +conflicts=$(printf '%s\n' "$text" \ + | sed -n -e 's/.*CONFLICT ([^)]*): Merge conflict in \(.*\)$/\1/p' \ + -e 's/.*CONFLICT ([^)]*): \(.*\) \(deleted\|renamed\) in .*/\1/p' \ + | sort -u) + +printf 'The bump-hrx run failed: %s\n' "$run" +if [ -n "$step" ]; then + printf '\n**Failing step:** `%s`' "$step" + [ -n "$attempt" ] && printf ' (attempt %s)' "$attempt" + printf '\n' +fi + +if [ -n "$conflicts" ]; then + printf '\n**Conflicting files** (from the log):\n\n```\n%s\n```\n' "$conflicts" + printf '\nResolve these by hand on `1bit/hrx-vulkan-patched` or on `1bit-MONSTER/hrx-system` `1bit/main`, then re-run the workflow.\n' +elif [ -z "$log" ]; then + printf '\n**The log was not available to this report**, so the conflicting files are not listed here. Open the run above and read the failing step.\n' +else + printf '\n**No conflicting files were named in the log** -- this failure is not a merge conflict. Open the run above and read the failing step.\n' +fi + +printf '\nWhy this report exists: bump-hrx failed on every scheduled run from 2026-10-01 to 2026-10-05 without anyone\nnoticing, and the HRX pin silently froze. A failed run files this issue; it is deduped while open.\n' diff --git a/tests/bump_hrx_report_test.sh b/tests/bump_hrx_report_test.sh new file mode 100755 index 000000000..52d8cca8e --- /dev/null +++ b/tests/bump_hrx_report_test.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Copyright 2026 bong-water-water-bong +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# bump_hrx_report_test.sh -- arms for scripts/bump-hrx-report.sh (engine#1945). +# +# The three cases a failure report must distinguish, because conflating any two of them is how a report stops +# being read: files in conflict, a failure that is not a conflict, and a report that could not read the log. +# +# usage: bump_hrx_report_test.sh +set -uo pipefail + +S=${1:?usage: bump_hrx_report_test.sh } +[ -x "$S" ] || { echo "not executable: $S"; exit 1; } +fail=0 +ok() { echo "ok $1"; } +bad() { echo "FAIL $1"; fail=1; } + +# 1. A merge conflict: git's two shapes (content and modify/delete), one path repeated as a retry would. +got=$("$S" --run https://example/runs/1 --log - --step "Sync the fork and merge AMD's pin into our line" <<'LOG' +Auto-merging ggml/src/ggml-hrx/ggml-hrx.cpp +CONFLICT (content): Merge conflict in ggml/src/ggml-hrx/ggml-hrx.cpp +CONFLICT (modify/delete): drivers/fake.c deleted in HEAD and modified in abc123 +CONFLICT (content): Merge conflict in ggml/src/ggml-hrx/ggml-hrx.cpp +LOG +) +[[ $got == *"ggml/src/ggml-hrx/ggml-hrx.cpp"* ]] && ok "a conflict names the file" || bad "a conflict names the file" +[[ $got == *"drivers/fake.c"* ]] && ok "the modify/delete form is caught too" || bad "the modify/delete form is caught too" +[[ $(printf '%s' "$got" | grep -c 'ggml-hrx/ggml-hrx.cpp') -ge 1 ]] || bad "path present" +n=$(printf '%s\n' "$got" | sed -n '/^```$/,/^```$/p' | grep -c 'ggml/src/ggml-hrx/ggml-hrx.cpp') +[ "$n" = 1 ] && ok "a repeated path is listed once" || bad "a repeated path is listed once (got $n)" +[[ $got == *"Failing step"*"Sync the fork"* ]] && ok "the failing step is in the body" || bad "the failing step is in the body" + +# 2. A failure that is NOT a conflict must say so -- an empty list would read as "no problem found". +got=$("$S" --run https://example/runs/2 --log - --step "Merge AMD's hrx-system pin into our line" <<'LOG' +error: could not apply 1a2b3c4... some commit +fatal: Exiting because of an unresolved dispute +LOG +) +[[ $got == *"not a merge conflict"* ]] && ok "a non-conflict failure says so" || bad "a non-conflict failure says so" +[[ $got != *'```'* ]] && ok "and prints no empty file block" || bad "and prints no empty file block" + +# 3. No log at all: the body must still render and must not claim there were no conflicts. +got=$("$S" --run https://example/runs/3 --step "Sync") +[[ $got == *"log was not available"* ]] && ok "a missing log is stated, not implied" || bad "a missing log is stated, not implied" +[[ $got != *"not a merge conflict"* ]] && ok "a missing log is not reported as a non-conflict" || bad "a missing log is not reported as a non-conflict" +[[ $got == *"https://example/runs/3"* ]] && ok "the run URL is always present" || bad "the run URL is always present" + +# 4. Contract: --run is required, an unknown flag is a usage error, not a silent empty body. +"$S" > /dev/null 2>&1; [ $? = 2 ] && ok "a missing --run exits 2" || bad "a missing --run exits 2" +"$S" --run x --nope > /dev/null 2>&1; [ $? = 2 ] && ok "an unknown argument exits 2" || bad "an unknown argument exits 2" + +[ $fail = 0 ] && echo "all bump-hrx-report arms passed" +exit $fail