mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Merge updated dev for v1.63.0
This commit is contained in:
@@ -48,6 +48,7 @@ jobs:
|
|||||||
BLOCKED: ${{ contains(github.event.issue.labels.*.name, 'blocked') }}
|
BLOCKED: ${{ contains(github.event.issue.labels.*.name, 'blocked') }}
|
||||||
EXHAUSTED: ${{ contains(github.event.issue.labels.*.name, 'review-4') }}
|
EXHAUSTED: ${{ contains(github.event.issue.labels.*.name, 'review-4') }}
|
||||||
SMALL: ${{ contains(github.event.issue.labels.*.name, 'small') }}
|
SMALL: ${{ contains(github.event.issue.labels.*.name, 'small') }}
|
||||||
|
TRIVIAL: ${{ contains(github.event.issue.labels.*.name, 'trivial') }}
|
||||||
NUM: ${{ github.event.issue.number }}
|
NUM: ${{ github.event.issue.number }}
|
||||||
run: |
|
run: |
|
||||||
# Этап определяется первым: от него зависит, какие вердикты считать.
|
# Этап определяется первым: от него зависит, какие вердикты считать.
|
||||||
@@ -58,8 +59,9 @@ jobs:
|
|||||||
*) echo "метка $LABEL конвейер не запускает" ;;
|
*) echo "метка $LABEL конвейер не запускает" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Лимит циклов: 4 обычный, 2 на лёгком треке (PROCESS.md §4).
|
# Лимит циклов: 4 обычный, 2 на лёгком и коротком треке (PROCESS.md §4).
|
||||||
limit=4; [ "$SMALL" = "true" ] && limit=2
|
limit=4
|
||||||
|
if [ "$SMALL" = "true" ] || [ "$TRIVIAL" = "true" ]; then limit=2; fi
|
||||||
|
|
||||||
# Счётчик считает вердикты ТОЛЬКО своего этапа. Раньше он брал все
|
# Счётчик считает вердикты ТОЛЬКО своего этапа. Раньше он брал все
|
||||||
# подряд, и вердикт по ТЗ съедал цикл из бюджета код-ревью: на #89
|
# подряд, и вердикт по ТЗ съедал цикл из бюджета код-ревью: на #89
|
||||||
@@ -134,8 +136,15 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
|
# Окружение готовит workflow, а не модель своими ходами. Раньше промпт
|
||||||
|
# велел ревьюеру самому выполнить `npm ci`: минуты уходили на установку без
|
||||||
|
# кэша, платились из бюджета 45 минут и из лимитов подписки, а ходы модели
|
||||||
|
# тратились на работу инфраструктуры. В validate.yml кэш стоит на всех
|
||||||
|
# тяжёлых job, здесь его не было.
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with: { node-version: 22 }
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
|
||||||
# Материал ревью живёт в ветке задачи: ТЗ в docs/specs/ и код коммитятся
|
# Материал ревью живёт в ветке задачи: ТЗ в docs/specs/ и код коммитятся
|
||||||
# в issue/<NN>-slug. Если ветка запушена — переключаемся на неё, иначе
|
# в issue/<NN>-slug. Если ветка запушена — переключаемся на неё, иначе
|
||||||
@@ -156,6 +165,24 @@ jobs:
|
|||||||
echo "МАТЕРИАЛ НЕ ЗАПУШЕН" >> "$GITHUB_STEP_SUMMARY"
|
echo "МАТЕРИАЛ НЕ ЗАПУШЕН" >> "$GITHUB_STEP_SUMMARY"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Зависимости ставятся ПОСЛЕ переключения на ветку задачи: lockfile мог
|
||||||
|
# измениться именно в ней, и установка по копии из dev дала бы не то дерево.
|
||||||
|
- name: Установить зависимости
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
# Браузер нужен не всякому ревью (см. правило выбора гейтов в промпте),
|
||||||
|
# но когда нужен — качать его заново дороже, чем держать в кэше.
|
||||||
|
- name: Кэш браузеров Playwright
|
||||||
|
id: pw
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.cache/ms-playwright
|
||||||
|
key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
|
||||||
|
|
||||||
|
- name: Установить Chromium
|
||||||
|
if: steps.pw.outputs.cache-hit != 'true'
|
||||||
|
run: npx playwright install --with-deps chromium
|
||||||
|
|
||||||
- name: Review
|
- name: Review
|
||||||
id: review
|
id: review
|
||||||
uses: anthropics/claude-code-action@v1
|
uses: anthropics/claude-code-action@v1
|
||||||
@@ -205,10 +232,38 @@ jobs:
|
|||||||
По каждому AC: либо он доказан автотестом и ты убедился, что тест
|
По каждому AC: либо он доказан автотестом и ты убедился, что тест
|
||||||
умеет падать, либо разобран по коду с явной записью «проверено
|
умеет падать, либо разобран по коду с явной записью «проверено
|
||||||
чтением, не исполнением». «Verified» без названной команды и её
|
чтением, не исполнением». «Verified» без названной команды и её
|
||||||
результата доказательством не является. Зависимостей в рабочей
|
результата доказательством не является. Зависимости уже установлены
|
||||||
копии нет: перед гейтами выполни `npm ci`. Проверь трейлеры Issue и
|
workflow, Chromium тоже — `npm ci` выполнять не нужно. Проверь
|
||||||
User-Visible, при User-Visible: yes — правки в оба changelog в том же
|
трейлеры Issue и User-Visible, при User-Visible: yes — правки в оба
|
||||||
коммите.
|
changelog в том же коммите.
|
||||||
|
|
||||||
|
**Объём гейтов соразмерен задаче.** Прогонять весь набор на каждой
|
||||||
|
правке — не тщательность, а потеря времени: полные наборы это
|
||||||
|
предрелизный гейт (PROCESS.md §8), а не гейт ревью.
|
||||||
|
|
||||||
|
Всегда, они дешёвые:
|
||||||
|
`npx tsc --noEmit`, `npm test`, `npm run build` со сверкой трёх
|
||||||
|
копий бандла.
|
||||||
|
|
||||||
|
По необходимости, и «необходимость» определяется diff'ом и AC:
|
||||||
|
- браузерные смоки `demo/smoke_*.mjs` — названные в AC плюс
|
||||||
|
относящиеся к тронутым поверхностям. Их 127; прогон всех уместен
|
||||||
|
только когда задача действительно задевает всё;
|
||||||
|
- `npm run golden:verify` — если diff может изменить видимый
|
||||||
|
результат: рендер, геометрия, стили, слои;
|
||||||
|
- `python -m pytest tests_backend -q` — если тронут
|
||||||
|
`custom_components/**/*.py`;
|
||||||
|
- performance-профили — если названы в AC либо тронуты
|
||||||
|
чувствительные к перфу пути.
|
||||||
|
|
||||||
|
Дисциплина «тест должен уметь падать» не отменяется, но применяется к
|
||||||
|
тем тестам, которые ты прогонял.
|
||||||
|
|
||||||
|
**В комментарии обязателен перечень: какие гейты прогнал, какие нет и
|
||||||
|
почему.** Это условие честности такого сужения: непрогнанный гейт
|
||||||
|
становится видимым решением, а не молчаливым пропуском. Раздел «чего
|
||||||
|
не проверял» в документе ревью — не формальность, а главный его
|
||||||
|
раздел на коротких задачах.
|
||||||
|
|
||||||
Ты НЕ правишь ни ТЗ, ни продуктовый код. Только оцениваешь.
|
Ты НЕ правишь ни ТЗ, ни продуктовый код. Только оцениваешь.
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,15 @@ of a status and `rejected` on a closed issue. Exactly one `S*` label per open
|
|||||||
issue. [GitHub Projects (v2)](https://github.com/users/Matysh/projects/1) is a
|
issue. [GitHub Projects (v2)](https://github.com/users/Matysh/projects/1) is a
|
||||||
human-facing view synchronised from the labels, not the source of truth.
|
human-facing view synchronised from the labels, not the source of truth.
|
||||||
|
|
||||||
|
Two shortcuts exist for small work. `small` — the light track: the spec lives in
|
||||||
|
the issue body and its review is a comment. `trivial` — the short track: no spec
|
||||||
|
stage at all, `S2-analysis` straight to `S5-ready`, with the AC written into the
|
||||||
|
issue body first. `trivial` requires a bug confined to one surface with no new UX
|
||||||
|
contract, no migration, no i18n, no perf or touch impact, at most three checkable
|
||||||
|
AC, **and expected behaviour already on record** — nothing left to decide. Code
|
||||||
|
review is never skipped on either track; it is what stands in for testing.
|
||||||
|
`PROCESS.md` §5 and §5.1 hold the criteria.
|
||||||
|
|
||||||
An issue filed by an outsider is worked exactly like one of the owner's own, once
|
An issue filed by an outsider is worked exactly like one of the owner's own, once
|
||||||
the owner has decided to take it. The check sits **at the entrance**, not on every
|
the owner has decided to take it. The check sits **at the entrance**, not on every
|
||||||
step: while an issue carries no status label it is outside the process and the
|
step: while an issue carries no status label it is outside the process and the
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ const CLASS_C = [
|
|||||||
|
|
||||||
const CHANGELOGS = ['docs/CHANGELOG.md', 'docs/CHANGELOG.ru.md'];
|
const CHANGELOGS = ['docs/CHANGELOG.md', 'docs/CHANGELOG.ru.md'];
|
||||||
|
|
||||||
|
// Метки, при которых файла ТЗ в docs/specs/ быть не должно: на лёгком треке ТЗ
|
||||||
|
// живёт в теле issue (§5), на коротком — там же, и ревью ТЗ вообще не проводится
|
||||||
|
// (§5.1, issue #128). Офлайн эти случаи неотличимы от «ТЗ не написано», поэтому
|
||||||
|
// проверка 3 краснеет только когда метки прочитаны.
|
||||||
|
export const NO_SPEC_FILE = ['small', 'trivial'];
|
||||||
|
|
||||||
export const ALLOWED_STATUS = ['S5-ready', 'S6-in-progress', 'S7-code-review', 'S8-merged'];
|
export const ALLOWED_STATUS = ['S5-ready', 'S6-in-progress', 'S7-code-review', 'S8-merged'];
|
||||||
export const STRICT_STATUS = ['S5-ready', 'S6-in-progress', 'S7-code-review'];
|
export const STRICT_STATUS = ['S5-ready', 'S6-in-progress', 'S7-code-review'];
|
||||||
|
|
||||||
@@ -235,12 +241,12 @@ export function checkSpecs(commits, specFiles, labelsOf = null) {
|
|||||||
if (labels === null) {
|
if (labels === null) {
|
||||||
out.push({
|
out.push({
|
||||||
level: 'warn', rule: 3, sha: c.short,
|
level: 'warn', rule: 3, sha: c.short,
|
||||||
msg: `класс A по ${t}, но ТЗ docs/specs/${nn}-*.md не найдено — допустимо только при метке small`,
|
msg: `класс A по ${t}, но ТЗ docs/specs/${nn}-*.md не найдено — допустимо при метке small или trivial`,
|
||||||
});
|
});
|
||||||
} else if (!labels.includes('small')) {
|
} else if (!labels.some((l) => NO_SPEC_FILE.includes(l))) {
|
||||||
out.push({
|
out.push({
|
||||||
level: 'fail', rule: 3, sha: c.short,
|
level: 'fail', rule: 3, sha: c.short,
|
||||||
msg: `класс A по ${t}: ТЗ docs/specs/${nn}-*.md нет, и метки small на issue нет — код без ТЗ`,
|
msg: `класс A по ${t}: ТЗ docs/specs/${nn}-*.md нет, и метки ${NO_SPEC_FILE.join(' / ')} на issue нет — код без ТЗ`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,8 +134,9 @@ test('a class A commit without a spec warns offline and fails with labels', () =
|
|||||||
assert.equal(offline[0].level, 'warn');
|
assert.equal(offline[0].level, 'warn');
|
||||||
assert.equal(offline[0].rule, 3);
|
assert.equal(offline[0].rule, 3);
|
||||||
|
|
||||||
// С метками: small оправдывает отсутствие файла, его отсутствие — нет.
|
// С метками: small и trivial оправдывают отсутствие файла, их отсутствие — нет.
|
||||||
assert.deepEqual(checkSpecs([c], [], () => ['small', 'S5-ready']), []);
|
assert.deepEqual(checkSpecs([c], [], () => ['small', 'S5-ready']), []);
|
||||||
|
assert.deepEqual(checkSpecs([c], [], () => ['trivial', 'S5-ready']), []);
|
||||||
const strict = checkSpecs([c], [], () => ['S5-ready']);
|
const strict = checkSpecs([c], [], () => ['S5-ready']);
|
||||||
assert.equal(strict.length, 1);
|
assert.equal(strict.length, 1);
|
||||||
assert.equal(strict[0].level, 'fail');
|
assert.equal(strict[0].level, 'fail');
|
||||||
|
|||||||
Reference in New Issue
Block a user