ci: считать раунды ревью по опубликованным документам
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (blend) (push) Failing after 1m56s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (isometric) (push) Failing after 1m45s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (large-house) (push) Failing after 2m0s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (overlay) (push) Failing after 1m57s
Проверка (CI) / Предполётные проверки: документация, провенанс, процесс (push) Failing after 51s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (plan-snap) (push) Failing after 1m53s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (space-default) (push) Failing after 1m48s
Проверка (CI) / Классификация изменённых файлов (push) Successful in 30s
Проверка (CI) / HACS: валидация репозитория (push) Skipped
Проверка (CI) / Hassfest: манифест интеграции (push) Skipped
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Skipped
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 44s
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Skipped
Проверка (CI) / Смоки: все шарды зелёные (push) Skipped
Проверка (CI) / Golden-кадры против принятых эталонов (push) Skipped
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Skipped
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Skipped
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (space-glow) (push) Failing after 1m54s

Зеркало process.yml из dev: конвейер исполняет версию из ветки по умолчанию.

User-Visible: no
Issue: #454
This commit is contained in:
Codex
2026-09-04 20:45:58 +03:00
parent effb36295d
commit 0cd5145df0
+80 -11
View File
@@ -43,9 +43,19 @@ jobs:
spent: ${{ steps.decide.outputs.spent }}
limit: ${{ steps.decide.outputs.limit }}
steps:
# Мелкий checkout: guard остаётся лёгким, но ему нужен
# scripts/review-doc-guard.mjs — счёт раундов вынесен туда, потому что
# inline-shell не покрывается тестами (#454). Node на раннере
# предустановлен, setup-node не нужен.
- uses: actions/checkout@v7
with:
fetch-depth: 1
ref: dev
persist-credentials: false
- id: decide
env:
GH_TOKEN: ${{ secrets.HP_PROCESS_TOKEN }}
REPO: ${{ github.repository }}
LABEL: ${{ github.event.label.name }}
BLOCKED: ${{ contains(github.event.issue.labels.*.name, 'blocked') }}
EXHAUSTED: ${{ contains(github.event.issue.labels.*.name, 'review-4') }}
@@ -87,19 +97,78 @@ jobs:
#
# Вердикты считаются ТОЛЬКО своего этапа: иначе вердикт по ТЗ съедал
# цикл из бюджета код-ревью (#89 получило r2/4). Этап опознаётся по
# имени документа в теле комментария; документа нет — вердикт не
# посчитается. Недосчёт даёт лишний заход, перерасчёт остановил бы
# работу досрочно: из двух ошибок выбрана обратимая.
# имени документа — раньше по подстроке маркера в теле комментария,
# теперь по имени документа ЭТОЙ задачи, `<MARKER>-<NUM>`: голая
# подстрока протекала на прозе. #454 поймала это на себе — разбор
# чужих задач в комментарии содержал `CODE-REVIEW`, и первый же
# код-ревью получил заход r3.
#
# Счёт по комментариям остаётся ровно тем же, но он БОЛЬШЕ НЕ
# ЕДИНСТВЕННЫЙ (#454). Маркер этапа попадает в тело комментария,
# только если ревьюер сам назвал имя файла, — то есть прежний счёт
# зависел от формулировки. На #449 первый спек-вердикт файла не
# назвал, заход r2 получил номер r1, и документ второго раунда лёг
# ПОВЕРХ документа первого. Оценка «недосчёт обратим» была неверна
# ровно здесь: номер захода входит в имя файла, и повтор номера — не
# лишний заход, а потеря артефакта.
#
# Поэтому рядом встаёт второй источник — опубликованные документы:
# их имена несёт сам конвейер, и подделать их прозой нельзя. Берётся
# МАКСИМУМ двух источников: недосчёт возможен только при отказе
# обоих, перерасчёт невозможен по построению.
attempt=1; spent=0; spent_list=""
if [ -n "$stage" ]; then
comments=$(gh issue view "$NUM" --repo "${{ github.repository }}" --json comments)
of_stage="[.comments[] | select(.body | test(\"Вердикт:\")) | select(.body | test(\"$marker\"))]"
# Блокирующим считается вердикт, у которого в строке вердикта стоит
# «жёлтый» или «красный». Регистр и окружение слова не важны.
blocking="$of_stage | map(select(.body | test(\"Вердикт:[^\\n]*(жёлт|красн)\"; \"i\")))"
attempt=$(( $(printf '%s' "$comments" | jq -r "$of_stage | length") + 1 ))
spent=$(printf '%s' "$comments" | jq -r "$blocking | length")
spent_list=$(printf '%s' "$comments" | jq -r "$blocking | map(\"- \" + .url) | join(\"\\n\")")
comments=$(mktemp)
gh issue view "$NUM" --repo "$REPO" --json comments > "$comments"
# Ветка задачи — та же, что выберет шаг ревью: свежая по коммиту.
# Её нет у задач, размеченных до появления конвейера; тогда счёт по
# файлам даёт ноль и работает страховка по комментариям.
branch=""; newest=""
for ref in $(gh api "repos/$REPO/git/matching-refs/heads/issue/$NUM-" \
--jq '.[].ref' 2>/dev/null | sed 's|^refs/heads/||'); do
date=$(gh api "repos/$REPO/commits/$ref" --jq '.commit.committer.date' 2>/dev/null || true)
if [ -n "$date" ] && { [ -z "$newest" ] || [ "$date" \> "$newest" ]; }; then
newest="$date"; branch="$ref"
fi
done
target="${branch:-dev}"
names=$(mktemp); docs=$(mktemp -d)
if ! gh api "repos/$REPO/contents/docs/reviews?ref=$target" --jq '.[].name' \
> "$names" 2>/dev/null; then
: > "$names"
echo "::warning::список docs/reviews на $target не получен — счёт по файлам отключён"
fi
# Каталог перечисляется одним ответом до 1000 записей; за этой
# границей ответ молча обрежется, и счёт по файлам занизится.
if [ "$(grep -c . "$names")" -ge 1000 ]; then
echo "::warning::в docs/reviews не меньше 1000 файлов — листинг contents обрезается, счёт по файлам ненадёжен"
fi
# Тела нужны только своим документам этапа: их единицы.
for name in $(grep -E "^${marker}-${NUM}-r[0-9]+\\.md$" "$names" || true); do
gh api "repos/$REPO/contents/docs/reviews/$name?ref=$target" \
-H 'Accept: application/vnd.github.raw' > "$docs/$name" 2>/dev/null || rm -f "$docs/$name"
done
list=$(mktemp)
counters=$(node scripts/review-doc-guard.mjs --counters \
--marker="$marker" --num="$NUM" --names="$names" --docs="$docs" \
--comments="$comments" --spent-list="$list")
spent_list=$(cat "$list")
# Пустой ответ означает, что скрипт не отработал. Тогда остаются
# значения по умолчанию (заход 1, циклов 0): guard обязан
# продолжить работу, а не встать.
new_attempt=$(printf '%s\n' "$counters" | sed -n 's/^attempt=//p')
new_spent=$(printf '%s\n' "$counters" | sed -n 's/^spent=//p')
new_blocking=$(printf '%s\n' "$counters" | sed -n 's/^blocking=//p')
case "$new_attempt" in ''|*[!0-9]*) echo "::warning::счётчик раундов не дал числа — работают значения по умолчанию" ;; *) attempt="$new_attempt" ;; esac
case "$new_spent" in ''|*[!0-9]*) : ;; *) spent="$new_spent" ;; esac
# Перечень учтённого обязан сходиться с числом: если цикл виден
# только документом, ссылка на комментарий его не объяснит.
if [ -n "$new_blocking" ]; then
spent_list="$spent_list
- документы: $new_blocking"
fi
echo "ветка материала: ${branch:-нет, читался dev}"
fi
# Отказ обязан быть виден в issue, а не только в логе прогона.