Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11a8b623a2 | ||
|
|
43459bcbe0 | ||
|
|
a55f564498 | ||
|
|
4168b1cfce | ||
|
|
8f13325c52 | ||
|
|
180239f44c | ||
|
|
b9fe916662 | ||
|
|
dc0a5ccc5a | ||
|
|
2452c51d00 | ||
|
|
230813b355 | ||
|
|
7372e977b8 | ||
|
|
f68878cc44 | ||
|
|
b7079ead80 | ||
|
|
255330b9bc | ||
|
|
dc6018ed5a | ||
|
|
b80193f726 | ||
|
|
b6bae3dba6 | ||
|
|
552504b834 | ||
|
|
e6987459a5 | ||
|
|
2529052749 | ||
|
|
d5df838976 | ||
|
|
e095c710f9 | ||
|
|
a32744255f | ||
|
|
054a713977 | ||
|
|
f86bc34dd5 | ||
|
|
774b7c7e04 | ||
|
|
e52afe6349 | ||
|
|
0ba81a994b | ||
|
|
d35a52a3f1 | ||
|
|
f0ba7c5da9 | ||
|
|
632d6a2f8e | ||
|
|
27d78801d0 | ||
|
|
02b374f504 | ||
|
|
1ba4c23a58 | ||
|
|
b4d6effbca | ||
|
|
f293c6e9dc | ||
|
|
449438fb20 | ||
|
|
a8437e69e5 | ||
|
|
23e55d4929 | ||
|
|
0a95a1cd65 | ||
|
|
62d0f50593 | ||
|
|
27ba3f2ee0 | ||
|
|
15e047949e | ||
|
|
744b4a8398 | ||
|
|
d90725815a | ||
|
|
a977f2c23e | ||
|
|
fa319e37b3 | ||
|
|
035399e7a6 | ||
|
|
59f0e49c6e | ||
|
|
fa91f3a731 | ||
|
|
524908b23b | ||
|
|
2bf929e34e | ||
|
|
a72989c4bb | ||
|
|
93bacc9696 | ||
|
|
7890005a92 | ||
|
|
d17bcc05fb | ||
|
|
2b3959e55b | ||
|
|
43b52826e4 | ||
|
|
9f3511f2dc | ||
|
|
3fafd9f2b6 | ||
|
|
857369b18b | ||
|
|
f8e089bdf2 | ||
|
|
b921ba7184 | ||
|
|
4fea0d2664 | ||
|
|
70fe860bac | ||
|
|
2eb9fdbf68 | ||
|
|
82a48ee0ce | ||
|
|
0f234f769d | ||
|
|
c70aab8bbf | ||
|
|
b7747bc7d8 | ||
|
|
345e613a4a | ||
|
|
66ae952c2a | ||
|
|
79ee16546d | ||
|
|
baf283c50f | ||
|
|
72acafd7eb | ||
|
|
92b83d9525 | ||
|
|
ee4bbf8f1f | ||
|
|
44ee23ee33 | ||
|
|
7bb55c2a41 | ||
|
|
c1cc94f0e0 | ||
|
|
d071ac2e14 | ||
|
|
040ad3dc5d | ||
|
|
22c7d6340a | ||
|
|
14396ed0b1 | ||
|
|
b666ac8985 | ||
|
|
1fbf7704ca | ||
|
|
c80f478df9 | ||
|
|
10d32312b5 | ||
|
|
c8f9b5d3b4 | ||
|
|
7dc7597260 | ||
|
|
fe92ce067a | ||
|
|
1d151b5565 | ||
|
|
af62bd4c64 | ||
|
|
4de5c0c0b5 | ||
|
|
067b944337 | ||
|
|
1514899746 | ||
|
|
0ca56b4450 | ||
|
|
51e1643762 | ||
|
|
913420df68 | ||
|
|
1babdfb558 | ||
|
|
4a5bf2290a | ||
|
|
9d26ffe657 | ||
|
|
adb1727c50 | ||
|
|
0f97e3e639 | ||
|
|
9d1d27325d | ||
|
|
32552224ea | ||
|
|
b0c4e7c32e | ||
|
|
47469bab22 | ||
|
|
14e56c0399 | ||
|
|
2b63dd8548 | ||
|
|
bf3e8a9b3a | ||
|
|
ee6ca4f3c9 | ||
|
|
284644acc8 | ||
|
|
6a905d9ac8 | ||
|
|
08ff4fbefd | ||
|
|
3c860d8159 | ||
|
|
959dda9e48 | ||
|
|
de8482fde0 | ||
|
|
5315ad297d | ||
|
|
77bf22f111 | ||
|
|
007efaee28 | ||
|
|
37879e0c82 | ||
|
|
fe790ca01f | ||
|
|
b38bd0c8b5 | ||
|
|
c9f8b50cd6 | ||
|
|
49bae62e9a | ||
|
|
f855cee891 | ||
|
|
d9bd5a446c | ||
|
|
8c61fe79dc | ||
|
|
a50cbd8f91 | ||
|
|
f234855e66 | ||
|
|
a2d6371545 | ||
|
|
86c765d99e | ||
|
|
bc6b243f56 | ||
|
|
1a6b12af86 | ||
|
|
5ac596f93c | ||
|
|
9beb6d45a0 | ||
|
|
53b20e8c45 | ||
|
|
f10dd1ebba | ||
|
|
15bc6bcf5a | ||
|
|
4051899b9b | ||
|
|
965bbb05a8 | ||
|
|
efa2a50665 | ||
|
|
5a38bea3ff | ||
|
|
0672c2b2ff | ||
|
|
50e67c0988 | ||
|
|
b1f1f6b754 | ||
|
|
a3397329d5 | ||
|
|
368ea35f7f | ||
|
|
9cf848c143 | ||
|
|
4366a1c796 | ||
|
|
44aa358060 | ||
|
|
5df8cfc0c1 | ||
|
|
46edcb1f37 | ||
|
|
0b55163dc3 | ||
|
|
2cfb6d2169 | ||
|
|
78698a075a | ||
|
|
6647ef5677 | ||
|
|
e08a7b4886 | ||
|
|
1dba818581 | ||
|
|
fb615f9d5d | ||
|
|
ee0f97c85c | ||
|
|
351fef43d6 | ||
|
|
e29dfdeb72 | ||
|
|
edfe8af52a | ||
|
|
0d969a5348 | ||
|
|
8a4ecdea63 | ||
|
|
551ff2f563 | ||
|
|
0b29ccb91d | ||
|
|
0d047450ce | ||
|
|
0e0a0e54e5 | ||
|
|
e95936cdce | ||
|
|
59fdae817a | ||
|
|
d36b7f80c3 | ||
|
|
1241b9499c | ||
|
|
9f82960c2c | ||
|
|
2a21ff65d4 | ||
|
|
37aed2c9b1 | ||
|
|
61292c9ea0 | ||
|
|
e8e5c863e4 | ||
|
|
52a8f58909 | ||
|
|
4b27268e2f | ||
|
|
4c9e013276 | ||
|
|
6569dced23 | ||
|
|
84cffb3f92 | ||
|
|
fc70a3fcf4 | ||
|
|
4382817e55 | ||
|
|
9bbf6fb248 | ||
|
|
d70b67abc4 | ||
|
|
61d51dd934 | ||
|
|
d7a1156233 | ||
|
|
431586b2c6 | ||
|
|
4c26b16b03 | ||
|
|
8d857b22f4 | ||
|
|
74fcff558c | ||
|
|
6d6d6a1317 | ||
|
|
748ebe86b9 | ||
|
|
20457660fa | ||
|
|
bb1e399d2e | ||
|
|
245189d23d | ||
|
|
cc2300bf86 | ||
|
|
0d30dde17e | ||
|
|
2f1876a405 | ||
|
|
551d4782fe | ||
|
|
08a56953cc | ||
|
|
82f99e3109 | ||
|
|
c2fd318850 | ||
|
|
ee3d78052a | ||
|
|
813554253a | ||
|
|
0a3e0674cc | ||
|
|
b63373fb9c | ||
|
|
8cd04d4cbe | ||
|
|
46823f90c2 | ||
|
|
e1fa2591a4 | ||
|
|
8ca877c9f7 | ||
|
|
10ebdc9792 | ||
|
|
c69d73f1f2 | ||
|
|
746147d0e0 | ||
|
|
9ba034a32f | ||
|
|
a466756247 | ||
|
|
3701c7bd20 | ||
|
|
a551af9219 | ||
|
|
57572d8c8c |
@@ -30,20 +30,14 @@ else
|
||||
fi
|
||||
|
||||
status=0
|
||||
gate_status=0
|
||||
|
||||
# Локальный набор гейтов (#343). Выключен по умолчанию намеренно: 20-45 секунд на
|
||||
# каждый пуш, включая пуши одной строки документации, — цена, которую стоит
|
||||
# платить осознанно. Документация: docs/TESTING.md.
|
||||
if [ "${HP_PREPUSH_GATE:-}" = "1" ] && [ -f "$repo_root/scripts/pre-push-gate.mjs" ]; then
|
||||
echo "pre-push-gate: HP_PREPUSH_GATE=1, прогоняю локальный набор" >&2
|
||||
if ! node "$repo_root/scripts/pre-push-gate.mjs" >&2; then
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
# Строки git читаются один раз: их нужно и локальному набору, и процессному гейту.
|
||||
refs=$(cat)
|
||||
|
||||
while read -r local_ref local_sha remote_ref remote_sha; do
|
||||
# Deleting a remote branch pushes nothing to examine.
|
||||
if [ "$local_sha" = "$zero" ]; then
|
||||
# An empty line (nothing on stdin) and deleting a remote branch push nothing to examine.
|
||||
if [ -z "$local_sha" ] || [ "$local_sha" = "$zero" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -71,10 +65,25 @@ while read -r local_ref local_sha remote_ref remote_sha; do
|
||||
if ! node "$gate" --range "${base}..${local_sha}" --target-ref "$remote_ref" $issues_flag >&2; then
|
||||
status=1
|
||||
fi
|
||||
done
|
||||
done <<EOF
|
||||
$refs
|
||||
EOF
|
||||
|
||||
# Локальный набор gate:small (#633, прежде opt-in #343) — после процессного
|
||||
# гейта: тот отвечает за секунды, а набор идёт минуты; прогоняются оба, чтобы
|
||||
# автор узнал обо всех провалах одним кругом. По умолчанию включён для
|
||||
# веток issue/* с исполняемым диффом; дифф только класса C/D (документы ревью,
|
||||
# changelog, бандл) и ветки вне issue/* его не запускают. Выключение — явное:
|
||||
# HP_PREPUSH_GATE=0; HP_PREPUSH_GATE=1 — прогнать для любой ветки. Документация:
|
||||
# docs/TESTING.md, docs/DEVELOPMENT.md «Локальный контур за 5 минут».
|
||||
if [ -f "$repo_root/scripts/pre-push-gate.mjs" ]; then
|
||||
if ! printf '%s\n' "$refs" | node "$repo_root/scripts/pre-push-gate.mjs" --hook >&2; then
|
||||
gate_status=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$status" -ne 0 ]; then
|
||||
cat >&2 <<'EOF'
|
||||
cat >&2 <<'MSG'
|
||||
|
||||
Push остановлен: нарушен процесс (PROCESS.md §10.2).
|
||||
|
||||
@@ -84,7 +93,12 @@ Push остановлен: нарушен процесс (PROCESS.md §10.2).
|
||||
|
||||
Обойти проверку можно через `git push --no-verify`, и тогда то же самое найдёт
|
||||
job `process-gate` в Validate — уже после того, как код окажется в dev.
|
||||
EOF
|
||||
MSG
|
||||
fi
|
||||
|
||||
if [ "$gate_status" -ne 0 ]; then
|
||||
echo "Push остановлен: красный локальный набор gate:small (см. вывод выше)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit "$status"
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
name: "Мутационный гейт · тело (#623)"
|
||||
|
||||
# Реестр известных поломок (issue #85): каждый мутант ломает продуктовый код
|
||||
# известным способом, и объявленный тест ОБЯЗАН на этом покраснеть. Тест,
|
||||
# оставшийся зелёным на сломанном коде, ничего не защищает — он лишь выглядит
|
||||
# защитой, и это хуже его отсутствия.
|
||||
#
|
||||
# Прогон дорогой и проверяет не продукт, а тесты, поэтому он не входит ни в
|
||||
# Validate, ни в цикл разработки, ни в релизный гейт (#513, решение владельца
|
||||
# 09.09): каждую ночь по расписанию, отказ — issue с отчётом (#472).
|
||||
# Мутанты, задетые диффом, конвейер ревью и слияние гоняют отдельно на своём
|
||||
# кандидате (#510); кандидат беты, `full=true` и ночной Validate их не
|
||||
# запрашивают (#601) — ночью достаточно этого полного реестра.
|
||||
# Дешёвая половина — «якоря патчей живы, guard-файлы существуют» — идёт с
|
||||
# обычными юнитами: test/mutation-gate.test.mjs.
|
||||
#
|
||||
# #332: бандл собирается только мутантам с браузерным гвардом (guardNeedsBundle),
|
||||
# компиляция тестов в worktree стартует с тёплого test-build (инкрементальный
|
||||
# tsc), а реестр режется на четыре чересполосных шарда — полный прогон
|
||||
# укладывается в десятки минут вместо часов. Локальный дифф-режим:
|
||||
# node scripts/mutation-gate.mjs --changed origin/dev..HEAD
|
||||
#
|
||||
# #620: ночь по расписанию на дереве, уже доказанном зелёным полным прогоном
|
||||
# (тот же tree материала, тот же SHA workflow, маркер не старше недели), шарды не
|
||||
# гоняет — в сводке «reused from run N». Маркер пишет только зелёный агрегатор,
|
||||
# поэтому красный не переносится: следующая ночь гонит реестр заново и снова
|
||||
# заводит issue (#472). Ручной dispatch гонит полный реестр всегда. Решение —
|
||||
# чистая функция scripts/mutation-nightly-reuse.mjs.
|
||||
#
|
||||
# #623: «SHA workflow» — `job.workflow_sha`, SHA этого файла-тела. В вызываемом
|
||||
# workflow `github.workflow_sha` принадлежит вызывающему `mutation-gate.yml` из
|
||||
# main и не меняется вместе с телом, поэтому здесь он не используется.
|
||||
|
||||
on:
|
||||
# #623: тело вызывается тонким файлом `mutation-gate.yml` из ветки по умолчанию
|
||||
# по ссылке `@dev`; триггеры, run-name и concurrency живут там.
|
||||
workflow_call:
|
||||
inputs:
|
||||
ref:
|
||||
description: "Git ref whose mutation guards must be proved"
|
||||
required: false
|
||||
type: string
|
||||
default: dev
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Concurrency уровня workflow — у вызывающего `mutation-gate.yml` (#623).
|
||||
|
||||
jobs:
|
||||
# #549: moving ref разрешается ровно один раз. Все шарды ниже получают один
|
||||
# commit/tree, а не самостоятельно читают dev в разное время.
|
||||
material:
|
||||
name: "Зафиксировать неизменяемый материал"
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
sha: ${{ steps.identity.outputs.sha }}
|
||||
tree: ${{ steps.identity.outputs.tree }}
|
||||
ref: ${{ steps.identity.outputs.ref }}
|
||||
reuse: ${{ steps.reuse.outputs.reuse }}
|
||||
reused_run: ${{ steps.reuse.outputs.reused_run }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || 'dev' }}
|
||||
fetch-depth: 0
|
||||
- name: Зафиксировать commit и tree
|
||||
id: identity
|
||||
run: |
|
||||
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
echo "tree=$(git rev-parse 'HEAD^{tree}')" >> "$GITHUB_OUTPUT"
|
||||
echo "ref=${{ github.event_name == 'workflow_dispatch' && inputs.ref || 'dev' }}" >> "$GITHUB_OUTPUT"
|
||||
# #620: самый свежий маркер зелёного прогона этого tree и этого workflow.
|
||||
# Ключ уникален на прогон (кэш неизменяем), восстанавливается по префиксу —
|
||||
# иначе перепроверка после недели не смогла бы освежить маркер.
|
||||
- name: Маркер зелёного прогона этого дерева
|
||||
if: github.event_name == 'schedule'
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
|
||||
with:
|
||||
path: artifacts/mutation-green
|
||||
key: mutation-green-v1-${{ steps.identity.outputs.tree }}-${{ job.workflow_sha }}-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
mutation-green-v1-${{ steps.identity.outputs.tree }}-${{ job.workflow_sha }}-
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
# Любой сбой решения — полный прогон: пропуск должен быть доказан, а не
|
||||
# выведен из ошибки. Материал без скрипта (dispatch старого ref) — тоже.
|
||||
- name: Нужен ли прогон
|
||||
id: reuse
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
TREE: ${{ steps.identity.outputs.tree }}
|
||||
WORKFLOW_SHA: ${{ job.workflow_sha }}
|
||||
RUN_URL_BASE: ${{ github.server_url }}/${{ github.repository }}/actions/runs
|
||||
run: |
|
||||
decision="$RUNNER_TEMP/mutation-reuse.txt"
|
||||
if [ -f scripts/mutation-nightly-reuse.mjs ] && node scripts/mutation-nightly-reuse.mjs --decide \
|
||||
--event="$EVENT_NAME" --tree="$TREE" --workflow-sha="$WORKFLOW_SHA" \
|
||||
--marker=artifacts/mutation-green/marker.json \
|
||||
--summary="$GITHUB_STEP_SUMMARY" --run-url-base="$RUN_URL_BASE" > "$decision"; then
|
||||
cat "$decision" >> "$GITHUB_OUTPUT"
|
||||
if grep -qx 'reuse=true' "$decision"; then
|
||||
echo "::notice::reused from run $(sed -n 's/^reused_run=//p' "$decision")"
|
||||
fi
|
||||
else
|
||||
echo "::warning::решение о повторном использовании не получено — полный прогон"
|
||||
echo "reuse=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
mutants:
|
||||
name: "Мутанты: каждый обязан красить тесты (шард ${{ matrix.shard }} из 6)"
|
||||
needs: material
|
||||
if: needs.material.outputs.reuse != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4, 5, 6]
|
||||
# Шесть чересполосных шардов (#604): при четырёх шард нёс ~203 мутанта из
|
||||
# 810 и рос с реестром — 42 мин 10.09, 57 мин 20.09, 61 мин 21.09, и шард
|
||||
# 2/4 был снят по потолку без единого FAIL. Делитель тот же, что у
|
||||
# `changed_mutants` в Validate; число шардов повторяется в `--shard=i/6`,
|
||||
# `--shards=6` и имени job — тест `mutation-gate.test.mjs` держит их
|
||||
# равными. Бандл собирают только браузерные гварды. Час — потолок против
|
||||
# зависшего Chromium, а не бюджет шарда: шард, упёршийся в него, — сигнал
|
||||
# снова делить, и отчёт (#472) называет такой шард прерванным, не «ok».
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ needs.material.outputs.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
|
||||
- run: npm ci
|
||||
|
||||
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
||||
with:
|
||||
python-version: '3.14'
|
||||
|
||||
- name: Установить backend test dependencies
|
||||
run: |
|
||||
# Точные версии — в tests_backend/requirements.txt (#392): без них
|
||||
# резолвер выбирает набор сам, и «зелёный backend» значит разное
|
||||
# в разные дни.
|
||||
pip install -r tests_backend/requirements.txt
|
||||
pip list --format=columns | grep -Ei 'homeassistant|voluptuous|^pytest '
|
||||
|
||||
- name: Кэш браузеров Playwright
|
||||
id: pw
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
|
||||
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: Реестр применим к текущему коду
|
||||
run: node scripts/mutation-gate.mjs --check
|
||||
|
||||
- name: Тёплый test-build для инкрементальной компиляции мутантов
|
||||
run: npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs
|
||||
|
||||
# Вывод шарда сохраняется артефактом (#472): строки
|
||||
# `FAIL <id>: тест остался зелёным…` — единственное место, где названо,
|
||||
# ЧТО сбежало. Без артефакта отказ безымянный. `PIPESTATUS` — чтобы
|
||||
# `tee` не съел код выхода раннера.
|
||||
- name: Каждый тест ловит свою поломку
|
||||
id: gate
|
||||
run: |
|
||||
mkdir -p artifacts/mutation-shard-${{ matrix.shard }}
|
||||
set -o pipefail
|
||||
node scripts/mutation-gate.mjs --shard=${{ matrix.shard }}/6 2>&1 | tee artifacts/mutation-shard-${{ matrix.shard }}/mutation-shard-${{ matrix.shard }}.log
|
||||
# Исход шага едет в evidence (#604): снятый по timeout-minutes шаг даёт
|
||||
# `cancelled`, и агрегатор отвергает такой шард как неполный — лог без
|
||||
# строк FAIL сам по себе зелёным не считается.
|
||||
- name: Записать identity шарда
|
||||
if: always()
|
||||
run: |
|
||||
node scripts/mutation-gate-report.mjs \
|
||||
--write-evidence=artifacts/mutation-shard-${{ matrix.shard }}/evidence.json \
|
||||
--sha=${{ needs.material.outputs.sha }} \
|
||||
--tree=${{ needs.material.outputs.tree }} \
|
||||
--workflow-sha=${{ job.workflow_sha }} \
|
||||
--run-id=${{ github.run_id }} --run-attempt=${{ github.run_attempt }} \
|
||||
--shard=${{ matrix.shard }} --shards=6 \
|
||||
--outcome=${{ steps.gate.outcome }}
|
||||
- name: Сохранить лог и identity шарда
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: mutation-shard-${{ matrix.shard }}-attempt-${{ github.run_attempt }}
|
||||
path: artifacts/mutation-shard-${{ matrix.shard }}
|
||||
if-no-files-found: warn
|
||||
retention-days: 30
|
||||
|
||||
# Результат нельзя приписывать material, пока не доказаны все шесть шардов —
|
||||
# каждый с identity и с дошедшим до конца прогоном (#549, #604).
|
||||
# always() нужен при красном мутанте: лог красного шарда всё равно evidence.
|
||||
evidence:
|
||||
name: "Доказать единый material всех шардов"
|
||||
needs: [material, mutants]
|
||||
if: always() && needs.material.outputs.reuse != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
# CLI отчётчика принадлежит зафиксированному material. В main лежит
|
||||
# только workflow и может не быть dev-версии scripts/**.
|
||||
ref: ${{ needs.material.outputs.sha }}
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Забрать evidence всех попыток
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
with:
|
||||
pattern: mutation-shard-*
|
||||
path: artifacts/mutation-logs
|
||||
- name: Проверить полноту и identity
|
||||
run: |
|
||||
node scripts/mutation-gate-report.mjs --verify-only \
|
||||
--logs=artifacts/mutation-logs --shards=6 \
|
||||
--sha=${{ needs.material.outputs.sha }} \
|
||||
--tree=${{ needs.material.outputs.tree }} \
|
||||
--workflow-sha=${{ job.workflow_sha }} \
|
||||
--run-id=${{ github.run_id }} --run-attempt=${{ github.run_attempt }}
|
||||
|
||||
# #620: маркер пишется ТОЛЬКО после зелёного агрегатора — все шесть шардов
|
||||
# доказаны на одном material. Красный или неполный прогон маркера не оставляет,
|
||||
# и следующая ночь гонит реестр заново.
|
||||
green_marker:
|
||||
name: "Записать маркер зелёного прогона"
|
||||
needs: [material, mutants, evidence]
|
||||
if: needs.material.outputs.reuse != 'true' && needs.mutants.result == 'success' && needs.evidence.result == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ needs.material.outputs.sha }}
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Маркер
|
||||
run: |
|
||||
node scripts/mutation-nightly-reuse.mjs \
|
||||
--write-marker=artifacts/mutation-green/marker.json \
|
||||
--tree=${{ needs.material.outputs.tree }} \
|
||||
--sha=${{ needs.material.outputs.sha }} \
|
||||
--workflow-sha=${{ job.workflow_sha }} \
|
||||
--run-id=${{ github.run_id }} --run-attempt=${{ github.run_attempt }} \
|
||||
--event=${{ github.event_name }}
|
||||
- name: Сохранить маркер
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
|
||||
with:
|
||||
path: artifacts/mutation-green
|
||||
key: mutation-green-v1-${{ needs.material.outputs.tree }}-${{ job.workflow_sha }}-${{ github.run_id }}
|
||||
|
||||
# Адресат у отказа (#472). Только по расписанию: ручной dispatch остаётся
|
||||
# для отладки самого гейта, его результат смотрят в прогоне — issue на
|
||||
# каждый такой отказ был бы шумом, который снова перестанут читать.
|
||||
#
|
||||
# Права job-уровня ЗАМЕНЯЮТ права workflow, а не дополняют (прецедент —
|
||||
# validate.yml, job с actions: read): перечислены все три.
|
||||
report:
|
||||
name: "Отказ расписания: issue и Telegram"
|
||||
needs: [material, mutants, evidence]
|
||||
# #620: ночь, принявшая доказательство прошлого зелёного прогона, отказом не
|
||||
# является — её шарды пропущены намеренно. Любой другой пропуск шардов
|
||||
# (упал material) по-прежнему заводит issue.
|
||||
if: always() && github.event_name == 'schedule' && needs.material.outputs.reuse != 'true' && (needs.mutants.result != 'success' || needs.evidence.result != 'success')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
# Не перечитываем moving dev: код отчётчика берётся из уже
|
||||
# зафиксированного material. В стабильном main dev-скриптов нет.
|
||||
ref: ${{ needs.material.outputs.sha }}
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Забрать логи шардов
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
with:
|
||||
pattern: mutation-shard-*
|
||||
path: artifacts/mutation-logs
|
||||
- name: Собрать отчёт
|
||||
id: report
|
||||
env:
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
run: |
|
||||
mkdir -p artifacts
|
||||
node scripts/mutation-gate-report.mjs \
|
||||
--require-evidence --logs=artifacts/mutation-logs --shards=6 \
|
||||
--sha=${{ needs.material.outputs.sha }} \
|
||||
--tree=${{ needs.material.outputs.tree }} \
|
||||
--workflow-sha=${{ job.workflow_sha }} \
|
||||
--run-id=${{ github.run_id }} --run-attempt=${{ github.run_attempt }} \
|
||||
--run-url="$RUN_URL" --ref=${{ needs.material.outputs.ref }} \
|
||||
--body-out=artifacts/mutation-report.md \
|
||||
--telegram-out=artifacts/mutation-telegram.txt >> "$GITHUB_OUTPUT"
|
||||
# Одно issue, а не одно на неделю: открытое с тем же маркером в заголовке
|
||||
# получает комментарий, новое заводится только если открытого нет.
|
||||
- name: Issue — создать или дописать
|
||||
id: issue
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
MARKER: ${{ steps.report.outputs.marker }}
|
||||
TITLE: ${{ steps.report.outputs.title }}
|
||||
run: |
|
||||
existing=$(gh issue list --repo "$REPO" --state open --search "\"$MARKER\" in:title" \
|
||||
--json number,title --jq '[.[] | select(.title | startswith(env.MARKER))][0].number // empty')
|
||||
if [ -n "$existing" ]; then
|
||||
gh issue comment "$existing" --repo "$REPO" --body-file artifacts/mutation-report.md
|
||||
url="${{ github.server_url }}/$REPO/issues/$existing"
|
||||
else
|
||||
url=$(gh issue create --repo "$REPO" --title "$TITLE" \
|
||||
--label infra --label process --label tests \
|
||||
--body-file artifacts/mutation-report.md)
|
||||
fi
|
||||
echo "url=$url" >> "$GITHUB_OUTPUT"
|
||||
echo "issue: $url"
|
||||
# Тот же канал, что у релизов (announce.yml). Нет секретов — не отказ:
|
||||
# issue уже заведено, а Telegram — второй адресат, не единственный.
|
||||
- name: Telegram
|
||||
if: always() && steps.issue.outcome == 'success'
|
||||
env:
|
||||
TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
CHAT: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
ISSUE_URL: ${{ steps.issue.outputs.url }}
|
||||
run: |
|
||||
if [ -z "$TOKEN" ] || [ -z "$CHAT" ]; then
|
||||
echo "::warning::TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID не заданы — оповещение пропущено, issue заведено"
|
||||
exit 0
|
||||
fi
|
||||
TEXT=$(sed "s|(issue)|$ISSUE_URL|" artifacts/mutation-telegram.txt)
|
||||
curl -sS --fail-with-body -X POST \
|
||||
"https://api.telegram.org/bot$TOKEN/sendMessage" \
|
||||
--data-urlencode "chat_id=$CHAT" \
|
||||
--data-urlencode "text=$TEXT" \
|
||||
-d disable_web_page_preview=true
|
||||
@@ -0,0 +1,64 @@
|
||||
# Ночной полный прогон (#479).
|
||||
#
|
||||
# Тяжёлые job Validate — смоки, golden, performance_smoke — на обычном пуше не
|
||||
# идут: они ни разу не ловили дефект в момент ревью и стоили ~6 минут
|
||||
# критического пути на каждую итерацию. Полный набор идёт на кандидате беты
|
||||
# (трейлер `Release:`), по кнопке и здесь — каждую ночь на голове `dev`.
|
||||
#
|
||||
# Почему не `schedule` прямо в validate.yml: расписание исполняется на ветке
|
||||
# по умолчанию (`main`), а проверять надо `dev`. Один dispatch с `--ref dev`
|
||||
# делает это без переписывания checkout во всех job. Reuse (#208) сохраняется:
|
||||
# при неизменённом дереве ночной прогон обойдётся маркерами.
|
||||
#
|
||||
# Красный ночной прогон — сигнал автору последних коммитов на dev, не гейт:
|
||||
# гейт беты по-прежнему требует зелёный Validate на точном SHA кандидата, и
|
||||
# там полный набор идёт заново.
|
||||
#
|
||||
# Сигнал обязан быть настоящим (#492 §7): до этой задачи job завершалась
|
||||
# успехом в момент постановки Validate в очередь, и красный полный прогон не
|
||||
# делал ночной workflow красным. Теперь job находит запущенный прогон и ждёт
|
||||
# его: успешный dispatch — не успешная проверка.
|
||||
name: "Ночной полный прогон dev · тело (#623)"
|
||||
|
||||
on:
|
||||
# #623: тело вызывается тонким файлом `nightly.yml` из ветки по умолчанию
|
||||
# по ссылке `@dev`; триггеры, run-name и concurrency живут там.
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dispatch:
|
||||
name: "Запустить Validate на dev с полным набором и дождаться результата"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
since=$(date -u +%FT%TZ)
|
||||
gh workflow run validate.yml --repo "$REPO" --ref dev -f full=true
|
||||
echo "Validate(dev, full=true) поставлен в очередь: $since"
|
||||
# Найти именно этот прогон: workflow_dispatch на dev, созданный не
|
||||
# раньше момента запуска. До трёх минут на появление в списке.
|
||||
run_id=""
|
||||
for _ in $(seq 1 18); do
|
||||
sleep 10
|
||||
run_id=$(gh run list --repo "$REPO" --workflow validate.yml --branch dev \
|
||||
--event workflow_dispatch --json databaseId,createdAt --limit 5 \
|
||||
--jq "[.[] | select(.createdAt >= \"$since\")] | sort_by(.createdAt) | last | .databaseId // empty")
|
||||
[ -n "$run_id" ] && break
|
||||
done
|
||||
if [ -z "$run_id" ]; then
|
||||
echo "::error::прогон Validate не появился за 3 минуты — dispatch не равен проверке"
|
||||
exit 1
|
||||
fi
|
||||
url="${{ github.server_url }}/$REPO/actions/runs/$run_id"
|
||||
echo "дочерний прогон: $url"
|
||||
echo "- Validate(dev, full=true): $url" >> "$GITHUB_STEP_SUMMARY"
|
||||
# Ждём завершения; красный дочерний прогон — красный ночной.
|
||||
gh run watch "$run_id" --repo "$REPO" --exit-status --interval 30
|
||||
@@ -0,0 +1,59 @@
|
||||
name: "Метрики процесса · тело (#623)"
|
||||
|
||||
# #637: еженедельный замер вместо ощущений — lead time S→S7→S8, раунды ревью,
|
||||
# прогоны Validate по исходам, минуты конвейера в S4/S7. Только чтение: отчёт
|
||||
# идёт в step summary и артефакт; в issue и репозиторий ничего не пишется.
|
||||
# Цифры аудита 22.09 (1,57 раунда код-ревью, S7 ≈ 36 мин/раунд, 226 Validate
|
||||
# за неделю) были собраны руками за час — теперь они стоят один запуск.
|
||||
|
||||
on:
|
||||
# #623: тело вызывается тонким файлом `process-metrics.yml` из ветки по умолчанию
|
||||
# по ссылке `@dev`; триггеры, run-name и concurrency живут там.
|
||||
workflow_call:
|
||||
inputs:
|
||||
days:
|
||||
description: "Окно в днях"
|
||||
required: false
|
||||
type: string
|
||||
default: "7"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
issues: read
|
||||
|
||||
jobs:
|
||||
metrics:
|
||||
name: "Снимок недели: issue, раунды, прогоны"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
# Код — из dev, как у reconcile: расписание читается из main, а исполняется
|
||||
# версия, которую проверил CI.
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: dev
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Собрать отчёт
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.HP_PROCESS_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
DAYS: ${{ inputs.days || '7' }}
|
||||
run: |
|
||||
mkdir -p artifacts/process-metrics
|
||||
node scripts/process-metrics.mjs --repo="$REPO" --days="$DAYS" \
|
||||
--output=artifacts/process-metrics/report.md \
|
||||
--json=artifacts/process-metrics/report.json > /dev/null
|
||||
cat artifacts/process-metrics/report.md >> "$GITHUB_STEP_SUMMARY"
|
||||
- name: Сохранить отчёт
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: process-metrics-${{ github.run_id }}
|
||||
path: artifacts/process-metrics
|
||||
if-no-files-found: error
|
||||
retention-days: 90
|
||||
@@ -0,0 +1,57 @@
|
||||
name: "Сверка очереди ревью · тело (#623)"
|
||||
|
||||
on:
|
||||
# #623: тело вызывается тонким файлом `process-reconcile.yml` из ветки по умолчанию
|
||||
# по ссылке `@dev`; триггеры, run-name и concurrency живут там.
|
||||
workflow_call:
|
||||
inputs:
|
||||
apply:
|
||||
description: "Повторно будить потерянные запросы и публиковать диагностику"
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
issues: read
|
||||
|
||||
jobs:
|
||||
reconcile:
|
||||
name: "Один снимок S4/S7 без polling модели"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
concurrency:
|
||||
group: process-reconcile
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
# Расписание читается из main, а исполняемый reconciler — из dev: так
|
||||
# после штатного merge действует та же версия кода, которую проверил CI.
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: dev
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Сопоставить labels, requests, runs и sealed evidence
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.HP_PROCESS_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
APPLY: ${{ github.event_name == 'schedule' || inputs.apply == true }}
|
||||
run: |
|
||||
mkdir -p artifacts/process-reconcile
|
||||
node scripts/process-reconcile.mjs \
|
||||
--repo "$REPO" \
|
||||
--apply="$APPLY" \
|
||||
--max-actions=5 \
|
||||
--output=artifacts/process-reconcile/summary.json
|
||||
- name: Опубликовать компактный machine-readable итог
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: process-reconcile-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: artifacts/process-reconcile/summary.json
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
@@ -0,0 +1,60 @@
|
||||
name: "Продолжение ревью после Validate · тело (#623)"
|
||||
|
||||
# #636. Стадия `prepare` конвейера (process.yml) больше не ждёт Validate с
|
||||
# мутантами на материале внутри job — раннер спал ≈ 28 минут на раунд при
|
||||
# 10–12 минутах работы модели. Она диспатчит прогон, кладёт запечатанный
|
||||
# маркер `review-pending-…` и выходит. Этот workflow просыпается на завершение
|
||||
# любого Validate и, если раунд ждал именно этот прогон (маркер на материале,
|
||||
# метка S7 стоит, активного прогона конвейера нет), переставляет метку S7 —
|
||||
# новый прогон `prepare` находит завершённый dispatch и продолжает раунд.
|
||||
# Ничего не оценивает: зелёный/красный разбирает сам конвейер. Страховка на
|
||||
# потерянное событие — process-reconcile.yml с тем же маркером.
|
||||
#
|
||||
# Для события `workflow_run` GitHub берёт workflow только из ветки по
|
||||
# умолчанию (main). Там лежит тонкий `process-resume.yml`, который вызывает
|
||||
# этот файл по ссылке `@dev` (#623); сверка тонких копий — в preflight
|
||||
# validate.yml.
|
||||
|
||||
on:
|
||||
# #623: тело вызывается тонким файлом `process-resume.yml` из ветки по умолчанию
|
||||
# по ссылке `@dev`; триггеры, run-name и concurrency живут там.
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
resume:
|
||||
name: "Разбудить раунд, ждавший этот Validate"
|
||||
if: github.event.workflow_run.event == 'workflow_dispatch' && startsWith(github.event.workflow_run.head_branch, 'issue/')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
concurrency:
|
||||
group: process-resume-${{ github.event.workflow_run.head_branch }}
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
# Код берётся из dev, как у reconcile: после штатного слияния действует
|
||||
# версия, которую проверил CI, а не копия из main.
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: dev
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
# Метка переставляется HP_PROCESS_TOKEN: событие от GITHUB_TOKEN не
|
||||
# запустило бы process.yml (см. шапку process.yml, п. 1).
|
||||
- name: Решить по маркеру ожидания и переставить S7
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.HP_PROCESS_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||
SHA: ${{ github.event.workflow_run.head_sha }}
|
||||
EVENT: ${{ github.event.workflow_run.event }}
|
||||
STATUS: ${{ github.event.workflow_run.status }}
|
||||
run: |
|
||||
node scripts/process-resume.mjs \
|
||||
--repo="$REPO" --branch="$BRANCH" --sha="$SHA" \
|
||||
--event="$EVENT" --status="$STATUS" --apply=true | tee -a "$GITHUB_STEP_SUMMARY"
|
||||
@@ -1,22 +1,12 @@
|
||||
name: Мутационный гейт
|
||||
|
||||
# Реестр известных поломок (issue #85): каждый мутант ломает продуктовый код
|
||||
# известным способом, и объявленный тест ОБЯЗАН на этом покраснеть. Тест,
|
||||
# оставшийся зелёным на сломанном коде, ничего не защищает — он лишь выглядит
|
||||
# защитой, и это хуже его отсутствия.
|
||||
#
|
||||
# Прогон дорогой и проверяет не продукт, а тесты, поэтому он не входит ни в
|
||||
# Validate, ни в цикл разработки, ни в релизный гейт (#513, решение владельца
|
||||
# 09.09): каждую ночь по расписанию, отказ — issue с отчётом (#472).
|
||||
# Мутанты, задетые диффом, конвейер ревью гоняет отдельно на кандидате (#510).
|
||||
# Дешёвая половина — «якоря патчей живы, guard-файлы существуют» — идёт с
|
||||
# обычными юнитами: test/mutation-gate.test.mjs.
|
||||
#
|
||||
# #332: бандл собирается только мутантам с браузерным гвардом (guardNeedsBundle),
|
||||
# компиляция тестов в worktree стартует с тёплого test-build (инкрементальный
|
||||
# tsc), а реестр режется на четыре чересполосных шарда — полный прогон
|
||||
# укладывается в десятки минут вместо часов. Локальный дифф-режим:
|
||||
# node scripts/mutation-gate.mjs --changed origin/dev..HEAD
|
||||
# Тонкий вызывающий файл (#623). Для этого события GitHub берёт workflow из
|
||||
# ветки по умолчанию (`main`), поэтому здесь только то, что обязано жить там:
|
||||
# триггеры, run-name, права и concurrency. Тело — `_mutation-gate.yml` по ссылке
|
||||
# `@dev`: правка конвейера — один коммит в `dev`, зеркало в `main` не нужно.
|
||||
# Этот файл меняется, только когда меняются сами триггеры или потолок прав;
|
||||
# тогда он зеркалится в `main`, и preflight `workflow_sync` (validate.yml)
|
||||
# держит копии равными.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -43,221 +33,15 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# #549: moving ref разрешается ровно один раз. Все шарды ниже получают один
|
||||
# commit/tree, а не самостоятельно читают dev в разное время.
|
||||
material:
|
||||
name: "Зафиксировать неизменяемый материал"
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
sha: ${{ steps.identity.outputs.sha }}
|
||||
tree: ${{ steps.identity.outputs.tree }}
|
||||
ref: ${{ steps.identity.outputs.ref }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || 'dev' }}
|
||||
fetch-depth: 0
|
||||
- name: Зафиксировать commit и tree
|
||||
id: identity
|
||||
run: |
|
||||
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
echo "tree=$(git rev-parse 'HEAD^{tree}')" >> "$GITHUB_OUTPUT"
|
||||
echo "ref=${{ github.event_name == 'workflow_dispatch' && inputs.ref || 'dev' }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
mutants:
|
||||
name: "Мутанты: каждый обязан красить тесты (шард ${{ matrix.shard }} из 4)"
|
||||
needs: material
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4]
|
||||
# Шард ~64 мутантов × свой guard; бандл собирают только браузерные гварды.
|
||||
# Час — потолок против зависшего Chromium.
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ needs.material.outputs.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
|
||||
- run: npm ci
|
||||
|
||||
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
||||
with:
|
||||
python-version: '3.14'
|
||||
|
||||
- name: Установить backend test dependencies
|
||||
run: |
|
||||
# Точные версии — в tests_backend/requirements.txt (#392): без них
|
||||
# резолвер выбирает набор сам, и «зелёный backend» значит разное
|
||||
# в разные дни.
|
||||
pip install -r tests_backend/requirements.txt
|
||||
pip list --format=columns | grep -Ei 'homeassistant|voluptuous|^pytest '
|
||||
|
||||
- name: Кэш браузеров Playwright
|
||||
id: pw
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
|
||||
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: Реестр применим к текущему коду
|
||||
run: node scripts/mutation-gate.mjs --check
|
||||
|
||||
- name: Тёплый test-build для инкрементальной компиляции мутантов
|
||||
run: npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs
|
||||
|
||||
# Вывод шарда сохраняется артефактом (#472): строки
|
||||
# `FAIL <id>: тест остался зелёным…` — единственное место, где названо,
|
||||
# ЧТО сбежало. Без артефакта отказ безымянный. `PIPESTATUS` — чтобы
|
||||
# `tee` не съел код выхода раннера.
|
||||
- name: Каждый тест ловит свою поломку
|
||||
run: |
|
||||
mkdir -p artifacts/mutation-shard-${{ matrix.shard }}
|
||||
set -o pipefail
|
||||
node scripts/mutation-gate.mjs --shard=${{ matrix.shard }}/4 2>&1 | tee artifacts/mutation-shard-${{ matrix.shard }}/mutation-shard-${{ matrix.shard }}.log
|
||||
- name: Записать identity шарда
|
||||
if: always()
|
||||
run: |
|
||||
node scripts/mutation-gate-report.mjs \
|
||||
--write-evidence=artifacts/mutation-shard-${{ matrix.shard }}/evidence.json \
|
||||
--sha=${{ needs.material.outputs.sha }} \
|
||||
--tree=${{ needs.material.outputs.tree }} \
|
||||
--workflow-sha=${{ github.workflow_sha }} \
|
||||
--run-id=${{ github.run_id }} --run-attempt=${{ github.run_attempt }} \
|
||||
--shard=${{ matrix.shard }} --shards=4
|
||||
- name: Сохранить лог и identity шарда
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: mutation-shard-${{ matrix.shard }}-attempt-${{ github.run_attempt }}
|
||||
path: artifacts/mutation-shard-${{ matrix.shard }}
|
||||
if-no-files-found: warn
|
||||
retention-days: 30
|
||||
|
||||
# Результат нельзя приписывать material, пока не доказаны все четыре шарда.
|
||||
# always() нужен при красном мутанте: лог красного шарда всё равно evidence.
|
||||
evidence:
|
||||
name: "Доказать единый material всех шардов"
|
||||
needs: [material, mutants]
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
# CLI отчётчика принадлежит зафиксированному material. В main лежит
|
||||
# только workflow и может не быть dev-версии scripts/**.
|
||||
ref: ${{ needs.material.outputs.sha }}
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Забрать evidence всех попыток
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
with:
|
||||
pattern: mutation-shard-*
|
||||
path: artifacts/mutation-logs
|
||||
- name: Проверить полноту и identity
|
||||
run: |
|
||||
node scripts/mutation-gate-report.mjs --verify-only \
|
||||
--logs=artifacts/mutation-logs --shards=4 \
|
||||
--sha=${{ needs.material.outputs.sha }} \
|
||||
--tree=${{ needs.material.outputs.tree }} \
|
||||
--workflow-sha=${{ github.workflow_sha }} \
|
||||
--run-id=${{ github.run_id }} --run-attempt=${{ github.run_attempt }}
|
||||
|
||||
# Адресат у отказа (#472). Только по расписанию: ручной dispatch остаётся
|
||||
# для отладки самого гейта, его результат смотрят в прогоне — issue на
|
||||
# каждый такой отказ был бы шумом, который снова перестанут читать.
|
||||
#
|
||||
# Права job-уровня ЗАМЕНЯЮТ права workflow, а не дополняют (прецедент —
|
||||
# validate.yml, job с actions: read): перечислены все три.
|
||||
report:
|
||||
name: "Отказ расписания: issue и Telegram"
|
||||
needs: [material, mutants, evidence]
|
||||
if: always() && github.event_name == 'schedule' && (needs.mutants.result != 'success' || needs.evidence.result != 'success')
|
||||
runs-on: ubuntu-latest
|
||||
# Потолок прав тела: объединение job-level прав `_mutation-gate.yml`. Вызываемый
|
||||
# workflow может права только сузить, поэтому каждая его job по-прежнему
|
||||
# получает свой прежний минимум (#556), а шире этого набора не получит никто.
|
||||
dev:
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
# Не перечитываем moving dev: код отчётчика берётся из уже
|
||||
# зафиксированного material. В стабильном main dev-скриптов нет.
|
||||
ref: ${{ needs.material.outputs.sha }}
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Забрать логи шардов
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
with:
|
||||
pattern: mutation-shard-*
|
||||
path: artifacts/mutation-logs
|
||||
- name: Собрать отчёт
|
||||
id: report
|
||||
env:
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
run: |
|
||||
mkdir -p artifacts
|
||||
node scripts/mutation-gate-report.mjs \
|
||||
--require-evidence --logs=artifacts/mutation-logs --shards=4 \
|
||||
--sha=${{ needs.material.outputs.sha }} \
|
||||
--tree=${{ needs.material.outputs.tree }} \
|
||||
--workflow-sha=${{ github.workflow_sha }} \
|
||||
--run-id=${{ github.run_id }} --run-attempt=${{ github.run_attempt }} \
|
||||
--run-url="$RUN_URL" --ref=${{ needs.material.outputs.ref }} \
|
||||
--body-out=artifacts/mutation-report.md \
|
||||
--telegram-out=artifacts/mutation-telegram.txt >> "$GITHUB_OUTPUT"
|
||||
# Одно issue, а не одно на неделю: открытое с тем же маркером в заголовке
|
||||
# получает комментарий, новое заводится только если открытого нет.
|
||||
- name: Issue — создать или дописать
|
||||
id: issue
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
MARKER: ${{ steps.report.outputs.marker }}
|
||||
TITLE: ${{ steps.report.outputs.title }}
|
||||
run: |
|
||||
existing=$(gh issue list --repo "$REPO" --state open --search "\"$MARKER\" in:title" \
|
||||
--json number,title --jq '[.[] | select(.title | startswith(env.MARKER))][0].number // empty')
|
||||
if [ -n "$existing" ]; then
|
||||
gh issue comment "$existing" --repo "$REPO" --body-file artifacts/mutation-report.md
|
||||
url="${{ github.server_url }}/$REPO/issues/$existing"
|
||||
else
|
||||
url=$(gh issue create --repo "$REPO" --title "$TITLE" \
|
||||
--label infra --label process --label tests \
|
||||
--body-file artifacts/mutation-report.md)
|
||||
fi
|
||||
echo "url=$url" >> "$GITHUB_OUTPUT"
|
||||
echo "issue: $url"
|
||||
# Тот же канал, что у релизов (announce.yml). Нет секретов — не отказ:
|
||||
# issue уже заведено, а Telegram — второй адресат, не единственный.
|
||||
- name: Telegram
|
||||
if: always() && steps.issue.outcome == 'success'
|
||||
env:
|
||||
TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
CHAT: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
ISSUE_URL: ${{ steps.issue.outputs.url }}
|
||||
run: |
|
||||
if [ -z "$TOKEN" ] || [ -z "$CHAT" ]; then
|
||||
echo "::warning::TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID не заданы — оповещение пропущено, issue заведено"
|
||||
exit 0
|
||||
fi
|
||||
TEXT=$(sed "s|(issue)|$ISSUE_URL|" artifacts/mutation-telegram.txt)
|
||||
curl -sS --fail-with-body -X POST \
|
||||
"https://api.telegram.org/bot$TOKEN/sendMessage" \
|
||||
--data-urlencode "chat_id=$CHAT" \
|
||||
--data-urlencode "text=$TEXT" \
|
||||
-d disable_web_page_preview=true
|
||||
uses: Matysh/houseplan-card/.github/workflows/_mutation-gate.yml@dev # #623: тело конвейера из dev
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
secrets: inherit
|
||||
|
||||
@@ -1,25 +1,13 @@
|
||||
# Ночной полный прогон (#479).
|
||||
#
|
||||
# Тяжёлые job Validate — смоки, golden, performance_smoke — на обычном пуше не
|
||||
# идут: они ни разу не ловили дефект в момент ревью и стоили ~6 минут
|
||||
# критического пути на каждую итерацию. Полный набор идёт на кандидате беты
|
||||
# (трейлер `Release:`), по кнопке и здесь — каждую ночь на голове `dev`.
|
||||
#
|
||||
# Почему не `schedule` прямо в validate.yml: расписание исполняется на ветке
|
||||
# по умолчанию (`main`), а проверять надо `dev`. Один dispatch с `--ref dev`
|
||||
# делает это без переписывания checkout во всех job. Reuse (#208) сохраняется:
|
||||
# при неизменённом дереве ночной прогон обойдётся маркерами.
|
||||
#
|
||||
# Красный ночной прогон — сигнал автору последних коммитов на dev, не гейт:
|
||||
# гейт беты по-прежнему требует зелёный Validate на точном SHA кандидата, и
|
||||
# там полный набор идёт заново.
|
||||
#
|
||||
# Сигнал обязан быть настоящим (#492 §7): до этой задачи job завершалась
|
||||
# успехом в момент постановки Validate в очередь, и красный полный прогон не
|
||||
# делал ночной workflow красным. Теперь job находит запущенный прогон и ждёт
|
||||
# его: успешный dispatch — не успешная проверка.
|
||||
name: Ночной полный прогон dev
|
||||
|
||||
# Тонкий вызывающий файл (#623). Для этого события GitHub берёт workflow из
|
||||
# ветки по умолчанию (`main`), поэтому здесь только то, что обязано жить там:
|
||||
# триггеры, run-name, права и concurrency. Тело — `_nightly.yml` по ссылке
|
||||
# `@dev`: правка конвейера — один коммит в `dev`, зеркало в `main` не нужно.
|
||||
# Этот файл меняется, только когда меняются сами триггеры или потолок прав;
|
||||
# тогда он зеркалится в `main`, и preflight `workflow_sync` (validate.yml)
|
||||
# держит копии равными.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 2 * * *'
|
||||
@@ -30,35 +18,12 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dispatch:
|
||||
name: "Запустить Validate на dev с полным набором и дождаться результата"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
since=$(date -u +%FT%TZ)
|
||||
gh workflow run validate.yml --repo "$REPO" --ref dev -f full=true
|
||||
echo "Validate(dev, full=true) поставлен в очередь: $since"
|
||||
# Найти именно этот прогон: workflow_dispatch на dev, созданный не
|
||||
# раньше момента запуска. До трёх минут на появление в списке.
|
||||
run_id=""
|
||||
for _ in $(seq 1 18); do
|
||||
sleep 10
|
||||
run_id=$(gh run list --repo "$REPO" --workflow validate.yml --branch dev \
|
||||
--event workflow_dispatch --json databaseId,createdAt --limit 5 \
|
||||
--jq "[.[] | select(.createdAt >= \"$since\")] | sort_by(.createdAt) | last | .databaseId // empty")
|
||||
[ -n "$run_id" ] && break
|
||||
done
|
||||
if [ -z "$run_id" ]; then
|
||||
echo "::error::прогон Validate не появился за 3 минуты — dispatch не равен проверке"
|
||||
exit 1
|
||||
fi
|
||||
url="${{ github.server_url }}/$REPO/actions/runs/$run_id"
|
||||
echo "дочерний прогон: $url"
|
||||
echo "- Validate(dev, full=true): $url" >> "$GITHUB_STEP_SUMMARY"
|
||||
# Ждём завершения; красный дочерний прогон — красный ночной.
|
||||
gh run watch "$run_id" --repo "$REPO" --exit-status --interval 30
|
||||
# Потолок прав тела: объединение job-level прав `_nightly.yml`. Вызываемый
|
||||
# workflow может права только сузить, поэтому каждая его job по-прежнему
|
||||
# получает свой прежний минимум (#556), а шире этого набора не получит никто.
|
||||
dev:
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
uses: Matysh/houseplan-card/.github/workflows/_nightly.yml@dev # #623: тело конвейера из dev
|
||||
secrets: inherit
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Метрики процесса
|
||||
|
||||
# Тонкий вызывающий файл (#623). Для этого события GitHub берёт workflow из
|
||||
# ветки по умолчанию (`main`), поэтому здесь только то, что обязано жить там:
|
||||
# триггеры, run-name, права и concurrency. Тело — `_process-metrics.yml` по ссылке
|
||||
# `@dev`: правка конвейера — один коммит в `dev`, зеркало в `main` не нужно.
|
||||
# Этот файл меняется, только когда меняются сами триггеры или потолок прав;
|
||||
# тогда он зеркалится в `main`, и preflight `workflow_sync` (validate.yml)
|
||||
# держит копии равными.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Понедельник 05:00 UTC — после ночных прогонов, до рабочего дня.
|
||||
- cron: '0 5 * * 1'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
days:
|
||||
description: "Окно в днях"
|
||||
required: false
|
||||
default: "7"
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
issues: read
|
||||
|
||||
jobs:
|
||||
# Потолок прав тела: объединение job-level прав `_process-metrics.yml`. Вызываемый
|
||||
# workflow может права только сузить, поэтому каждая его job по-прежнему
|
||||
# получает свой прежний минимум (#556), а шире этого набора не получит никто.
|
||||
dev:
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
issues: read
|
||||
uses: Matysh/houseplan-card/.github/workflows/_process-metrics.yml@dev # #623: тело конвейера из dev
|
||||
with:
|
||||
days: ${{ inputs.days }}
|
||||
secrets: inherit
|
||||
@@ -1,6 +1,14 @@
|
||||
name: Сверка очереди ревью
|
||||
run-name: "reconcile process queue · ${{ github.event_name }}"
|
||||
|
||||
# Тонкий вызывающий файл (#623). Для этого события GitHub берёт workflow из
|
||||
# ветки по умолчанию (`main`), поэтому здесь только то, что обязано жить там:
|
||||
# триггеры, run-name, права и concurrency. Тело — `_process-reconcile.yml` по ссылке
|
||||
# `@dev`: правка конвейера — один коммит в `dev`, зеркало в `main` не нужно.
|
||||
# Этот файл меняется, только когда меняются сами триггеры или потолок прав;
|
||||
# тогда он зеркалится в `main`, и preflight `workflow_sync` (validate.yml)
|
||||
# держит копии равными.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '7,37 * * * *'
|
||||
@@ -18,41 +26,15 @@ permissions:
|
||||
issues: read
|
||||
|
||||
jobs:
|
||||
reconcile:
|
||||
name: "Один снимок S4/S7 без polling модели"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
concurrency:
|
||||
group: process-reconcile
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
# Расписание читается из main, а исполняемый reconciler — из dev: так
|
||||
# после штатного merge действует та же версия кода, которую проверил CI.
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: dev
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Сопоставить labels, requests, runs и sealed evidence
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.HP_PROCESS_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
APPLY: ${{ github.event_name == 'schedule' || inputs.apply == true }}
|
||||
run: |
|
||||
mkdir -p artifacts/process-reconcile
|
||||
node scripts/process-reconcile.mjs \
|
||||
--repo "$REPO" \
|
||||
--apply="$APPLY" \
|
||||
--max-actions=5 \
|
||||
--output=artifacts/process-reconcile/summary.json
|
||||
- name: Опубликовать компактный machine-readable итог
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: process-reconcile-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: artifacts/process-reconcile/summary.json
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
# Потолок прав тела: объединение job-level прав `_process-reconcile.yml`. Вызываемый
|
||||
# workflow может права только сузить, поэтому каждая его job по-прежнему
|
||||
# получает свой прежний минимум (#556), а шире этого набора не получит никто.
|
||||
dev:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
issues: read
|
||||
uses: Matysh/houseplan-card/.github/workflows/_process-reconcile.yml@dev # #623: тело конвейера из dev
|
||||
with:
|
||||
apply: ${{ inputs.apply == true }}
|
||||
secrets: inherit
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
name: Продолжение ревью после Validate
|
||||
run-name: "resume · ${{ github.event.workflow_run.head_branch }} · ${{ github.event.workflow_run.conclusion }}"
|
||||
|
||||
# Тонкий вызывающий файл (#623). Для этого события GitHub берёт workflow из
|
||||
# ветки по умолчанию (`main`), поэтому здесь только то, что обязано жить там:
|
||||
# триггеры, run-name, права и concurrency. Тело — `_process-resume.yml` по ссылке
|
||||
# `@dev`: правка конвейера — один коммит в `dev`, зеркало в `main` не нужно.
|
||||
# Этот файл меняется, только когда меняются сами триггеры или потолок прав;
|
||||
# тогда он зеркалится в `main`, и preflight `workflow_sync` (validate.yml)
|
||||
# держит копии равными.
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Проверка (CI)"]
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
# Потолок прав тела: объединение job-level прав `_process-resume.yml`. Вызываемый
|
||||
# workflow может права только сузить, поэтому каждая его job по-прежнему
|
||||
# получает свой прежний минимум (#556), а шире этого набора не получит никто.
|
||||
dev:
|
||||
# Тот же фильтр, что у job `resume` тела: посторонние события не
|
||||
# поднимают вызов, а прогон остаётся skipped, как до #623.
|
||||
if: github.event.workflow_run.event == 'workflow_dispatch' && startsWith(github.event.workflow_run.head_branch, 'issue/')
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
uses: Matysh/houseplan-card/.github/workflows/_process-resume.yml@dev # #623: тело конвейера из dev
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,335 @@
|
||||
name: "Релиз: независимое ревью линии"
|
||||
run-name: "Release review ${{ inputs.tag }}"
|
||||
|
||||
# #638, PROCESS.md §11.5: перед стабильным релизом — одно независимое ревью
|
||||
# поверхностей, изменённых всей линией бет, «с нуля»: без ТЗ и документов
|
||||
# раундов, по SCOPE и USER-GUIDE. Инкрементальное ревью судит дифф задачи
|
||||
# против её ТЗ; четыре дефекта линии 1.77 (#607, #608, #611, #619) не входили
|
||||
# ни в один AC и нашлись только так.
|
||||
#
|
||||
# Решение владельца 2026-09-25: ревью НЕ блокирует выпуск. `release.yml`
|
||||
# запускает этот workflow параллельно гейтам из job, от которого не зависит ни
|
||||
# один job выпуска; документ — рекомендация, в работу его берёт владелец.
|
||||
#
|
||||
# Только `workflow_dispatch`: GitHub исполняет файл с той ветки, на которой
|
||||
# запущен прогон (`release.yml` зовёт `--ref dev`), поэтому зеркало в `main`
|
||||
# не нужно, а правка — один коммит в `dev`.
|
||||
#
|
||||
# Три job, как у конвейера (#551, #556): детерминированная подготовка,
|
||||
# недоверенная модель без единого права на запись, детерминированная
|
||||
# публикация документа в `dev`.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Stable release tag, for example v1.78.0"
|
||||
required: true
|
||||
type: string
|
||||
candidate:
|
||||
description: "Exact candidate SHA; empty = the commit of the tag"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
force:
|
||||
description: "Review again even when the document already exists in dev"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: release-review-${{ inputs.tag }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
name: "Ревью релиза: вход линии"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
proceed: ${{ steps.line.outputs.proceed }}
|
||||
candidate: ${{ steps.line.outputs.candidate }}
|
||||
base: ${{ steps.line.outputs.base }}
|
||||
doc: ${{ steps.line.outputs.doc }}
|
||||
issues: ${{ steps.line.outputs.issues }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: dev
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Кандидат, база и issue линии
|
||||
id: line
|
||||
env:
|
||||
TAG: ${{ inputs.tag }}
|
||||
CANDIDATE: ${{ inputs.candidate }}
|
||||
FORCE: ${{ inputs.force }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
run: |
|
||||
doc=$(node scripts/release-review.mjs doc --tag="$TAG")
|
||||
git fetch -q --tags origin
|
||||
if [ -z "$CANDIDATE" ]; then
|
||||
CANDIDATE=$(git rev-parse --verify -q "refs/tags/$TAG^{commit}") || {
|
||||
echo "::error::тега $TAG нет, а кандидат не передан"; exit 1; }
|
||||
fi
|
||||
git cat-file -e "$CANDIDATE^{commit}"
|
||||
CANDIDATE=$(git rev-parse "$CANDIDATE^{commit}")
|
||||
# Повтор на тот же тег не тратит модель: документ уже есть (§11.5).
|
||||
if [ "$FORCE" != "true" ] && git cat-file -e "origin/dev:$doc" 2>/dev/null; then
|
||||
echo "::notice::$doc уже есть в dev — повторное ревью не запускается (force=true, чтобы переснять)"
|
||||
echo "proceed=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
out="$RUNNER_TEMP/release-review-input"
|
||||
node scripts/release-review.mjs prepare --tag="$TAG" --candidate="$CANDIDATE" \
|
||||
--out="$out" --run-url="$RUN_URL" | tee -a "$GITHUB_OUTPUT"
|
||||
echo "candidate=$CANDIDATE" >> "$GITHUB_OUTPUT"
|
||||
echo "proceed=true" >> "$GITHUB_OUTPUT"
|
||||
(cd "$out" && sha256sum brief.md line-membership.json > manifest.sha256)
|
||||
cat "$out/brief.md" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Передать вход модели
|
||||
if: steps.line.outputs.proceed == 'true'
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: release-review-input-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ runner.temp }}/release-review-input
|
||||
if-no-files-found: error
|
||||
retention-days: 3
|
||||
|
||||
model_review:
|
||||
name: "Ревью релиза: работа модели"
|
||||
needs: prepare
|
||||
if: needs.prepare.outputs.proceed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
# Недоверенная стадия. Прав на запись нет никаких: ни в репозиторий, ни в
|
||||
# issue. Документ публикует `publish`; находки в issue превращает владелец.
|
||||
# `github_token` у шага Review обязателен (#556): без него action меняет
|
||||
# OIDC на собственный App-токен с правом записи.
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ needs.prepare.outputs.candidate }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
|
||||
- name: Получить вход линии
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: release-review-input-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ runner.temp }}/release-review-input
|
||||
|
||||
- name: Проверить вход и кандидата
|
||||
env:
|
||||
CANDIDATE: ${{ needs.prepare.outputs.candidate }}
|
||||
run: |
|
||||
(cd "$RUNNER_TEMP/release-review-input" && sha256sum -c manifest.sha256)
|
||||
test "$(git rev-parse HEAD)" = "$CANDIDATE"
|
||||
|
||||
- name: Установить зависимости
|
||||
run: npm ci
|
||||
|
||||
- name: Кэш браузеров Playwright
|
||||
id: pw
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
|
||||
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 chromium
|
||||
|
||||
# Тот же обход, что у конвейера (_process.yml, anthropics issue 1817).
|
||||
- name: Установить Claude Code детерминированно
|
||||
id: claude_bin
|
||||
run: |
|
||||
src=$(ls "$RUNNER_WORKSPACE"/../_actions/anthropics/claude-code-*/v1/src/entrypoints/run.ts 2>/dev/null | head -1)
|
||||
ver=$(grep -oE 'claudeCodeVersion = "[0-9]+\.[0-9]+\.[0-9]+"' "$src" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || true)
|
||||
ver="${ver:-2.1.265}"
|
||||
base=https://downloads.claude.ai/claude-code-releases
|
||||
bin="$HOME/.local/bin/claude"
|
||||
mkdir -p "$(dirname "$bin")"
|
||||
curl -fsSL --retry 3 "$base/$ver/linux-x64/claude" -o "$bin"
|
||||
sum=$(curl -fsSL --retry 3 "$base/$ver/manifest.json" | jq -r '.platforms["linux-x64"].checksum')
|
||||
echo "$sum $bin" | sha256sum -c -
|
||||
chmod +x "$bin"
|
||||
"$bin" --version
|
||||
echo "path=$bin" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Review
|
||||
id: review
|
||||
uses: anthropics/claude-code-action@9cdae7f0d995e3ba7c33f226087fdf82a59cd520 # v1
|
||||
env:
|
||||
REVIEW_DOC: ${{ runner.temp }}/release-review.md
|
||||
REVIEW_INPUT: ${{ runner.temp }}/release-review-input
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path_to_claude_code_executable: ${{ steps.claude_bin.outputs.path }}
|
||||
prompt: |
|
||||
Ты независимый ревьюер релиза House Plan. Язык ответа — русский.
|
||||
|
||||
Релиз: ${{ inputs.tag }} · кандидат ${{ needs.prepare.outputs.candidate }}
|
||||
(рабочая копия уже на нём) · база линии: ${{ needs.prepare.outputs.base || 'нет' }}.
|
||||
Вход линии — файл $REVIEW_INPUT/brief.md (issue линии, доказанные
|
||||
трейлерами, и изменённые продуктовые файлы) и
|
||||
$REVIEW_INPUT/line-membership.json.
|
||||
|
||||
Правила этого ревью — docs/process/REVIEWER.md, раздел «Независимое
|
||||
ревью линии», канон — PROCESS.md §11.5. Прочитай их первыми.
|
||||
|
||||
Главное, что нельзя пропустить:
|
||||
- Ты судишь ПОВЕРХНОСТИ, изменённые линией, против ПОЛЬЗОВАТЕЛЯ, а
|
||||
не дифф против ТЗ. Не читай ТЗ задач (раздел «## ТЗ» в issue,
|
||||
docs/specs/**) и документы раундов (docs/reviews/**): независимость
|
||||
и есть смысл шага. Основа суждения — docs/SCOPE.md (персоны и
|
||||
работы) и docs/USER-GUIDE.ru.md (что обещано пользователю).
|
||||
- Проверяй исполнением, а не чтением: собери бандл
|
||||
(`npm run bundle:sync`), открой затронутые поверхности в браузере
|
||||
(стенд `demo/`, смоки `demo/smoke_*.mjs`), вводи текст
|
||||
посимвольно, закрывай диалоги настоящим Escape и крестиком. Где
|
||||
есть диалоги HA — пиннутая фикстура `ha-dialog` (#505,
|
||||
demo/helpers/README-ha-dialog-505.md,
|
||||
`node demo/verify_ha_dialog_discard_recovery.mjs`).
|
||||
- Для каждой поверхности: обычный сценарий и самый рискованный
|
||||
соседний, шесть классов риска (async, данные/права, геометрия,
|
||||
визуал, объём/perf, host/input) — PROCESS.md §2.6.
|
||||
- Ты ничего не правишь и не публикуешь: ни код, ни issue, ни
|
||||
комментарии. Права на запись у тебя нет. Любые изменения рабочей
|
||||
копии будут отброшены — после проверок восстанови её сам
|
||||
(`git checkout -- . && git clean -fd`), если что-то менял.
|
||||
|
||||
Находки: High / Medium / Low, у каждой — поверхность, воспроизведение
|
||||
(команда или шаги), что увидит пользователь, какая персона задета.
|
||||
Выпуск это ревью не останавливает: документ — рекомендация владельцу.
|
||||
|
||||
Напиши полный документ в файл по пути из переменной REVIEW_DOC
|
||||
(абсолютный, вне репозитория). Разделы: что входило в линию,
|
||||
поверхности и как каждая проверялась (команда → результат),
|
||||
находки, что проверено и корректно, чего не проверял и почему.
|
||||
Первой строкой после заголовка — `Итог: High N · Medium N · Low N`.
|
||||
|
||||
Затем верни JSON по схеме — последнее обязательное действие.
|
||||
claude_args: |
|
||||
--max-turns 200
|
||||
--allowedTools Read,Write,Grep,Glob,Bash
|
||||
--json-schema '{"type":"object","properties":{"high":{"type":"integer"},"medium":{"type":"integer"},"low":{"type":"integer"},"summary":{"type":"string"}},"required":["high","medium","low","summary"]}'
|
||||
|
||||
- name: Запечатать результат модели
|
||||
env:
|
||||
SOURCE: ${{ runner.temp }}/release-review.md
|
||||
OUT: ${{ steps.review.outputs.structured_output }}
|
||||
run: |
|
||||
test -s "$SOURCE" || { echo "::error::модель не оставила документ ревью"; exit 1; }
|
||||
dir="$RUNNER_TEMP/release-review-result"
|
||||
mkdir -p "$dir"
|
||||
printf '%s' "$OUT" > "$dir/result.json"
|
||||
jq -e '(.high|type=="number") and (.medium|type=="number") and (.low|type=="number") and (.summary|type=="string")' \
|
||||
"$dir/result.json" >/dev/null
|
||||
cp "$SOURCE" "$dir/release-review.md"
|
||||
(cd "$dir" && sha256sum release-review.md result.json > manifest.sha256)
|
||||
|
||||
- name: Передать результат публикации
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: release-review-result-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ runner.temp }}/release-review-result
|
||||
if-no-files-found: error
|
||||
retention-days: 3
|
||||
|
||||
publish:
|
||||
name: "Ревью релиза: документ в dev"
|
||||
needs: [prepare, model_review]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: dev
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Получить результат модели
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: release-review-result-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ runner.temp }}/release-review-result
|
||||
|
||||
# Модель пишет только текст документа. Путь, машинный блок, индекс и
|
||||
# коммит решает этот шаг; всё остальное в рабочей копии не существует.
|
||||
- name: Опубликовать документ
|
||||
env:
|
||||
TOKEN: ${{ secrets.HP_PROCESS_TOKEN }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
DOC: ${{ needs.prepare.outputs.doc }}
|
||||
CANDIDATE: ${{ needs.prepare.outputs.candidate }}
|
||||
BASE: ${{ needs.prepare.outputs.base }}
|
||||
ISSUES: ${{ needs.prepare.outputs.issues }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
run: |
|
||||
dir="$RUNNER_TEMP/release-review-result"
|
||||
(cd "$dir" && sha256sum -c manifest.sha256)
|
||||
test "$DOC" = "$(node scripts/release-review.mjs doc --tag="$TAG")"
|
||||
counts=$(jq -r '"High \(.high) · Medium \(.medium) · Low \(.low)"' "$dir/result.json")
|
||||
for attempt in 1 2 3; do
|
||||
git fetch -q origin dev
|
||||
git reset -q --hard origin/dev
|
||||
git clean -fdq
|
||||
mkdir -p docs/reviews
|
||||
{
|
||||
cat "$dir/release-review.md"
|
||||
printf '\n\n<!-- hp-release-review-anchors -->\n### Материал ревью\n\n```\n'
|
||||
printf 'tag %s\ncandidate %s\nbase %s\nissues %s\nrun %s\n' \
|
||||
"$TAG" "$CANDIDATE" "${BASE:-—}" "${ISSUES:-—}" "$RUN_URL"
|
||||
printf '```\n'
|
||||
} > "$DOC"
|
||||
node scripts/reviews-index.mjs --dir=docs/reviews
|
||||
git add -- "$DOC" docs/reviews/INDEX.md
|
||||
git diff --cached --name-only | node scripts/review-doc-guard.mjs
|
||||
git -c user.name="claude[bot]" \
|
||||
-c user.email="209825114+claude[bot]@users.noreply.github.com" \
|
||||
commit -q -F - <<MSG
|
||||
docs: release review for $TAG
|
||||
|
||||
Независимое ревью линии перед стабильным релизом (PROCESS.md §11.5).
|
||||
Итог: $counts. Выпуск не блокирует; решение по находкам — за владельцем.
|
||||
|
||||
Issue: #638
|
||||
User-Visible: no
|
||||
MSG
|
||||
git diff --name-only "origin/dev...HEAD" | node scripts/review-doc-guard.mjs
|
||||
if git push -q "https://x-access-token:$TOKEN@github.com/${{ github.repository }}" HEAD:dev; then
|
||||
echo "### Независимое ревью $TAG" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "Итог: $counts — \`$DOC\` в dev. Выпуск не блокируется." >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "::notice::$DOC опубликован: $counts"
|
||||
exit 0
|
||||
fi
|
||||
echo "::warning::dev ушёл вперёд — попытка $attempt из 3, документ собирается заново"
|
||||
sleep $((attempt * 10))
|
||||
done
|
||||
echo "::error::документ ревью не опубликован в dev за три попытки"
|
||||
exit 1
|
||||
@@ -131,6 +131,36 @@ jobs:
|
||||
echo "release $TAG is public: repair mode — only missing assets may be added"
|
||||
fi
|
||||
|
||||
independent-review:
|
||||
name: "Независимое ревью линии (не блокирует выпуск)"
|
||||
# #638, PROCESS.md §11.5. Решение владельца 2026-09-25: ревью идёт
|
||||
# параллельно гейтам и выпуск не ждёт и не останавливает. Поэтому этот job
|
||||
# только ставит в очередь `release-review.yml` на `dev` и ни один job
|
||||
# выпуска от него не зависит (`needs` на него запрещён тестом
|
||||
# release-workflow); его отказ — предупреждение, а не красный релиз.
|
||||
# Беты пропускаются: ревью линии — перед стабильным.
|
||||
needs: candidate
|
||||
if: ${{ needs.candidate.outputs.prerelease != 'true' }}
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
actions: write
|
||||
steps:
|
||||
- name: Поставить в очередь ревью линии
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ needs.candidate.outputs.tag }}
|
||||
SHA: ${{ needs.candidate.outputs.sha }}
|
||||
run: |
|
||||
if gh workflow run release-review.yml --repo "${{ github.repository }}" --ref dev \
|
||||
-f tag="$TAG" -f candidate="$SHA"; then
|
||||
echo "Независимое ревью $TAG поставлено в очередь: release-review.yml на dev" >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "::warning::ревью линии $TAG не запущено — выпуск продолжается; запустить руками: gh workflow run release-review.yml --ref dev -f tag=$TAG -f candidate=$SHA"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gate:
|
||||
name: "Гейт: контракт, Validate, Full Performance и E2E на точном SHA"
|
||||
needs: candidate
|
||||
|
||||
@@ -18,13 +18,15 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
full:
|
||||
description: 'Полный набор: смоки, golden, performance_smoke, мутанты по диффу'
|
||||
description: 'Полный набор: смоки, golden, performance_smoke (без мутантов — #601)'
|
||||
type: boolean
|
||||
default: true
|
||||
# Мутанты по диффу бегут по запросу (#510): ревью-конвейер и слияние
|
||||
# кандидата запускают `-f full=false -f mutants=true` на материале.
|
||||
# Мутанты по диффу бегут только по явному запросу (#510, #601):
|
||||
# ревью-конвейер и слияние кандидата запускают `-f full=false
|
||||
# -f mutants=true` на материале. `full=true`, трейлер `Release:` и ночь
|
||||
# их не включают — ночью идёт полный реестр (mutation-gate.yml).
|
||||
mutants:
|
||||
description: 'Мутанты по диффу на этом SHA (кандидат ревью)'
|
||||
description: 'Мутанты по диффу на этом SHA (кандидат ревью или слияния)'
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
@@ -64,7 +66,7 @@ jobs:
|
||||
#
|
||||
# Коммиты и деревья скачиваются целиком, поэтому диапазоны и `merge-base`
|
||||
# работают как раньше. Единственная догрузка по требованию здесь —
|
||||
# `git show origin/main:.github/workflows/process.yml`: один блоб.
|
||||
# `git show origin/main:.github/workflows/<тонкий файл>`: по блобу на файл.
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with: { fetch-depth: 0, filter: 'blob:none' }
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
@@ -90,25 +92,46 @@ jobs:
|
||||
echo "скриншоты документации: режим $mode"
|
||||
node scripts/check-docs.mjs --external --screenshots=$mode
|
||||
|
||||
# Конвейер читает `process.yml` из ветки по умолчанию, поэтому файл обязан
|
||||
# совпадать в `main` и `dev`. До этой проверки совпадение держалось на
|
||||
# дисциплине: каждая правка требовала двух пушей и ручной сверки.
|
||||
- name: "Процесс: process.yml и mutation-gate.yml идентичны в main и dev"
|
||||
# #635: `docs/reviews/INDEX.md` — снимок каталога ревью; расхождение с
|
||||
# каталогом — невидимые через индекс документы (r2 #635 H1). Гейт стоит
|
||||
# только на push в dev: там свежесть держит конвейер — пересборка при
|
||||
# публикации документа и после каждого своего ребейза (приведение к dev,
|
||||
# слияние кандидата). На issue-ветках не судится: их ребейзит и
|
||||
# публикует в них конвейер из ветки по умолчанию, и доехавший туда
|
||||
# документ раунда индекс ветки знать не обязан — красить автора за
|
||||
# коммит конвейера нельзя; расхождение излечивает слияние.
|
||||
- name: "Индекс ревью совпадает с каталогом docs/reviews"
|
||||
id: reviews_index
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
|
||||
continue-on-error: true
|
||||
run: node scripts/reviews-index.mjs --dir=docs/reviews --check
|
||||
|
||||
# #623: для событий `issues`, `schedule` и `workflow_run` GitHub берёт
|
||||
# workflow из ветки по умолчанию (`main`). Там лежат тонкие вызывающие
|
||||
# файлы — триггеры, run-name, права, concurrency, — а тело каждого
|
||||
# (`_<имя>.yml`) они вызывают по ссылке `@dev`. Правка конвейера — один
|
||||
# коммит в `dev`; тела здесь не сверяются: их копия в `main` не исполняется
|
||||
# ни одним событием. Тонкий файл сверяется: правка триггера или потолка
|
||||
# прав, не доехавшая до `main`, действовала бы только в dev-копии.
|
||||
#
|
||||
# `performance.yml` не входит: по расписанию он судит `main` собственным
|
||||
# телом из `main`, расхождение с `dev` до промоушена законно. Список ниже
|
||||
# держит равным множеству файлов с такими триггерами
|
||||
# test/default-branch-workflows.test.mjs.
|
||||
- name: "Процесс: тонкие вызывающие workflow идентичны в main и dev"
|
||||
id: workflow_sync
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git fetch --quiet origin main dev
|
||||
# #472: расписание mutation-gate.yml тоже исполняется из ветки по
|
||||
# умолчанию — та же ловушка, что у process.yml. Сверяются оба.
|
||||
status=0
|
||||
for file in process.yml mutation-gate.yml; do
|
||||
for file in process.yml mutation-gate.yml process-resume.yml nightly.yml process-reconcile.yml process-metrics.yml; do
|
||||
if diff <(git show "origin/main:.github/workflows/$file") \
|
||||
<(git show "origin/dev:.github/workflows/$file"); then
|
||||
echo "$file: main и dev идентичны"
|
||||
else
|
||||
echo "РАСХОЖДЕНИЕ: $file в main и dev различаются."
|
||||
echo "Файл исполняется из ветки по умолчанию, поэтому"
|
||||
echo "правку нужно отправить в обе ветки."
|
||||
echo "Тонкий вызывающий файл исполняется из ветки по умолчанию,"
|
||||
echo "поэтому правку триггеров или прав нужно зеркалить в main (#623)."
|
||||
status=1
|
||||
fi
|
||||
done
|
||||
@@ -193,6 +216,7 @@ jobs:
|
||||
PROVENANCE: ${{ steps.provenance.outcome }}
|
||||
PROCESS_GATE: ${{ steps.process_gate.outcome }}
|
||||
ACTION_PINS: ${{ steps.action_pins.outcome }}
|
||||
REVIEWS_INDEX: ${{ steps.reviews_index.outcome }}
|
||||
run: |
|
||||
fail=0
|
||||
check() {
|
||||
@@ -206,10 +230,12 @@ jobs:
|
||||
}
|
||||
echo "### Предполётные проверки" >> "$GITHUB_STEP_SUMMARY"
|
||||
check "документация" "$DOCS"
|
||||
check "process.yml и mutation-gate.yml в main и dev" "$WORKFLOW_SYNC"
|
||||
check "тонкие вызывающие workflow в main и dev" "$WORKFLOW_SYNC"
|
||||
check "провенанс коммитов" "$PROVENANCE"
|
||||
check "процессный гейт" "$PROCESS_GATE"
|
||||
check "пины сторонних Actions" "$ACTION_PINS"
|
||||
# skipped — шаг не судил (не push в dev), это не отказ
|
||||
[ "$REVIEWS_INDEX" = "skipped" ] || check "индекс ревью совпадает с каталогом" "$REVIEWS_INDEX"
|
||||
exit $fail
|
||||
|
||||
# Классификация изменённых путей: тяжёлые job идут только там, где менялось
|
||||
@@ -241,7 +267,7 @@ jobs:
|
||||
mutants_requested: ${{ steps.heavy.outputs.mutants_requested }} # #510
|
||||
base: ${{ steps.base.outputs.base }}
|
||||
# `base` — до какого коммита классифицировать файлы ветки (#387),
|
||||
# `range_base` — от какого судить диапазон на dev (#388).
|
||||
# `range_base` — от какого судить диапазон на dev/main (#388, #619).
|
||||
range_base: ${{ steps.base.outputs.range_base }}
|
||||
steps:
|
||||
# `git diff --name-only` содержимого файлов не читает вовсе, поэтому
|
||||
@@ -281,9 +307,10 @@ jobs:
|
||||
gh api -X GET "repos/$REPO/actions/workflows/validate.yml/runs" \
|
||||
-f branch="$BRANCH" -f status=completed -F per_page=100 \
|
||||
> /tmp/validate-runs.json || echo '{}' > /tmp/validate-runs.json
|
||||
if [ "$REF" = "refs/heads/dev" ]; then
|
||||
# На dev классифицировать нечего (всё true), но база диапазона
|
||||
# нужна гейту «новый код не добавляет any» в job frontend (#388).
|
||||
if [ "$REF" = "refs/heads/dev" ] || [ "$REF" = "refs/heads/main" ]; then
|
||||
# На интеграционных ветках классифицировать нечего (всё true), но
|
||||
# база диапазона нужна гейту «новый код не добавляет any» в job
|
||||
# frontend (#388, #619).
|
||||
node scripts/classify-base.mjs --head="$HEAD_SHA" --mode=range \
|
||||
--name=range_base --fallback="$FALLBACK" --runs=/tmp/validate-runs.json
|
||||
exit 0
|
||||
@@ -300,8 +327,8 @@ jobs:
|
||||
HEAD_SHA: ${{ github.sha }}
|
||||
REF: ${{ github.ref }}
|
||||
run: |
|
||||
if [ "$REF" = "refs/heads/dev" ]; then
|
||||
echo "dev: без фильтров, всё true"
|
||||
if [ "$REF" = "refs/heads/dev" ] || [ "$REF" = "refs/heads/main" ]; then
|
||||
echo "dev/main: без фильтров, всё true"
|
||||
node scripts/classify-changes.mjs --all >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
@@ -556,9 +583,11 @@ jobs:
|
||||
# детерминированных непересекающихся шарда вместо ослабления проверки.
|
||||
# С #510 job бежит не на каждом пуше, а на кандидате: ревью-конвейер и
|
||||
# слияние кандидата запускают Validate по кнопке с `mutants=true` на
|
||||
# материале, ночной прогон/PR/кандидат беты берут её сами. За 08–09.09 на
|
||||
# промежуточных пушах она стоила 48 из 56 часов job-минут и в основном
|
||||
# отменялась следующим пушем. Когда мутанты запрошены, job бежит даже при
|
||||
# материале; PR берёт её сам. За 08–09.09 на промежуточных пушах она стоила
|
||||
# 48 из 56 часов job-минут и в основном отменялась следующим пушем. С #601
|
||||
# её не запрашивают ни `full=true`, ни кандидат беты (`Release:`): к бете
|
||||
# каждая задача уже прогнана мутантами на ревью и на слитом кандидате, а
|
||||
# ночь покрыта полным реестром. Когда мутанты запрошены, job бежит даже при
|
||||
# диффе без входов (отбор пустой, минута на checkout): гейт ревью читает
|
||||
# её исход по job, и skipped был бы неотличим от «не запрашивали».
|
||||
changed_mutants:
|
||||
@@ -629,29 +658,47 @@ jobs:
|
||||
fi
|
||||
echo "диапазон: $base..$HEAD_SHA"
|
||||
echo "base=$base" >> "$GITHUB_OUTPUT"
|
||||
count=$(node scripts/mutation-gate.mjs --changed="$base..$HEAD_SHA" --shard="$SHARD/6" \
|
||||
--ledger="artifacts/mutation-ledger/shard-$SHARD.json" --plan-only \
|
||||
| tee /dev/stderr | sed -n 's/^plan=//p' | tail -1)
|
||||
plan="$RUNNER_TEMP/mutation-plan-$SHARD.txt"
|
||||
node scripts/mutation-gate.mjs --changed="$base..$HEAD_SHA" --shard="$SHARD/6" \
|
||||
--ledger="artifacts/mutation-ledger/shard-$SHARD.json" --plan-only > "$plan"
|
||||
cat "$plan"
|
||||
count=$(sed -n 's/^plan=//p' "$plan" | tail -1)
|
||||
echo "к прогону в этом шарде: ${count:-0}"
|
||||
echo "count=${count:-0}" >> "$GITHUB_OUTPUT"
|
||||
# #620: план называет и окружение своих гардов. Непрочитанная строка
|
||||
# при непустом плане значит «ставить»: лишняя установка стоит минуты,
|
||||
# недоустановка — красный чистый прогон и лишний круг задачи.
|
||||
if [ "${count:-0}" != "0" ]; then
|
||||
browser=$(sed -n 's/^plan-browser=//p' "$plan" | tail -1)
|
||||
python=$(sed -n 's/^plan-python=//p' "$plan" | tail -1)
|
||||
echo "окружение шарда: браузер ${browser:-true}, Python ${python:-true}"
|
||||
echo "browser=${browser:-true}" >> "$GITHUB_OUTPUT"
|
||||
echo "python=${python:-true}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- if: steps.plan.outputs.count != '0'
|
||||
run: npm ci
|
||||
- if: steps.plan.outputs.count != '0'
|
||||
# Python и зависимости бэкенда — только шарду с pytest-гардами (#620);
|
||||
# колёса pip кэшируются по requirements.txt.
|
||||
- if: steps.plan.outputs.count != '0' && steps.plan.outputs.python == 'true'
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
||||
with:
|
||||
python-version: '3.14'
|
||||
cache: pip
|
||||
cache-dependency-path: tests_backend/requirements.txt
|
||||
- name: Backend test dependencies
|
||||
if: steps.plan.outputs.count != '0'
|
||||
if: steps.plan.outputs.count != '0' && steps.plan.outputs.python == 'true'
|
||||
run: pip install -r tests_backend/requirements.txt
|
||||
# Chromium — только шарду с браузерными гардами (#620): смоки, golden и
|
||||
# юниты, чей граф исполнения импортирует Playwright.
|
||||
- name: Кэш браузеров Playwright
|
||||
id: pw
|
||||
if: steps.plan.outputs.count != '0'
|
||||
if: steps.plan.outputs.count != '0' && steps.plan.outputs.browser == 'true'
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
|
||||
- name: Установить Chromium
|
||||
if: steps.plan.outputs.count != '0' && steps.pw.outputs.cache-hit != 'true'
|
||||
if: steps.plan.outputs.count != '0' && steps.plan.outputs.browser == 'true' && steps.pw.outputs.cache-hit != 'true'
|
||||
run: npx playwright install --with-deps chromium
|
||||
- name: Затронутые мутанты ловятся
|
||||
if: steps.plan.outputs.count != '0'
|
||||
@@ -695,8 +742,10 @@ jobs:
|
||||
run: npm run typecheck
|
||||
# Долг типизации снимается при извлечении подсистем (#34), а не разовой
|
||||
# заменой: в src/** его 1034 вхождения в 49 файлах. Гейт держит приращение
|
||||
# на нуле — судит только добавленные строки (#342).
|
||||
- name: "Новый код не добавляет any"
|
||||
# на нуле — судит только добавленные строки (#342). Тем же диапазоном
|
||||
# судится и приращение записей смоков в приватное состояние карточки
|
||||
# (#629): больше двух тысяч старых не трогаем, новых не добавляем.
|
||||
- name: "Новый код не добавляет any и приватных записей в смоках"
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
BEFORE_SHA: ${{ github.event.before }}
|
||||
@@ -709,11 +758,12 @@ jobs:
|
||||
git fetch -q origin dev
|
||||
zero=$(printf '%040d' 0)
|
||||
base=""
|
||||
# Пуш прямо в dev: диапазон — то, что добавлено с последнего
|
||||
# ДОКАЗАННО зелёного предка (#388), а не с головы предыдущего пуша:
|
||||
# его прогон штатно отменяется следующим, и добавленные им строки не
|
||||
# судил бы никто. Фолбэк — прежний `before`.
|
||||
if [ "$EVENT_NAME" != "pull_request" ] && [ "$REF" = "refs/heads/dev" ]; then
|
||||
# Пуш прямо в dev/main: диапазон — то, что добавлено с последнего
|
||||
# ДОКАЗАННО зелёного предка (#388, #619), а не с головы предыдущего
|
||||
# пуша: его прогон штатно отменяется следующим, и добавленные им
|
||||
# строки не судил бы никто. Фолбэк — прежний `before`.
|
||||
if [ "$EVENT_NAME" != "pull_request" ] \
|
||||
&& { [ "$REF" = "refs/heads/dev" ] || [ "$REF" = "refs/heads/main" ]; }; then
|
||||
base="${PROVEN_BASE:-$BEFORE_SHA}"
|
||||
fi
|
||||
# Ветка, PR, новая история: точки отсчёта нет, берём merge-base с dev.
|
||||
@@ -726,7 +776,10 @@ jobs:
|
||||
echo "не нашёл базу диапазона — гейт пропущен, это увидит следующий пуш"
|
||||
exit 0
|
||||
fi
|
||||
node scripts/no-new-any.mjs --base "$base" --head "$HEAD_SHA"
|
||||
status=0
|
||||
node scripts/no-new-any.mjs --base "$base" --head "$HEAD_SHA" || status=1
|
||||
node scripts/no-new-private-writes.mjs --base "$base" --head "$HEAD_SHA" || status=1
|
||||
exit "$status"
|
||||
- name: Unit tests
|
||||
run: npm test
|
||||
- name: Build
|
||||
@@ -736,6 +789,11 @@ jobs:
|
||||
run: |
|
||||
node scripts/bundle-tree.mjs dist custom_components/houseplan/frontend
|
||||
npm run bundle:budget
|
||||
# #624: `tsc --noUnusedLocals` чист вне порта/харнесса, шесть чисел
|
||||
# связности монолита не выше scripts/monolith-baseline.json. После
|
||||
# сборки — одно из чисел считает байты dist/.
|
||||
- name: Мёртвый код и связность монолита
|
||||
run: npm run lint:unused
|
||||
# Бандл собирается один раз и уезжает артефактом (#336). Прежде каждая из
|
||||
# пяти браузерных job собирала его заново: `bundle:sync` — это `tsc
|
||||
# --noEmit` плюс rollup, около минуты на job.
|
||||
|
||||
@@ -22,11 +22,22 @@ For work that changes visible behaviour, also read `docs/USER-GUIDE.ru.md` —
|
||||
interface wording comes from there and is not invented, or the UI starts speaking
|
||||
developer.
|
||||
|
||||
Then `PROCESS.md` (the full process), `docs/STATUS.md` (where the release line
|
||||
is), and for non-trivial changes `docs/ARCHITECTURE.md` plus the canonical
|
||||
document of the subsystem you touch: `SUN.md`, `LIGHT.md`, `CANVAS.md`,
|
||||
`WALL-THICKNESS.md`, `UX-MODES.md`, `CONFIG-COMPATIBILITY.md`,
|
||||
`TOUCH-SUPPORT.md`.
|
||||
**Reading order by role** (#634). `node scripts/entry-cost.mjs` measures each
|
||||
route and `test/entry-cost.test.mjs` keeps this list equal to its routes:
|
||||
|
||||
- author (analysis, spec, implementation, infrastructure): `docs/SCOPE.md` →
|
||||
`AGENTS.md` → `docs/process/AUTHOR.md` → `docs/STATUS.md`;
|
||||
- reviewer (spec or code): `docs/SCOPE.md` → `AGENTS.md` →
|
||||
`docs/process/REVIEWER.md`, then the issue body and its comments;
|
||||
- changing the pipeline, the gates or the process itself: `docs/SCOPE.md` →
|
||||
`AGENTS.md` → `PROCESS.md` → `docs/STATUS.md`.
|
||||
|
||||
The two digests quote and link `PROCESS.md` section by section; it stays the
|
||||
only complete canon and wins any disagreement, so open the linked section
|
||||
whenever a digest line governs your current step. For non-trivial changes add
|
||||
`docs/ARCHITECTURE.md` plus the canonical document of the subsystem you touch:
|
||||
`SUN.md`, `LIGHT.md`, `CANVAS.md`, `WALL-THICKNESS.md`, `UX-MODES.md`,
|
||||
`CONFIG-COMPATIBILITY.md`, `TOUCH-SUPPORT.md`.
|
||||
|
||||
Standard commands live in `package.json` scripts, `CONTRIBUTING.md` and
|
||||
`docs/DEVELOPMENT.md`.
|
||||
@@ -133,6 +144,17 @@ Release: v1.62.0-beta.9
|
||||
Baseline-Reviewed: https://github.com/Matysh/houseplan-card/actions/runs/<run-id>
|
||||
```
|
||||
|
||||
or, for a complete attested capture made in the repository's WSL/ext4 clone:
|
||||
|
||||
```text
|
||||
Release: v1.62.0-beta.9
|
||||
Baseline-Reviewed-Local: sha256:<wsl-attestation hash>
|
||||
```
|
||||
|
||||
Exactly one of the two `Baseline-Reviewed*` trailers is allowed. The local hash
|
||||
must equal `localAttestation.sha256` in the committed baseline index; it does not
|
||||
replace the mandatory full GitHub Validate on the final exact SHA.
|
||||
|
||||
Never invent a review link and never rewrite published history to satisfy
|
||||
trailers. `.githooks/commit-msg` and the `provenance` CI job both run
|
||||
`scripts/validate-commit-provenance.mjs`.
|
||||
@@ -162,8 +184,10 @@ remaining work is the rebase. When the conflict is only in the committed bundle
|
||||
(`dist/**`, `custom_components/houseplan/frontend/**` — the usual case when two
|
||||
tasks built it in parallel), run `node scripts/rebase-on-dev.mjs` (#479): it takes
|
||||
`dev`'s copy through the rebase, rebuilds with `npm run bundle:sync` and amends
|
||||
the result into your last commit; a conflict anywhere else aborts and leaves the
|
||||
tree as it was. Then push the branch and re-apply `S7-code-review`. When the
|
||||
the result into your last commit; a conflict in `docs/reviews/INDEX.md` is
|
||||
rebuilt from the directory (#643, the same helper the pipeline uses, so the
|
||||
pipeline no longer bounces a task on it); a conflict anywhere else aborts and
|
||||
leaves the tree as it was. Then push the branch and re-apply `S7-code-review`. When the
|
||||
only difference from the reviewed material is the pipeline's own review-document
|
||||
commit, the next run re-applies the green verdict without calling the model
|
||||
(#499); any other change to the tree — a rebase included — gets a full review.
|
||||
@@ -297,8 +321,11 @@ concealment, not repair; the exception is a defect proven to be **in the fixture
|
||||
as on #89, where the sun sat at azimuth 180° and the only window faced north, so no
|
||||
ray was ever built.
|
||||
|
||||
Baselines are still accepted only via `npm run golden:accept -- --reviewed` on a
|
||||
complete Linux CI artefact. "So the gate goes green" is not a reason.
|
||||
Baselines are still accepted only via `npm run golden:accept -- --reviewed` from
|
||||
either a complete Linux CI artefact or a complete attested WSL artefact produced
|
||||
by `npm run golden:wsl:capture`. "So the gate goes green" is not a reason, and
|
||||
the WSL route never replaces the independent full GitHub Validate on the final
|
||||
exact SHA.
|
||||
|
||||
The exchange happens in **issue comments** — there is no local message bus. Verdict
|
||||
format:
|
||||
@@ -381,7 +408,7 @@ npm run bundle:budget # initial View graph <= 256000 B gzip (#337)
|
||||
```
|
||||
|
||||
`npm run gate:small` runs the mandatory part of PROCESS §8 in one go (#479,
|
||||
#576): build with typecheck, `no-new-any` and `smoke-select` start in parallel;
|
||||
#576): build with typecheck, `no-new-any`, `no-new-private-writes` and `smoke-select` start in parallel;
|
||||
unit tests follow the completed build because their bundle-contract witnesses
|
||||
read the freshly produced `dist`, then the bundle-tree comparison and the
|
||||
bundle budget run. It prints the smokes the
|
||||
@@ -403,7 +430,7 @@ push (#479).** `smoke`, `golden` and `performance_smoke` in Validate are gated
|
||||
on the `heavy` output: true for a head commit carrying a `Release:` trailer, for
|
||||
`workflow_dispatch full=true` (which `nightly.yml` issues on `dev` every night)
|
||||
and for pull requests. A plain push to `dev` runs preflight, frontend (types,
|
||||
units, build, bundle sync, no-new-any), the narrow TS/Python geometry parity
|
||||
units, build, bundle sync, no-new-any, no-new-private-writes), the narrow TS/Python geometry parity
|
||||
guard when its inputs changed, backend, hacs and hassfest. Screenshot
|
||||
freshness in `check-docs` is likewise a warning on a plain push and an error on
|
||||
the candidate; `publish-prerelease.yml` and `release.yml` refuse a candidate
|
||||
@@ -418,6 +445,11 @@ demo/smoke_<name>.mjs`. A red smoke that reaches the review costs a cycle; run
|
||||
locally it costs a minute. Precedent: on #89 a fixture error lived through a
|
||||
whole review round that a local run would have caught immediately.
|
||||
|
||||
**A smoke enters through the public surface (#629)**: contract `data-hp` hooks,
|
||||
HA/fixture events and the harness facade `window.__hpTest`
|
||||
(`docs/TESTING.md`); private card fields are read-only in assertions, and a new
|
||||
write needs `// private-ok: <reason>` or the `no-new-private-writes` gate fails.
|
||||
|
||||
**One handoff, one push (#510).** Run `node scripts/process-gate.mjs --issues`
|
||||
locally with `gh` available before pushing (without `gh` the hook cannot check the
|
||||
issue status and stays silent). After `S7-code-review` do not push to the branch
|
||||
@@ -425,10 +457,31 @@ until the verdict or the return arrives: a push on top of a running review cance
|
||||
it (10–20 runner minutes) and, after the material is fixed, also the merge (#312).
|
||||
Set `S7` once per round, not after every CI fix: the pipeline now runs Validate
|
||||
with the diff mutants on the material itself and returns a red one to `S6` without
|
||||
spending a review cycle. Mutants by diff no longer run on ordinary pushes — only
|
||||
on the review candidate, the merge candidate, the beta candidate, PRs and the
|
||||
nightly run — so a routine push costs ~3 minutes; 08–09.09 they cost 48 of 56
|
||||
Validate job-hours and were mostly cancelled by the next push.
|
||||
spending a review cycle; since #636 it does not sleep while Validate runs — the
|
||||
prepare stage leaves a sealed `review-pending` marker and exits, and the
|
||||
completion of Validate (`process-resume.yml`, `workflow_run`) re-applies `S7`
|
||||
so a fresh run finds the finished dispatch; `process-reconcile.yml` is the
|
||||
fallback for a lost event. A second `S7` from the pipeline itself is therefore
|
||||
normal and is not a new round. Mutants by diff run only where they are explicitly
|
||||
requested — the review candidate, the merge candidate (both dispatch Validate
|
||||
with `mutants=true`) and PRs (#510, #601). Ordinary pushes, the beta candidate
|
||||
(`Release:` trailer) and `full=true` do not request them: a routine push costs
|
||||
~3 minutes (08–09.09 mutants cost 48 of 56 Validate job-hours and were mostly
|
||||
cancelled by the next push), and by the beta every issue has already been
|
||||
mutated twice — on review and on the rebased merge candidate; the night runs
|
||||
the full registry (`mutation-gate.yml`), not a diff subset.
|
||||
|
||||
**Workflows run from the default branch are thin callers (#623).** For
|
||||
`issues`, `schedule` and `workflow_run` GitHub executes the file from `main`.
|
||||
The six such files (`process.yml`, `process-resume.yml`,
|
||||
`process-reconcile.yml`, `mutation-gate.yml`, `nightly.yml`,
|
||||
`process-metrics.yml`) carry only triggers, run-name, permissions and
|
||||
concurrency and call their body `_<name>.yml` at `@dev` with
|
||||
`secrets: inherit`. A pipeline change is one commit to `dev` — edit the
|
||||
`_<name>.yml` body; no mirror into `main`, no merge-back before promotion.
|
||||
Only a change of triggers, dispatch inputs or the permission ceiling touches the
|
||||
thin file, and then it is mirrored into `main` (preflight `workflow_sync`
|
||||
compares exactly these six; `PROCESS.md` §10.4).
|
||||
|
||||
The full smoke set, `golden` and `performance_smoke` still belong to the
|
||||
pre-beta run — which is then mandatory and complete. WSL runs of the full HA
|
||||
@@ -449,10 +502,16 @@ so no frame ever matches an accepted baseline byte for byte, no environment
|
||||
witness can exist, and acceptance would refuse anyway (#401 accepts any
|
||||
environment that proves itself with byte-identical undeclared frames — Linux is
|
||||
simply the only one we have). The deliberate override is
|
||||
`HP_ALLOW_FOREIGN_CAPTURE="reason"`; the reason travels into the output and the
|
||||
manifest. Baselines are still accepted only via
|
||||
`npm run golden:accept -- --reviewed` on a complete artefact, and the accepted
|
||||
index records the platform next to the Chromium build. The one local
|
||||
`HP_ALLOW_FOREIGN_CAPTURE="reason"`; the reason travels into the output and the
|
||||
manifest. Baselines are still accepted only via
|
||||
`npm run golden:accept -- --reviewed` on a complete artefact. A local Linux
|
||||
capture additionally requires the self-hashed WSL passport produced by
|
||||
`npm run golden:wsl:capture`; plain `golden:capture` remains diagnostic. The
|
||||
passport proves a clean published branch SHA, WSL/ext4, pinned toolchain,
|
||||
current fingerprint, complete matrix, PNG hashes and witness floor. The
|
||||
accepted index records that local provenance separately from a GitHub run,
|
||||
and a final full GitHub Validate on the accepted exact SHA remains mandatory.
|
||||
The one local
|
||||
shortcut is `npm run docs:accept -- --identical` (#512): it re-captures on this
|
||||
machine, compares decoded pixels with the committed frames and, only when every
|
||||
frame is identical, refreshes the manifest fingerprints — frames that differ go
|
||||
@@ -464,9 +523,11 @@ sets it.
|
||||
**Backend.** A full Home Assistant harness cannot run on native Windows at all:
|
||||
Home Assistant imports the Unix-only `fcntl` module. Its canon is Linux CI or WSL.
|
||||
Locally only the pure subset runs; `python -m pytest tests_backend/ -q` without
|
||||
Home Assistant **silently skips** `test_ha_*.py` (`conftest.py` ignores them when
|
||||
`homeassistant` is not importable), so a green result proves nothing. Say so in the
|
||||
report instead of claiming the backend was verified. Cloud agents have the harness
|
||||
Home Assistant **does not collect** `test_ha_*.py` at all (`conftest.py`
|
||||
`collect_ignore_glob` when `homeassistant` is not importable — not a skip) and, since
|
||||
#630, prints `HA harness NOT collected: N files (M tests)` with the canon to run
|
||||
them. A green result there proves nothing about the harness. Say so in the report
|
||||
instead of claiming the backend was verified. Cloud agents have the harness
|
||||
at `.venv-backend/bin/python`.
|
||||
|
||||
**Running the app / smoke suite**: build a fresh bundle and copy it into the demo
|
||||
@@ -475,9 +536,12 @@ required: `demo/srv/demo.html` stubs `hass`, registries and `callService`.
|
||||
|
||||
**Golden images**: `npm run golden:capture` and `npm run golden:verify` refuse a
|
||||
stale demo bundle. Build and copy first, then review `artifacts/golden/actual/` and
|
||||
`diff/`. Update baselines only with `npm run golden:accept -- --reviewed`, using the
|
||||
complete Linux CI artifact; never accept a partial scenario or images merely to make
|
||||
CI green. See `demo/golden/README.md`.
|
||||
`diff/`. Update baselines only with `npm run golden:accept -- --reviewed`, using a
|
||||
complete Linux CI artifact or the attested artefact made in the WSL/ext4 clone by
|
||||
`npm run golden:wsl:capture`; never accept a partial scenario or images merely to
|
||||
make CI green. The local route saves the first expected-red CI round trip only:
|
||||
the accepted commit still needs a full GitHub Validate on its exact SHA. See
|
||||
`demo/golden/README.md`.
|
||||
|
||||
**Freshness contract**: the embedded fingerprint covers `src/` plus Rollup,
|
||||
TypeScript and package-lock build inputs. Every browser check must verify it
|
||||
@@ -503,7 +567,11 @@ development cycle never run E2E. Stable installable assets (`houseplan.zip`,
|
||||
`houseplan-card.js` and their `SHA256SUMS`) reach the public stable release only
|
||||
from `release.yml` after those gates; a release published by hand is turned back
|
||||
into a draft first (#540). The prerelease publisher additionally ships a
|
||||
candidate-bound `RELEASE-MEMBERSHIP.json` covered by the same passport (#547).
|
||||
candidate-bound `RELEASE-MEMBERSHIP.json` covered by the same passport (#547). Before
|
||||
every stable release `release.yml` also queues an independent review of the
|
||||
whole beta line (`release-review.yml`, `PROCESS.md` §11.5, #638): no specs, no
|
||||
review rounds, output `docs/reviews/RELEASE-REVIEW-vX.Y.Z.md`; it runs in
|
||||
parallel and never blocks the release.
|
||||
|
||||
**"Verified" without a named command and its result is not evidence.**
|
||||
|
||||
|
||||
@@ -38,6 +38,17 @@ an eager import. Extend the runtime, manifest, file/key/placeholder parity and
|
||||
regional-locale tests together; never bypass the registry by importing a locale
|
||||
directly in a component.
|
||||
|
||||
Administrator-only copy lives in three lazy namespace dictionaries —
|
||||
`src/i18n/settings/`, `src/i18n/support/` and `src/i18n/topology/` (#627). Their
|
||||
English file is static (the synchronous fallback); every other language is one
|
||||
lazy chunk per namespace × language: a two-line loader module
|
||||
`src/i18n/<namespace>/<namespace>-<code>.ts`, one `import()` with a
|
||||
content-hashed retry token in `src/i18n/<namespace>.ts`, and one entry in
|
||||
`NAMESPACE_LOCALE_CHUNKS` in `scripts/bundle-manifest.mjs`. The lazy surfaces
|
||||
(onboarding, editor runtime, Zigbee overlay) wait for their dictionaries before
|
||||
painting; never import a non-English namespace JSON statically — the budget
|
||||
gate and `test/i18n-lazy-namespaces.test.mjs` refuse it.
|
||||
|
||||
The current `subst()` helper does not implement plural rules. Phrase strings so
|
||||
their grammar does not depend on the numeric value (for example, use a neutral
|
||||
label followed by `{n}` rather than an English singular/plural pair).
|
||||
|
||||
@@ -12,6 +12,14 @@
|
||||
> репозитории: до августа 2026 канон лежал только в папке владельца, и свежий клон
|
||||
> его не содержал вовсе.
|
||||
>
|
||||
> **Ролевые конспекты** (#634): `docs/process/AUTHOR.md` и
|
||||
> `docs/process/REVIEWER.md` — выжимки этого файла со ссылками на его разделы.
|
||||
> Автор и ревьюер входят через них (порядок чтения по роли — `AGENTS.md`) и
|
||||
> открывают раздел канона, когда пункт конспекта касается текущего шага.
|
||||
> Правил конспекты не добавляют; при расхождении побеждает этот файл. Ссылки и
|
||||
> ключевые формулировки конспектов сверяет `test/process-digests.test.mjs`,
|
||||
> поэтому правка формулировки здесь правит и конспект тем же коммитом.
|
||||
>
|
||||
> **Приоритет источников.** Канонический бэклог — GitHub Issues; статус живёт в
|
||||
> метках и больше нигде: Project v2 не используется. При расхождении
|
||||
> документации с GitHub побеждает GitHub. Этот файл — единственный полный канон
|
||||
@@ -263,6 +271,29 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready →
|
||||
fix-up. Серверный стопор — шаг слияния конвейера сверяет вершину ветки с
|
||||
SHA материала ревью (допустим только собственный doc-коммит публикации
|
||||
поверх) и при расхождении отменяет слияние с возвратом в `S6-in-progress`.
|
||||
- **Контракты по монолиту — исполнением, не regex по тексту (#624).**
|
||||
Новое утверждение о `src/houseplan-card.ts` или
|
||||
`src/houseplan-editor-runtime.ts` доказывается экспортом функции и её
|
||||
вызовом в `test-build`, а не поиском строки в исходнике: текстовый якорь
|
||||
краснеет на переносе метода без единой регрессии, и это делает вынос дороже,
|
||||
чем оставить монолит как есть. Список тестов, читающих монолит как текст,
|
||||
заморожен (`test/monolith-text-anchors.test.mjs`, 54 файла на 23.09.2026) и
|
||||
может только уменьшаться; новое имя в нём — находка ревью, а не запись в
|
||||
список. Связность монолита измеряется шестью числами
|
||||
(`scripts/monolith-metrics.mjs`: делегаты, члены порта, `host.`, приватные
|
||||
члены порта и харнесса, байты `dist/`), база — `scripts/monolith-baseline.json`;
|
||||
гейт `npm run lint:unused` (в `gate:small` и Validate после сборки) красит
|
||||
рост любого из них и любой мёртвый код по `noUnusedLocals` вне порта и
|
||||
харнесса. Снижение фиксируется тем же коммитом
|
||||
(`node scripts/unused-locals-gate.mjs --update`); рост — только с записью в
|
||||
issue задачи и правкой базы в том же коммите.
|
||||
- **Смок входит в сценарий через публичную поверхность (#629).** DOM с
|
||||
контрактными хуками, события HA и фикстуры, тестовый фасад `window.__hpTest`
|
||||
(`docs/TESTING.md`, «Тестовый фасад и приватное состояние»). Приватное поле
|
||||
карточки — только для чтения в ассертах. Новая запись в него без
|
||||
`// private-ok: <конкретная причина>` — находка ревью, даже если гейт
|
||||
`no-new-private-writes` её не увидел (мутация через вызов, запись через
|
||||
псевдоним).
|
||||
- **Выход:** очередь на пре-релиз либо возврат в «В разработке», не более
|
||||
4 циклов (§4). Второй и последующие циклы разбираются по дельте (§2.10).
|
||||
|
||||
@@ -329,6 +360,34 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready →
|
||||
проверки, со ссылкой на документ того раунда и его материал. Без перечня
|
||||
сокращение превращается в молчаливое доверие.
|
||||
|
||||
**Индекс документов ревью** — `docs/reviews/INDEX.md` (#635): одна строка на
|
||||
документ — issue, этап, раунд, вердикт, число High/Medium, заголовки находок,
|
||||
файлы из находок (искать по имени файла: `grep form-kit docs/reviews/INDEX.md`).
|
||||
Файл генерируется `node scripts/reviews-index.mjs` и пересобирается конвейером
|
||||
тем же коммитом, что публикует документ, а также после каждого его ребейза
|
||||
(приведение к dev перед ревью, слияние кандидата — `--commit-if-stale`, коммит
|
||||
класса C); руками не правится. Конфликт ребейза, в котором **все** пути —
|
||||
`INDEX.md`, отказом не считается (#643): `scripts/rebase-generated.mjs`
|
||||
пересобирает индекс по каталогу на остановке и продолжает ребейз — так делают
|
||||
приведение к dev, слияние кандидата и авторский `rebase-on-dev.mjs`; индекс
|
||||
вместе с любым другим путём — прежний отказ с перечнем файлов. Шаг Validate
|
||||
«индекс ревью совпадает с каталогом» красит push в `dev`, где `INDEX.md`
|
||||
расходится с каталогом (на issue-ветках не судится: их переписывает конвейер).
|
||||
Правка `docs/reviews/`
|
||||
руками — перенос в `legacy/`, удаление — сопровождается
|
||||
`node scripts/reviews-index.mjs` в том же коммите. Прежде чем брать
|
||||
задачу по подсистеме, стоит прочитать её строки в индексе: что находили и чем
|
||||
закрывали — там, а не в тысяче файлов. Уроки, пережившие свою задачу,
|
||||
собираются в `docs/LESSONS.md` с датой и ссылкой на источник.
|
||||
|
||||
**Хранение документов** (решение владельца 23.09, #635): в `docs/reviews/`
|
||||
лежат все раунды всех задач текущей линии — предыдущие раунды нужны ссылкам
|
||||
«Унаследовано из r<N−1>» и якорям материала. При стабильном релизе документы
|
||||
задач, вошедших в него (`RELEASE-MEMBERSHIP.json` линии), переносятся в
|
||||
`legacy/reviews/<vX.Y.Z>/` одним коммитом класса C; индекс пересобирается и
|
||||
перечисляет только живые. Перенос — часть чеклиста стабильного релиза, не
|
||||
отдельная задача.
|
||||
|
||||
Дешёвые гейты (`typecheck`, `test`, `build` со сверкой копий бандла) гоняются в
|
||||
каждом раунде: код изменился, а стоят они минуты. Тяжёлые — по дельте (§10.2).
|
||||
|
||||
@@ -376,9 +435,14 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready →
|
||||
11. **Документация — в том же коммите, что поведение.** Отдельным «допишу потом»
|
||||
коммитом документация не бывает.
|
||||
12. **Сгенерированное не коммитится само по себе.** Только релизный промоушен или
|
||||
принятие эталонов со ссылкой на прогон CI.
|
||||
принятие эталонов с доказательством ревью: URL Linux CI run либо хеш
|
||||
аттестованного WSL-артефакта.
|
||||
13. **Golden-эталоны принимаются только** `npm run golden:accept -- --reviewed` по
|
||||
полному Linux-артефакту. Принятие ради зелёного CI — нарушение процесса.
|
||||
полному Linux-артефакту: либо GitHub CI, либо `npm run golden:wsl:capture` в
|
||||
WSL/ext4 с clean опубликованным SHA и машинно-проверяемым паспортом. Второй
|
||||
путь убирает только первый ожидаемо красный CI-прогон; полный GitHub Validate
|
||||
на точном SHA коммита с эталонами остаётся обязательным. Принятие ради
|
||||
зелёного CI — нарушение процесса.
|
||||
14. **Issue закрывается после выпуска беты** с зелёным CI на точном SHA. Не
|
||||
раньше, не «по факту наличия кода», не исполнителем.
|
||||
15. **Закрытый issue не переоткрывается.** Новый дефект — новый issue со ссылкой.
|
||||
@@ -821,7 +885,10 @@ Performance зелёные на точном SHA, плюс зелёный E2E н
|
||||
|
||||
- **`commit-msg`** — есть, работает. Отклоняет коммит без терминального
|
||||
`Issue: #NN`, требует ровно один `User-Visible: yes|no`, а для коммитов,
|
||||
трогающих `demo/golden/baselines/**`, — `Release:` плюс `Baseline-Reviewed:`.
|
||||
трогающих `demo/golden/baselines/**`, — `Release:` плюс ровно один источник:
|
||||
`Baseline-Reviewed: <URL GitHub run>` либо
|
||||
`Baseline-Reviewed-Local: sha256:<хеш аттестации>`. Локальный хеш обязан
|
||||
совпадать с `localAttestation.sha256` в принятом индексе.
|
||||
Реализация — `scripts/validate-commit-provenance.mjs`, тот же скрипт вызывается
|
||||
job `provenance` в `validate.yml`.
|
||||
- **`pre-push`** — есть, работает. Прогоняет `scripts/process-gate.mjs` по каждому
|
||||
@@ -866,8 +933,9 @@ Performance зелёные на точном SHA, плюс зелёный E2E н
|
||||
Добавление нового файла в `docs/specs/**` тоже предупреждение: каталог
|
||||
заморожен (#517);
|
||||
4. `User-Visible: yes` → правки в обоих changelog в том же коммите;
|
||||
5. коммит только класса D невалиден без `Release: vX.Y.Z` либо
|
||||
`Baseline-Reviewed: <ссылка на прогон CI>`;
|
||||
5. коммит только класса D невалиден без `Release: vX.Y.Z`,
|
||||
`Baseline-Reviewed: <ссылка на прогон CI>` либо
|
||||
`Baseline-Reviewed-Local: sha256:<хеш аттестации>`;
|
||||
6. релизный коммит не содержит изменений в `src/` и `custom_components/**/*.py`;
|
||||
7. документов ревью на один issue не больше четырёх (`-r1`…`-r4`).
|
||||
|
||||
@@ -929,7 +997,7 @@ SHA. Manifest публикуется и входит в `SHA256SUMS`; свежа
|
||||
|
||||
### 10.4 Событийный конвейер: метка как триггер
|
||||
|
||||
`.github/workflows/process.yml`, issue #114. Смена статусной метки — не запись в
|
||||
`.github/workflows/process.yml` (тело — `_process.yml`, #623), issue #114. Смена статусной метки — не запись в
|
||||
журнал, а **сообщение**: она порождает событие, событие запускает следующий шаг.
|
||||
|
||||
```
|
||||
@@ -939,8 +1007,9 @@ S7-code-review → код-ревью → слияние в dev → S8-merged л
|
||||
|
||||
Текущая техническая реализация независимого ревьюера —
|
||||
`anthropics/claude-code-action`; это деталь автоматизации, а не закрепление роли
|
||||
или вида задач за Claude. Ревьюер читает `docs/SCOPE.md`, `AGENTS.md`, этот
|
||||
документ и тело issue, публикует разбор комментарием, заводит issue на
|
||||
или вида задач за Claude. Ревьюер читает `docs/SCOPE.md`, `AGENTS.md`,
|
||||
конспект `docs/process/REVIEWER.md` с разделами этого документа по его ссылкам
|
||||
(#634) и тело issue, публикует разбор комментарием, заводит issue на
|
||||
Medium-находки вне скоупа задачи (#202), кладёт документ в `docs/reviews/` ветки
|
||||
задачи и возвращает вердикт структурированным JSON. **Метку переставляет отдельная
|
||||
детерминированная стадия по вердикту, а не модель.**
|
||||
@@ -950,27 +1019,63 @@ Medium-находки вне скоупа задачи (#202), кладёт до
|
||||
1. метки переставляет **PAT**, а не `GITHUB_TOKEN`: GitHub намеренно не порождает
|
||||
события от `GITHUB_TOKEN`, чтобы не было циклов, и цепочка обрывалась бы после
|
||||
первого шага без ошибок в логах;
|
||||
2. `process.yml` обязан лежать в **ветке по умолчанию**: для события `issues`
|
||||
GitHub берёт workflow только оттуда, независимо от содержимого `dev`;
|
||||
2. для события `issues` GitHub берёт workflow только из **ветки по умолчанию**
|
||||
(`main`), независимо от содержимого `dev`. Поэтому в `main` лежит тонкий
|
||||
`process.yml` — триггер, run-name, потолок прав, — а тело `_process.yml` он
|
||||
вызывает по ссылке `@dev` (#623). Правило ниже, «Workflow из ветки по
|
||||
умолчанию», — общее для всех таких файлов;
|
||||
3. слияние в `dev` происходит **до** простановки `S8-merged`, иначе метка врёт в
|
||||
промежутке — она утверждает, что код в `dev`;
|
||||
4. многострочный текст внутри `run:` — только через heredoc: строка с нулевым
|
||||
отступом обрывает блок YAML, и скрипт обрезается без ошибки парсера.
|
||||
|
||||
**Workflow из ветки по умолчанию: тонкий файл и тело из `dev`** (#623). Для
|
||||
событий `issues`, `schedule` и `workflow_run` GitHub исполняет workflow из
|
||||
`main`. Таких файлов шесть: `process.yml`, `process-resume.yml`,
|
||||
`process-reconcile.yml`, `mutation-gate.yml`, `nightly.yml`,
|
||||
`process-metrics.yml`. Каждый — тонкий вызывающий: триггеры, run-name,
|
||||
права, concurrency и одна job `uses:
|
||||
Matysh/houseplan-card/.github/workflows/_<имя>.yml@dev` с `secrets: inherit`.
|
||||
Тело `_<имя>.yml` читается из `dev` в момент запуска, поэтому **правка
|
||||
конвейера — один коммит в `dev`**, зеркало в `main` и возврат `main` в `dev`
|
||||
перед промоушеном не нужны. Потолок прав вызывающей job равен объединению
|
||||
прав job тела: вызываемый workflow права только сужает, и каждая job тела
|
||||
получает прежний минимум (#556). Тонкий файл меняется, только когда меняются
|
||||
триггеры, входы ручного запуска или потолок прав; тогда он зеркалится в
|
||||
`main`, и preflight `workflow_sync` в `validate.yml` держит копии равными —
|
||||
сверяются ровно эти шесть файлов, список держит
|
||||
`test/default-branch-workflows.test.mjs`. `performance.yml` в список не входит:
|
||||
по расписанию он судит `main` собственным телом из `main`.
|
||||
|
||||
**Ревью не начинается на красном коде** (#510). После фиксации материала конвейер
|
||||
запускает Validate с мутантами по диффу на этом SHA (`scripts/validate-gate.mjs`:
|
||||
`workflow_dispatch validate.yml -f mutants=true`) и ждёт его до 45 минут. Красный или
|
||||
`workflow_dispatch validate.yml -f mutants=true`). **Ждёт его не раннер, а событие**
|
||||
(#636): подготовка убеждается, что dispatch встал на материал, кладёт запечатанный
|
||||
маркер ожидания `review-pending-…` и завершается; по завершении Validate
|
||||
`process-resume.yml` (`workflow_run`) переставляет метку `S7-code-review`, и новый
|
||||
прогон конвейера находит завершённый dispatch сразу. Страховка на потерянное
|
||||
событие — `process-reconcile.yml`: успешный прогон подготовки с маркером и уже
|
||||
завершённым Validate он будит повторной меткой, без маркера — как прежде, только
|
||||
диагностика. Будить без маркера нельзя: это второй вызов модели. Красный или
|
||||
пропавший прогон возвращает задачу в `S6-in-progress` с комментарием и ссылкой —
|
||||
код никто не читал, цикл ревью не израсходован. Мутанты по диффу вообще бегут
|
||||
только по запросу: на кандидате ревью, кандидате слияния (#492), кандидате беты,
|
||||
в ночном прогоне и на PR; обычный push обходится дешёвыми гейтами (~3 минуты).
|
||||
За 08–09.09 мутанты на каждом промежуточном пуше стоили 48 из 56 часов
|
||||
job-минут Validate и в основном отменялись следующим пушем.
|
||||
только по явному запросу: на кандидате ревью, кандидате слияния (#492) — оба
|
||||
диспатчат Validate с `mutants=true` — и на PR, где Validate единственный сигнал;
|
||||
обычный push обходится дешёвыми гейтами (~3 минуты). За 08–09.09 мутанты на
|
||||
каждом промежуточном пуше стоили 48 из 56 часов job-минут Validate и в основном
|
||||
отменялись следующим пушем. **Кандидат беты (`Release:`), `full=true` и ночь
|
||||
мутантов не запрашивают** (#601, решение владельца 20.09): мутационный гейт
|
||||
проверяет тесты, а не продукт (#513), к бете каждая задача прогнана им дважды —
|
||||
на ревью и на слитом после ребейза кандидате, — а ночью идёт полный реестр
|
||||
(`mutation-gate.yml`, 01:00 UTC). Релизный гейт (#541) требует полного Validate,
|
||||
но не mutant-jobs; для ревью и слияния шесть исполненных mutant-jobs остаются
|
||||
обязательными.
|
||||
|
||||
Ожидание gates, работа модели и публикация/интеграция — три независимых jobs
|
||||
(#551) с отдельными бюджетами 55, 45 и 55 минут. Поэтому долгий Validate не
|
||||
съедает время модели, а ожидание кандидата после зелёного вердикта не обрывает
|
||||
готовый review. Между jobs передаётся запечатанный artifact: run/attempt, issue,
|
||||
съедает время модели (с #636 — и не занимает раннер: до этого подготовка спала
|
||||
≈ 28 минут на раунд при 10–12 минутах работы модели), а ожидание кандидата после
|
||||
зелёного вердикта не обрывает готовый review. Между jobs передаётся запечатанный artifact: run/attempt, issue,
|
||||
этап, раунд, branch, SHA/tree материала, якоря ТЗ и результат Validate. Получатель
|
||||
сверяет полный набор файлов, SHA-256 и все поля с outputs предыдущей стадии;
|
||||
неполный, чужой или устаревший результат fail-closed не публикуется и не разрешает
|
||||
@@ -1150,7 +1255,9 @@ Golden, браузерные смоки, performance и полный HA-харн
|
||||
- трейлеры на коммите как обычно, `Issue: #NN` того же issue;
|
||||
- при `User-Visible: yes` — правки в оба changelog в том же коммите;
|
||||
- эталоны golden принимаются только через `npm run golden:accept -- --reviewed`
|
||||
на полном артефакте Linux CI. «Чтобы гейт позеленел» основанием не является.
|
||||
на полном Linux-артефакте GitHub CI либо полном аттестованном WSL-артефакте;
|
||||
после локальной приёмки полный GitHub Validate на точном финальном SHA всё
|
||||
равно обязателен. «Чтобы гейт позеленел» основанием не является.
|
||||
|
||||
**Границы, за которыми исключение не действует.** Оно про починку названного
|
||||
гейтом дефекта, а не про продолжение разработки под видом починки. Правка идёт
|
||||
@@ -1173,6 +1280,50 @@ Golden, браузерные смоки, performance и полный HA-харн
|
||||
Это исключение из правила «код-ревью не пропускается никогда» (§5, §7.1) —
|
||||
единственное, и относится только к окну между `S8-merged` и выпуском.
|
||||
|
||||
### 11.5 Независимое ревью линии перед стабильным релизом
|
||||
|
||||
Решение владельца 2026-09-25, issue #638.
|
||||
|
||||
**Зачем.** Инкрементальное ревью судит дифф задачи против её ТЗ, а не
|
||||
поверхность против пользователя. Пять раундов по эпику #591 не нашли того, что
|
||||
нашли четыре независимых ревью перед аудитом 22.09: невидимый после крестика HA
|
||||
диалог (#607), кламп по символу (#608), маршруты робота при импорте (#611),
|
||||
детерминированный отказ релизного гейта (#619). Ни ветка `ha-dialog`, ни
|
||||
посимвольный ввод не входили ни в один AC.
|
||||
|
||||
**Шаг.** Перед каждым стабильным релизом — одно ревью поверхностей, изменённых
|
||||
всей линией бет, «с нуля»:
|
||||
|
||||
- **вход** — issue линии, доказанные трейлерами `Issue: #NN` в диапазоне
|
||||
«прошлый стабильный тег..кандидат» (тот же построитель и та же схема, что
|
||||
`RELEASE-MEMBERSHIP.json` беты, #547; метка S8 доказательством не является),
|
||||
и изменённые продуктовые файлы. Собирает их
|
||||
`scripts/release-review.mjs prepare`;
|
||||
- **без ТЗ и без документов раундов**: основа суждения — `docs/SCOPE.md` и
|
||||
`docs/USER-GUIDE.ru.md`. Проверка исполнением: бандл, стенд и смоки, пиннутая
|
||||
фикстура `ha-dialog` (#505) там, где есть диалоги, посимвольный ввод,
|
||||
настоящие Escape и крестик; для каждой поверхности — обычный сценарий и самый
|
||||
рискованный соседний (§2.6, шесть классов риска);
|
||||
- **выход** — `docs/reviews/RELEASE-REVIEW-vX.Y.Z.md` в `dev`, находки
|
||||
High/Medium/Low с воспроизведением.
|
||||
|
||||
**Исполнитель** — модель в CI, `.github/workflows/release-review.yml`: три job
|
||||
(вход, модель, публикация), модель без единого права на запись, документ, его
|
||||
машинный блок, индекс и коммит — детерминированный шаг. Независимость
|
||||
обеспечена построением: сессия свежая, ТЗ и раунды ей не даются.
|
||||
|
||||
**Выпуск не блокирует.** `release.yml` ставит ревью в очередь job
|
||||
`independent-review` сразу после закрепления SHA кандидата — параллельно
|
||||
гейтам; ни один job выпуска от него не зависит, его отказ — предупреждение.
|
||||
Документ — рекомендация: владелец берёт находки в работу (issue в очередь
|
||||
следующей беты) либо оставляет без действий. Автоматически находки в issue
|
||||
не превращаются.
|
||||
|
||||
Повторный запуск на тот же тег модель не тратит, если документ уже в `dev`
|
||||
(`force=true` — переснять). Ручной запуск:
|
||||
`gh workflow run release-review.yml --ref dev -f tag=vX.Y.Z [-f candidate=<sha>]`.
|
||||
Беты шаг пропускают. Первый прогон — линия v1.78.0.
|
||||
|
||||
---
|
||||
|
||||
## 12. Запрещено
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 JB (justbusiness)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,40 @@
|
||||
# House Plan furniture pack 0.4.1
|
||||
|
||||
Corrected derivative of the 0.4.0 built-in furniture artwork (#593, #606).
|
||||
|
||||
- `svg/menu/`: 33 front-view category illustrations, used only by the lazy
|
||||
editor bundle.
|
||||
- `svg/plan/`: 60 top-view drawings used on the plan.
|
||||
- `pack.json`: stable public ids, category links, default dimensions and names.
|
||||
The filename avoids the `*manifest.json` suffix reserved by HACS.
|
||||
|
||||
Corrections to 0.4.0:
|
||||
|
||||
1. The drawing in `plan/cactus.svg` is an exercise machine. It is unchanged
|
||||
geometrically and is now `plan/exercise.svg`, public id `exercise`, in the
|
||||
formerly hidden `exercise` category. Old saved `symbol: cactus` is resolved
|
||||
as a read-compatible alias by the application, not duplicated in this pack.
|
||||
2. The *contents* of `plan/bookshelf.svg` and `plan/shelf_floor.svg` were swapped
|
||||
so each filename and unchanged public id shows the named furniture item.
|
||||
3. All 33 menu categories now have a plan variant.
|
||||
4. `menu/dishwasher.svg` uses the replacement front-view illustration supplied
|
||||
by JustBusiness in [issue #640](https://github.com/Matysh/houseplan-card/issues/640).
|
||||
Its reviewed source archive is `dishwasher.zip`, SHA-256
|
||||
`1A9B1BDAF490B35812F51E4DD3CB1C425224EC768219C31B979DD6A84E890203`;
|
||||
the path geometry is unchanged apart from safe repository normalization.
|
||||
JustBusiness supplied this asset for House Plan to use, modify and distribute
|
||||
under the repository MIT License without separate UI attribution.
|
||||
|
||||
The original 0.4.0 set of 93 SVG drawings originated with Sergey Matyunin
|
||||
(`Matysh`). He granted House Plan permission to use, modify and distribute them
|
||||
under the repository MIT License without separate UI attribution in
|
||||
[issue #593](https://github.com/Matysh/houseplan-card/issues/593#issuecomment-5739841899).
|
||||
The reviewed source archive is `houseplan-furniture-0.4.0.zip`, attached to
|
||||
[issue #593](https://github.com/Matysh/houseplan-card/issues/593), SHA-256
|
||||
`69BA5E0C398542D59F24269F637F57B8EBF31C2836C9D493F084AD29AB299FDE`.
|
||||
This directory is a corrected derivative, not a new designer source archive.
|
||||
`LICENSE.md` carries `JB (justbusiness)`, the same author under his own domain;
|
||||
the authoritative name spelling is Sergey Matyunin, as decided in #369.
|
||||
|
||||
The editable source is linked from `pack.json`. Generated TypeScript must not
|
||||
be edited by hand; run `npm run furniture:generate` after changing this pack.
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M85 71.5 L85 63 M25 71.5 L25 63 M25 71.5 L89.5 71.5 C90.8807 71.5 92 70.3807 92 69 L92 41.5 C92 40.1193 90.8807 39 89.5 39 L20.5 39 C19.1193 39 18 40.1193 18 41.5 L18 69 C18 70.3807 19.1193 71.5 20.5 71.5 L25 71.5 Z M85 63 C85 61.6193 83.8807 60.5 82.5 60.5 L27.5 60.5 C26.1193 60.5 25 61.6193 25 63 M85 63 L85 66.5 L25 66.5 L25 63" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 516 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M77.5 48.5 L77.5 39 C77.5 33.4772 73.0228 29 67.5 29 L42.5 29 C36.9772 29 32.5 33.4772 32.5 39 L32.5 48.5205 M77.5 48.5 C80.8137 48.5 83.5 51.1863 83.5 54.5 C83.5 57.1123 81.8302 59.3334 79.5 60.1572 L79.5 71.5 C79.5 73.7091 77.7091 75.5 75.5 75.5 M77.5 48.5 C74.2576 48.5 71.617 51.072 71.5049 54.2871 C71.5012 54.3576 71.5 54.4286 71.5 54.5 L71.5 61.75 M38 61.75 L38 54 C38 53.2396 37.7873 52.5291 37.4189 51.9238 C36.5228 50.0421 34.6765 48.7002 32.5 48.5205 M32.5 48.5205 C32.3351 48.5069 32.1684 48.5 32 48.5 M75.5 75.5 L34 75.5 M75.5 75.5 L72.5 75.5 M34 75.5 C31.7909 75.5 30 73.7091 30 71.5 L30 60.1572 C27.6698 59.3334 26 57.1123 26 54.5 C26 51.1863 28.6863 48.5 32 48.5 M34 75.5 L36.5 75.5 M32 48.5 C34.3914 48.5 36.4549 49.8996 37.4189 51.9238 M38 61.75 C38 64.3734 40.1266 66.5 42.75 66.5 L66.75 66.5 C69.3734 66.5 71.5 64.3734 71.5 61.75 M38 61.75 C38 59.1266 40.1266 57 42.75 57 L66.75 57 C69.3734 57 71.5 59.1266 71.5 61.75 M36.5 75.5 L44.5 75.5 M36.5 75.5 L36.5 80.5 C36.5 81.3284 37.1716 82 38 82 L41.3921 82 C42.0506 82 42.6321 81.5705 42.8258 80.9411 L44.5 75.5 M44.5 75.5 L64.5 75.5 M72.5 75.5 L64.5 75.5 M72.5 75.5 L72.5 80.5 C72.5 81.3284 71.8284 82 71 82 L67.6079 82 C66.9494 82 66.3679 81.5705 66.1742 80.9411 L64.5 75.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M28 74.8457 L28 80 C28 80.8284 28.6716 81.5 29.5 81.5 L32.8921 81.5 C33.5506 81.5 34.1321 81.0705 34.3258 80.4411 L35.6923 76 M28.0191 74.8558 C28.0127 74.8524 28.0063 74.8491 28 74.8457 C25.4198 73.4833 23.483 71.0169 22.8673 67.9996 M19.5 51.5 L90 51.5 M19.5 51.5 C18.1193 51.5 17 50.3807 17 49 C17 47.6193 18.1193 46.5 19.5 46.5 M19.5 51.5 L22.8673 67.9996 M90 51.5 C91.3807 51.5 92.5 50.3807 92.5 49 C92.5 47.6193 91.3807 46.5 90 46.5 M90 51.5 L86.6327 67.9996 C85.9778 71.2088 83.8285 73.7948 81 75.0922 M90 46.5 L19.5 46.5 M90 46.5 L20 46.5 M19.5 46.5 L20 46.5 M22.8673 67.9996 C23.4846 71.0243 25.4293 73.4954 28.0191 74.8558 M30 36 L30 34 C30 32.6739 29.4732 31.4021 28.5355 30.4645 C27.5979 29.5268 26.3261 29 25 29 C23.6739 29 22.4021 29.5268 21.4645 30.4645 C20.5268 31.4021 20 32.6739 20 34 L20 46.5 M81 75.0922 C79.7247 75.6771 78.3114 76 76.8347 76 L73.3077 76 M81 75.0922 L81 80 C81 80.8284 80.3284 81.5 79.5 81.5 L76.1079 81.5 C75.4494 81.5 74.8679 81.0705 74.6742 80.4411 L73.3077 76 M73.3077 76 L35.6923 76 M35.6923 76 L32.6653 76 C31.1128 76 29.6303 75.643 28.3047 75 C28.2086 74.9534 28.1134 74.9053 28.0191 74.8558" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M16 75.5 L24 75.5 M16 75.5 L16 80.5 C16 81.3284 16.6716 82 17.5 82 L20.8921 82 C21.5506 82 22.1321 81.5705 22.3258 80.9411 L24 75.5 M16 75.5 L15 75.5 M24 75.5 L86.5 75.5 M94.5 75.5 L86.5 75.5 M94.5 75.5 L94.5 80.5 C94.5 81.3284 93.8284 82 93 82 L89.6079 82 C88.9494 82 88.3679 81.5705 88.1742 80.9411 L86.5 75.5 M94.5 75.5 L95.5 75.5 M88 50.1296 L88 31.5 C88 30.1193 86.8807 29 85.5 29 L25 29 C23.6193 29 22.5 30.1193 22.5 31.5 L22.5 50.1296 M87.9383 50.1068 C87.9589 50.1143 87.9795 50.1219 88 50.1296 C91.7967 51.5483 94.5 55.2085 94.5 59.5 M22.5617 50.1068 C22.5411 50.1143 22.5205 50.1219 22.5 50.1296 C18.7033 51.5483 16 55.2085 16 59.5 M94.5 65.5 L94.5 59.5 M94.5 65.5 L16 65.5 M94.5 65.5 L95.5 65.5 M16 65.5 L16 59.5 M16 65.5 L15 65.5 M84.5 49.5 L26 49.5 M84.5 49.5 L80 49.5 M94.5 59.5 C94.5 55.1853 91.7674 51.5088 87.9383 50.1068 C86.8662 49.7142 85.7081 49.5 84.5 49.5 M26 49.5 L30.5 49.5 M16 59.5 C16 55.1853 18.7326 51.5088 22.5617 50.1068 C23.6338 49.7142 24.7919 49.5 26 49.5 M15 75.5 L95.5 75.5 M15 75.5 C14.4477 75.5 14 75.0523 14 74.5 L14 66.5 C14 65.9477 14.4477 65.5 15 65.5 M95.5 75.5 C96.0523 75.5 96.5 75.0523 96.5 74.5 L96.5 66.5 C96.5 65.9477 96.0523 65.5 95.5 65.5 M95.5 65.5 L15 65.5 M51.5 49.5 L51.5 45.5 C51.5 42.7386 49.2614 40.5 46.5 40.5 L35.5 40.5 C32.7386 40.5 30.5 42.7386 30.5 45.5 L30.5 49.5 M51.5 49.5 L30.5 49.5 M51.5 49.5 L59 49.5 M80 49.5 L80 45.5 C80 42.7386 77.7614 40.5 75 40.5 L64 40.5 C61.2386 40.5 59 42.7386 59 45.5 L59 49.5 M80 49.5 L59 49.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M46 67.5 L64.5 67.5 M46 67.5 C40.4772 67.5 36 63.0228 36 57.5 L36 53 M46 67.5 C45.4902 67.5 44.9893 67.4618 44.5 67.3882 L44.5 79.5 C44.5 80.0523 44.9477 80.5 45.5 80.5 L65 80.5 C65.5523 80.5 66 80.0523 66 79.5 L66 67.3882 C65.5107 67.4618 65.0098 67.5 64.5 67.5 M64.5 67.5 C70.0229 67.5 74.5 63.0228 74.5 57.5 L74.5 53 M74.5 53 L74.5 47.5 C74.5 46.9477 74.0523 46.5 73.5 46.5 M74.5 53 L36 53 M36 53 L36 47.5 C36 46.9477 36.4477 46.5 37 46.5 M73.5 46.5 L37 46.5 M73.5 46.5 L60 46.5 M37 46.5 L50.5 46.5 M51.5 40 L59 40 M51.5 40 C50.9477 40 50.5 40.4477 50.5 41 L50.5 46.5 M51.5 40 L53 40 M59 40 C59.5523 40 60 40.4477 60 41 L60 46.5 M59 40 L57.5 40 M60 46.5 L50.5 46.5 M57.5 40 L57.5 30 C57.5 29.4477 57.0523 29 56.5 29 L54 29 C53.4477 29 53 29.4477 53 30 M57.5 40 L53 40 M53 40 L53 30 M53 40 L53 36 M53 30 L53 31.5 M53 31.5 L49 31.5 C48.4477 31.5 48 31.9477 48 32.5 L48 35 C48 35.5523 48.4477 36 49 36 L53 36 M53 31.5 L53 36" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M51.5 81.5 L51.5 86 C51.5 86.5523 51.0523 87 50.5 87 L47.5 87 C46.9477 87 46.5 86.5523 46.5 86 L46.5 81.5 M51.5 81.5 L46.5 81.5 M51.5 81.5 L59 81.5 M46.5 81.5 L44 81.5 M64 81.5 L64 86 C64 86.5523 63.5523 87 63 87 L60 87 C59.4477 87 59 86.5523 59 86 L59 81.5 M64 81.5 L59 81.5 M64 81.5 L66.5 81.5 M74.5 73 L74.5 73.5 C74.5 77.9183 70.9183 81.5 66.5 81.5 M44 81.5 C39.5817 81.5 36 77.9183 36 73.5 L36 73 L36 31 C36 26.5817 39.5817 23 44 23 L66.5 23 C70.9183 23 74.5 26.5817 74.5 31 L74.5 73 M74.5 73 L36 73 M66.5 81.5 L44 81.5 M55 42.5 L58.5 39 M62.5 42.5 C62.5 46.366 59.366 49.5 55.5 49.5 C51.634 49.5 48.5 46.366 48.5 42.5 C48.5 38.634 51.634 35.5 55.5 35.5 C59.366 35.5 62.5 38.634 62.5 42.5 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 880 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M41.25 60.5 L68.75 60.5 M68.75 60.5 C70.5449 60.5 72 59.0449 72 57.25 C72 55.6281 70.8119 54.2836 69.2585 54.0395 C69.0928 54.0135 68.923 54 68.75 54 L41.25 54 C41.077 54 40.9072 54.0135 40.7415 54.0395 C39.1881 54.2836 38 55.6281 38 57.25 C38 59.0449 39.4551 60.5 41.25 60.5 M41.25 60.5 L46 60.5 M68.75 60.5 L64 60.5 M64 60.5 L69.5 60.5 L69.5 82 C69.5 82.8284 68.8284 83.5 68 83.5 L67.3752 83.5 C66.5972 83.5 65.9483 82.9051 65.8809 82.1299 L64 60.5 Z M64 60.5 L46 60.5 M46 60.5 L40.5 60.5 L40.5 82 C40.5 82.8284 41.1716 83.5 42 83.5 L42.6248 83.5 C43.4028 83.5 44.0517 82.9051 44.1191 82.1299 L46 60.5 Z M40.7415 54.0395 C40.5847 53.5546 40.5 53.0372 40.5 52.5 L40.5 39.5 C40.5 32.5964 46.0964 27 53 27 L57 27 C63.9036 27 69.5 32.5964 69.5 39.5 L69.5 52.5 C69.5 53.0372 69.4153 53.5546 69.2585 54.0395" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 988 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M85.2381 55 L86.5 55 C87.0523 55 87.5 54.5523 87.5 54 L87.5 51 L84.6598 43.4847 C84.2586 42.5818 83.3633 42 82.3753 42 L26.6247 42 C25.6367 42 24.7414 42.5818 24.3402 43.4847 L21.5 51 L21.5 54 C21.5 54.5523 21.9477 55 22.5 55 L25 55 M87.5 51 L21.5 51 M25 55 L25 66.5 C25 67.3284 25.6716 68 26.5 68 L27.2113 68 C27.983 68 28.6288 67.4144 28.7041 66.6464 L29.8459 55 M25 55 L29.8459 55 M29.8459 55 L80.3922 55 M80.3922 55 L81.534 66.6464 C81.6093 67.4144 82.2551 68 83.0268 68 L83.7381 68 C84.5665 68 85.2381 67.3284 85.2381 66.5 L85.2381 55 M80.3922 55 L85.2381 55" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 748 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M43 65 L48 65 M43 65 L24.5 65 C23.1193 65 22 63.8807 22 62.5 L22 40.5 C22 39.1193 23.1193 38 24.5 38 L64.5 38 C65.8807 38 67 39.1193 67 40.5 L67 62.5 C67 63.8807 65.8807 65 64.5 65 L48 65 M43 65 L43 72 M48 65 L48 72 M37 72 L43 72 M54.0711 72 L48 72 M76 60 L84 60 M76 43 L84 43 M35 53 L35 46 L39.5 46 C40.4283 46 41.3185 46.3687 41.9749 47.0251 C42.6313 47.6815 43 48.5717 43 49.5 C43 50.4283 42.6312 51.3185 41.9749 51.9749 C41.3185 52.6313 40.4283 53 39.5 53 L35 53 Z M35 53 L35 56 M54.5355 54.5355 C53.8363 55.2348 52.9454 55.711 51.9755 55.9039 C51.0055 56.0969 50.0002 55.9978 49.0866 55.6194 C48.173 55.241 47.3921 54.6001 46.8427 53.7779 C46.2932 52.9556 46 51.9889 46 51 C46 50.0111 46.2932 49.0444 46.8427 48.2221 C47.3921 47.3999 48.173 46.759 49.0866 46.3806 C50.0002 46.0022 51.0055 45.9031 51.9755 46.0961 C52.9454 46.289 53.8363 46.7652 54.5355 47.4645 M48 72 L43 72 M74.5 72 L85.5 72 C86.8807 72 88 70.8807 88 69.5 L88 40.5 C88 39.1193 86.8807 38 85.5 38 L74.5 38 C73.1193 38 72 39.1193 72 40.5 L72 69.5 C72 70.8807 73.1193 72 74.5 72 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M25 31.5 C25 30.1193 26.1193 29 27.5 29 L82.5 29 C83.8807 29 85 30.1193 85 31.5 L85 78.5 C85 79.8807 83.8807 81 82.5 81 L27.5 81 C26.1193 81 25 79.8807 25 78.5 L25 31.5 Z M49.5 66.5 C49.5 70.9183 45.9183 74.5 41.5 74.5 C37.0817 74.5 33.5 70.9183 33.5 66.5 C33.5 62.0817 37.0817 58.5 41.5 58.5 C45.9183 58.5 49.5 62.0817 49.5 66.5 Z M76 43.5 C76 47.9183 72.4183 51.5 68 51.5 C63.5817 51.5 60 47.9183 60 43.5 C60 39.0817 63.5817 35.5 68 35.5 C72.4183 35.5 76 39.0817 76 43.5 Z M73 66.5 C73 69.2614 70.7614 71.5 68 71.5 C65.2386 71.5 63 69.2614 63 66.5 C63 63.7386 65.2386 61.5 68 61.5 C70.7614 61.5 73 63.7386 73 66.5 Z M49.5 43.5 C49.5 47.9183 45.9183 51.5 41.5 51.5 C37.0817 51.5 33.5 47.9183 33.5 43.5 C33.5 39.0817 37.0817 35.5 41.5 35.5 C45.9183 35.5 49.5 39.0817 49.5 43.5 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 963 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M26.875 47 L23.5 47 C22.9477 47 22.5 46.5523 22.5 46 L22.5 43 L24.8802 35.5939 C25.2539 34.633 26.1791 34 27.2102 34 L83.7898 34 C84.8209 34 85.7462 34.633 86.1198 35.5939 L88.5 43 L88.5 46 C88.5 46.5523 88.0523 47 87.5 47 L84.125 47 M22.5 43 L88.5 43 M78.875 47 L84.742 73.8205 C84.8926 74.5092 85.5024 75 86.2073 75 L86.7474 75 C87.6682 75 88.3716 74.1781 88.2294 73.2684 L84.125 47 M78.875 47 L84.125 47 M78.875 47 L32.125 47 M32.125 47 L26.258 73.8205 C26.1074 74.5092 25.4976 75 24.7927 75 L24.2526 75 C23.3318 75 22.6284 74.1781 22.7706 73.2684 L26.875 47 M32.125 47 L26.875 47" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 768 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M29 76C29 77.3807 30.1193 78.5 31.5 78.5H78.5C79.8807 78.5 81 77.3807 81 76M29 76V82.5C29 83.8807 30.1193 85 31.5 85H78.5C79.8807 85 81 83.8807 81 82.5V76M29 76V40M81 76V40M81 40C81 38.6193 79.8807 37.5 78.5 37.5H31.5C30.1193 37.5 29 38.6193 29 40M81 40V27.5C81 26.1193 79.8807 25 78.5 25H31.5C30.1193 25 29 26.1193 29 27.5V40M43 42H67M35.75 31H45.25M69.5 31C69.5 32.1046 68.6046 33 67.5 33C66.3954 33 65.5 32.1046 65.5 31C65.5 29.8954 66.3954 29 67.5 29C68.6046 29 69.5 29.8954 69.5 31ZM77 31C77 32.1046 76.1046 33 75 33C73.8954 33 73 32.1046 73 31C73 29.8954 73.8954 29 75 29C76.1046 29 77 29.8954 77 31Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 791 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M81 82.5 L81 27.5 M81 82.5 C81 83.8807 79.8807 85 78.5 85 L31.5 85 C30.1193 85 29 83.8807 29 82.5 L29 55.5 M81 82.5 L81 40 M81 27.5 C81 26.1193 79.8807 25 78.5 25 L31.5 25 C30.1193 25 29 26.1193 29 27.5 M81 27.5 L81 40 M29 27.5 L29 55.5 M29 27.5 L29 40 M81 40 C81 38.6193 79.8807 37.5 78.5 37.5 L31.5 37.5 C30.1193 37.5 29 38.6193 29 40 M29 40 L29 55.5 M35.75 31 L45.25 31 M55.9943 55 C55.9943 55 54.6654 56.8105 54.5153 58.1429 C54.3169 59.9042 56.1139 60.6968 55.9943 62.4643 C55.8942 63.9429 54.5153 66 54.5153 66 M60.4943 55 C60.4943 55 59.1654 56.8105 59.0153 58.1429 C58.8169 59.9042 60.6139 60.6968 60.4943 62.4643 C60.3942 63.9429 59.0153 66 59.0153 66 M50.9943 55 C50.9943 55 49.6654 56.8105 49.5153 58.1429 C49.3169 59.9042 51.1139 60.6968 50.9943 62.4643 C50.8942 63.9429 49.5153 66 49.5153 66 M72 60.5 C72 69.8888 64.3888 77.5 55 77.5 C45.6112 77.5 38 69.8888 38 60.5 C38 51.1112 45.6112 43.5 55 43.5 C64.3888 43.5 72 51.1112 72 60.5 Z M66.5 60.5 C66.5 66.8513 61.3513 72 55 72 C48.6487 72 43.5 66.8513 43.5 60.5 C43.5 54.1487 48.6487 49 55 49 C61.3513 49 66.5 54.1487 66.5 60.5 Z M69.5 31 C69.5 32.1046 68.6046 33 67.5 33 C66.3954 33 65.5 32.1046 65.5 31 C65.5 29.8954 66.3954 29 67.5 29 C68.6046 29 69.5 29.8954 69.5 31 Z M77 31 C77 32.1046 76.1046 33 75 33 C73.8954 33 73 32.1046 73 31 C73 29.8954 73.8954 29 75 29 C76.1046 29 77 29.8954 77 31 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M55.091 60.072 L65.7741 35.5156 M42.2119 56.2083 L55.091 60.072 L60.8511 61.8001 L66.5032 63.4957 C70.733 64.7646 73.6297 68.6579 73.6297 73.074 L73.6297 76.2635 L29.3385 76.2635 L29.3385 65.7866 C29.3385 60.9371 32.7227 57.0804 37.0256 56.051 C38.5108 55.6956 40.1055 55.6771 41.7081 56.0709 M41.7081 56.0709 C41.876 56.1122 42.044 56.1579 42.2119 56.2083 L60.8511 61.8001 M71.9311 35.5156 L76.4108 35.5156 C78.2451 35.5156 79.9321 34.5112 80.8064 32.8987 L83.8375 27.308 C84.3011 26.4529 83.9993 25.384 83.1569 24.8976 C82.2982 24.4018 81.2002 24.696 80.7045 25.5547 L77.8652 30.4723 C77.3293 31.4005 76.3389 31.9723 75.2671 31.9723 L65.6573 31.9723 C64.6788 31.9723 63.8856 32.7655 63.8856 33.744 C63.8856 34.7224 64.6788 35.5156 65.6573 35.5156 L65.7741 35.5156 M71.9311 35.5156 C71.9603 35.7673 71.9261 36.0298 71.8188 36.2793 L60.8511 61.8001 M71.9311 35.5156 L65.7741 35.5156 M41.7081 56.0709 L38.03 42.6022 M37.0256 56.051 L33.3529 42.6022 M38.03 42.6022 L33.3529 42.6022 L29.7814 42.6022 C28.5583 42.6022 27.5668 41.6107 27.5668 40.3876 C27.5668 39.1646 28.5583 38.1731 29.7814 38.1731 L41.2971 38.1731 C42.5202 38.1731 43.5117 39.1646 43.5117 40.3876 C43.5117 41.6107 42.5202 42.6022 41.2971 42.6022 L38.03 42.6022 Z M25.7952 78.921 C25.7952 77.4533 26.985 76.2635 28.4526 76.2635 L74.5155 76.2636 C75.9832 76.2636 77.173 77.4533 77.173 78.921 C77.173 80.3887 75.9832 81.5785 74.5155 81.5785 L28.4526 81.5785 C26.985 81.5785 25.7952 80.3887 25.7952 78.921 Z M29.3385 81.5785 L29.3385 84.5677 C29.3385 85.3962 30.01 86.0677 30.8385 86.0677 L33.6379 86.0677 C34.2512 86.0677 34.8028 85.6943 35.0306 85.1248 L36.5002 81.5785 L29.3385 81.5785 Z M73.6297 81.5785 L73.6297 84.5677 C73.6297 85.3962 72.9581 86.0677 72.1297 86.0677 L69.3303 86.0677 C68.717 86.0677 68.1654 85.6943 67.9376 85.1248 L66.5002 81.5785 L73.6297 81.5785 Z M45.2833 66.0766 C45.2833 68.7673 43.102 70.9486 40.4113 70.9486 C37.7205 70.9486 35.5392 68.7673 35.5392 66.0766 C35.5392 63.3858 37.7205 61.2045 40.4113 61.2045 C43.102 61.2045 45.2833 63.3858 45.2833 66.0766 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M71.1884 73.8943 L71.1884 50.4399 C71.1884 47.6784 68.9498 45.4399 66.1884 45.4399 L43.1445 45.4399 C40.3831 45.4399 38.1445 47.6784 38.1445 50.4399 L38.1445 73.8942 M38.1445 73.8942 L26.7031 73.8942 L26.7031 34.4252 L82.6629 34.4252 L82.6629 73.8942 L71.1884 73.8943 L38.1445 73.8942 Z M62.0101 62.8795 L47.3239 62.8795 C45.8031 62.8795 44.5703 64.1124 44.5703 65.6332 C44.5703 67.154 45.8031 68.3868 47.3239 68.3868 M47.3239 68.3868 C45.8031 68.3868 44.5703 69.6195 44.5703 71.1403 C44.5703 72.6611 45.8031 73.894 47.3239 73.894 M47.3239 68.3868 C48.8447 68.3868 50.0776 69.6195 50.0776 71.1403 C50.0776 72.6611 48.8447 73.894 47.3239 73.894 M47.3239 68.3868 L61.9918 68.3868 M47.3239 73.894 L62.0101 73.894 C63.5309 73.894 64.7637 72.6611 64.7637 71.1403 C64.7637 69.6242 63.5385 68.3945 62.0243 68.3869 M62.0114 68.3869 C62.0049 68.3869 61.9983 68.3869 61.9918 68.3868 M62.0114 68.3869 C62.0157 68.3869 62.02 68.3869 62.0243 68.3869 M62.0114 68.3869 L62.0243 68.3869 M62.0114 68.3869 L61.9918 68.3868 M61.9918 68.3868 C60.48 68.3763 59.2578 67.1475 59.2578 65.6332 C59.2578 64.1124 60.4906 62.8796 62.0114 62.8796 C63.5322 62.8796 64.7651 64.1124 64.7651 65.6332 C64.7651 67.1498 63.5392 68.3799 62.0243 68.3869 M61.9918 68.3868 L62.0101 68.3868 C62.0148 68.3868 62.0195 68.3868 62.0243 68.3869 M24 81.6962 L85.3341 81.6962 C85.8864 81.6962 86.3341 81.2485 86.3341 80.6962 L86.3341 74.8942 C86.3341 74.3419 85.8864 73.8942 85.3341 73.8942 L24 73.8942 C23.4477 73.8942 23 74.3419 23 74.8942 L23 80.6962 C23 81.2485 23.4477 81.6962 24 81.6962 Z M24 34.4252 L85.3341 34.4252 C85.8864 34.4252 86.3341 33.9775 86.3341 33.4252 L86.3341 29 C86.3341 28.4477 85.8864 28 85.3341 28 L24 28 C23.4477 28 23 28.4477 23 29 L23 33.4252 C23 33.9775 23.4477 34.4252 24 34.4252 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M35.5 87 C34.1193 87 33 85.8807 33 84.5 M35.5 87 L74.5 87 M35.5 87 L35.5 90.5 C35.5 91.0523 35.9477 91.5 36.5 91.5 L73.5 91.5 C74.0523 91.5 74.5 91.0523 74.5 90.5 L74.5 87 M33 84.5 L33 20.5 M33 84.5 L33 46.5 M77.5 84.5 L77.5 20.5 M77.5 84.5 C77.5 85.8807 76.3807 87 75 87 L74.5 87 M77.5 84.5 L77.5 46.5 M77.5 20.5 C77.5 19.1193 76.3807 18 75 18 L35.5 18 C34.1193 18 33 19.1193 33 20.5 M77.5 20.5 L77.5 46.5 M33 20.5 L33 46.5 M33 46.5 L77.5 46.5 M38.5 41 L38.5 31.5 M38.5 62 L38.5 52.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 669 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M66 57 L83 69 L83 71.5 C83 72.8807 81.8807 74 80.5 74 L29.5 74 C28.1193 74 27 72.8807 27 71.5 L27 69 L44 57 M44 57 L66 57 M44 57 L44 37.5 C44 36.1193 45.1193 35 46.5 35 L63.5 35 C64.8807 35 66 36.1193 66 37.5 L66 57 M83 69 L27 69" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 414 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M72 84.95 L72 88 C72 88.5523 71.5523 89 71 89 L39 89 C38.4477 89 38 88.5523 38 88 L38 84.95 M55 20 L71.5 20 C72.8807 20 74 21.1193 74 22.5 L74 39.5 C74 40.8807 72.8807 42 71.5 42 L55 42 M55 20 L55 42 M55 20 L38.5 20 C37.1193 20 36 21.1193 36 22.5 L36 39.5 C36 40.8807 37.1193 42 38.5 42 L55 42 M59 33 L59 38 M51 33 L51 38 M55 85 L71.5 85 C72.8807 85 74 83.8807 74 82.5 L74 65.5 C74 64.1193 72.8807 63 71.5 63 L55 63 M55 85 L55 63 M55 85 L38.5 85 C37.1193 85 36 83.8807 36 82.5 L36 65.5 C36 64.1193 37.1193 63 38.5 63 L55 63 M67 67 L62 67 M48 67 L43 67" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 736 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M49 54 L49 59 M51.5 37.5 L44 37.5 M51.5 37.5 C52.0523 37.5 52.5 37.9477 52.5 38.5 L52.5 44 M51.5 37.5 L48 37.5 M44 37.5 C43.4477 37.5 43 37.9477 43 38.5 L43 44 M44 37.5 L48 37.5 M43 44 L21.5 44 C20.1193 44 19 45.1193 19 46.5 L19 81.5 C19 82.8807 20.1193 84 21.5 84 L89.5 84 C90.8807 84 92 82.8807 92 81.5 L92 46.5 C92 45.1193 90.8807 44 89.5 44 L52.5 44 M43 44 L52.5 44 M58 33 L58 31 C58 29.6739 57.4732 28.4021 56.5355 27.4645 C55.5979 26.5268 54.3261 26 53 26 C51.6739 26 50.4021 26.5268 49.4645 27.4645 C48.5268 28.4021 48 29.6739 48 31 L48 37.5 M62 54 L62 59 M26 50 L52.5 50 C53.0523 50 53.5 50.4477 53.5 51 L53.5 77 C53.5 77.5523 53.0523 78 52.5 78 L26 78 C25.4477 78 25 77.5523 25 77 L25 51 C25 50.4477 25.4477 50 26 50 Z M58.5 50 L85 50 C85.5523 50 86 50.4477 86 51 L86 77 C86 77.5523 85.5523 78 85 78 L58.5 78 C57.9477 78 57.5 77.5523 57.5 77 L57.5 51 C57.5 50.4477 57.9477 50 58.5 50 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M34.5 76 C33.1193 76 32 74.8807 32 73.5 M34.5 76 L41.2308 76 M34.5 76 C34.3288 76 34.1616 75.9828 34 75.95 L34 78.5 C34 79.3284 34.6716 80 35.5 80 L38.8921 80 C39.5506 80 40.1321 79.5705 40.3258 78.9411 L41.2308 76 M32 73.5 L32 33 M32 73.5 L32 57.5 M78 73.5 L78 33 M69.2692 76 L75.5 76 C75.8556 76 76.1938 75.9258 76.5 75.792 C77.383 75.4062 78 74.5252 78 73.5 M78 73.5 L78 57.5 M78 33 C78 31.6193 76.8807 30.5 75.5 30.5 L34.5 30.5 C33.1193 30.5 32 31.6193 32 33 M78 33 L78 39.5 M32 33 L32 39.5 M32 39.5 L78 39.5 M32 39.5 L32 57.5 M78 39.5 L78 57.5 M78 57.5 L32 57.5 M50.5 48.5 L60 48.5 M50.5 66 L60 66 M41.2308 76 L69.2692 76 M69.2692 76 L70.1742 78.9411 C70.3679 79.5705 70.9494 80 71.6079 80 L75 80 C75.8284 80 76.5 79.3284 76.5 78.5 L76.5 75.792" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 934 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M29 76 C29 77.3807 30.1193 78.5 31.5 78.5 L78.5 78.5 C79.8807 78.5 81 77.3807 81 76 M29 76 L29 40 M29 76 L29 82.5 C29 83.8807 30.1193 85 31.5 85 L78.5 85 C79.8807 85 81 83.8807 81 82.5 L81 76 M81 76 L81 40 M81 40 C81 38.6193 79.8807 37.5 78.5 37.5 L31.5 37.5 C30.1193 37.5 29 38.6193 29 40 M81 40 L81 27.5 C81 26.1193 79.8807 25 78.5 25 L31.5 25 C30.1193 25 29 26.1193 29 27.5 L29 40 M35.75 31 L45.25 31 M43 42 L67 42 M40.5 72 L69.5 72 C70.8807 72 72 70.8807 72 69.5 L72 49.5 C72 48.1193 70.8807 47 69.5 47 L40.5 47 C39.1193 47 38 48.1193 38 49.5 L38 69.5 C38 70.8807 39.1193 72 40.5 72 Z M69.5 31 C69.5 32.1046 68.6046 33 67.5 33 C66.3954 33 65.5 32.1046 65.5 31 C65.5 29.8954 66.3954 29 67.5 29 C68.6046 29 69.5 29.8954 69.5 31 Z M77 31 C77 32.1046 76.1046 33 75 33 C73.8954 33 73 32.1046 73 31 C73 29.8954 73.8954 29 75 29 C76.1046 29 77 29.8954 77 31 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M55.7064 55.8805 L55.7064 52.9357 M55.7064 52.9357 C56.839 47.0461 62.7965 41.9801 68.6182 37.5322 M55.7064 52.9357 C50.7678 48.7899 47.7236 46.7763 41.6621 43.8748 M67.4992 61.3174 L65.9151 75.8488 C65.7779 77.1181 64.7063 78.0801 63.4296 78.0801 L48.4373 78.0801 C47.1606 78.0801 46.089 77.1181 45.9518 75.8488 L44.3593 61.3174 L67.4992 61.3174 Z M60.2363 44.7807 C58.4234 36.1732 66.8047 29.604 76.9989 31.869 C76.9989 40.024 68.3904 48.6318 60.2363 44.7807 Z M50.476 49.9288 C51.9926 41.2641 43.391 34.9863 33.2805 37.5996 C33.5602 45.7498 42.4589 54.0573 50.476 49.9288 Z M43.1152 61.3174 L68.7509 61.3174 C69.3031 61.3174 69.7509 60.8697 69.7509 60.3174 L69.7509 56.8809 C69.7509 56.3286 69.3031 55.8809 68.7509 55.8809 L43.1152 55.8809 C42.5629 55.8809 42.1152 56.3286 42.1152 56.8809 L42.1152 60.3174 C42.1152 60.8697 42.5629 61.3174 43.1152 61.3174 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M90.2693 33.8735 L84.648 33.8735 M25.6208 33.8735 L19.9995 33.8735 M90.2693 39.0264 L84.648 39.0264 M25.6208 39.0264 L19.9995 39.0264 M90.2693 44.1792 L84.648 44.1792 M25.6208 44.1792 L19.9995 44.1792 M90.2693 49.332 L84.648 49.332 M25.6208 49.332 L19.9995 49.332 M90.2693 54.4854 L84.648 54.4854 M25.6208 54.4854 L19.9995 54.4854 M90.2693 59.6382 L84.648 59.6382 M25.6208 59.6382 L19.9995 59.6382 M90.2693 64.791 L84.648 64.791 M25.6208 64.791 L19.9995 64.791 M90.2693 69.9438 L84.648 69.9438 M25.6208 69.9438 L19.9995 69.9438 M90.2693 75.0967 L84.648 75.0967 M25.6208 75.0967 L19.9995 75.0967 M28.121 32 L82.145 32 C83.5257 32 84.645 33.1193 84.645 34.5 L84.645 74.939 C84.645 76.3197 83.5257 77.439 82.145 77.439 L28.121 77.439 C26.7403 77.439 25.621 76.3197 25.621 74.939 L25.621 34.5 C25.621 33.1193 26.7403 32 28.121 32 Z M29.8369 32 L29.8369 77.439 M80.4302 32 L80.4302 77.439 M37.8005 54.4852 L55.6014 46.0532 L73.4022 54.4852 L55.6014 62.9172 L37.8005 54.4852 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M64 58.5 L64 47 C64 46.4477 64.4477 46 65 46 L68 46 C68.5523 46 69 46.4477 69 47 M64 58.5 L69 58.5 M64 58.5 L28 58.5 L28 39 L35.5 39 M69 58.5 L69 47 M69 58.5 L69 44.5 M69 58.5 L76 58.5 M69 47 L69 44.5 M52.5 39 L52.5 27.5 C52.5 26.9477 52.0523 26.5 51.5 26.5 L48.5 26.5 C47.9477 26.5 47.5 26.9477 47.5 27.5 M52.5 39 L47.5 39 M52.5 39 L81 39 L81 58.5 L76 58.5 M47.5 39 L47.5 27.5 M47.5 39 L47.5 25 M47.5 39 L40.5 39 M47.5 27.5 L47.5 25 M40.5 39 L40.5 27.5 M40.5 39 L40.5 25 M40.5 39 L35.5 39 M35.5 39 L35.5 27.5 C35.5 26.9477 35.9477 26.5 36.5 26.5 L39.5 26.5 C40.0523 26.5 40.5 26.9477 40.5 27.5 M40.5 27.5 L40.5 25 M49 72 L36.5 72 M50.5 72 L36.5 72 C35.9477 72 35.5 71.5523 35.5 71 L35.5 68.5 C35.5 67.9477 35.9477 67.5 36.5 67.5 L49 67.5 C49.5523 67.5 50 67.9477 50 68.5 L50 71 C50 71.5523 49.5523 72 49 72 M50.5 78 C51.0523 78 51.5 77.5523 51.5 77 L51.5 73 C51.5 72.4477 51.0523 72 50.5 72 L49 72 M36.5 72 C35.9477 72 35.5 72.4477 35.5 73 L35.5 77 C35.5 77.5523 35.9477 78 36.5 78 M76 58.5 L76 44.5 C76 43.9477 75.5523 43.5 75 43.5 L70 43.5 C69.4477 43.5 69 43.9477 69 44.5 M47.5 25 C47.5 24.4477 47.0523 24 46.5 24 L41.5 24 C40.9477 24 40.5 24.4477 40.5 25 M50.5 78 L36.5 78 M50.5 78 L81 78 L81 85 L28 85 L28 78 L36.5 78 M28 89.5 L28 23 C28 21.6193 29.1193 20.5 30.5 20.5 L78.5 20.5 C79.8807 20.5 81 21.6193 81 23 L81 89.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M39 84 L39 31.7143 C39 25.245 44.2564 20 50.75 20 C56.4663 20 61.2394 24.5462 62.1824 29.0091 M51.5 39.25 C51.5 33.5891 56.0891 29 61.75 29 C67.4109 29 72 33.5891 72 39.25 L51.5 39.25 M54.75 44.25 L54.75 46.75 M62 44.25 L62 46.75 M69.25 44.25 L69.25 46.75 M54.75 50.5 L54.75 53 M62 50.5 L62 53 M69.25 50.5 L69.25 53 M54.75 56.75 L54.75 59.25 M62 56.75 L62 59.25 M69.25 56.75 L69.25 59.25 M54.75 63 L54.75 65.5 M62 63 L62 65.5 M69.25 63 L69.25 65.5 M31 84 L80 84 C80.5523 84 81 84.4477 81 85 L81 88 C81 88.5523 80.5523 89 80 89 L31 89 C30.4477 89 30 88.5523 30 88 L30 85 C30 84.4477 30.4477 84 31 84" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 783 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M51 41.5 L58.5 41.5 M51 41.5 C50.4477 41.5 50 41.9477 50 42.5 L50 48 M51 41.5 L55 41.5 M58.5 41.5 C59.0523 41.5 59.5 41.9477 59.5 42.5 L59.5 48 M58.5 41.5 L55 41.5 M59.5 48 L77 48 C78.3807 48 79.5 49.1193 79.5 50.5 L79.5 57 C79.5 62.5228 75.0228 67 69.5 67 L40 67 C34.4772 67 30 62.5228 30 57 L30 50.5 C30 49.1193 31.1193 48 32.5 48 L50 48 M59.5 48 L50 48 M45 37 L45 35 C45 33.6739 45.5268 32.4021 46.4645 31.4645 C47.4021 30.5268 48.6739 30 50 30 C51.3261 30 52.5979 30.5268 53.5355 31.4645 C54.4732 32.4021 55 33.6739 55 35 L55 41.5 M45 80 L64.5 80 C65.0523 80 65.5 79.5523 65.5 79 L65.5 67 L44 67 L44 79 C44 79.5523 44.4477 80 45 80 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 822 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M88.5 44.6689 L88.5 34 C88.5 31.2386 86.2614 29 83.5 29 L59.5 29 C56.7386 29 54.5 31.2386 54.5 34 M88.5 44.6689 C89.2354 44.2435 90.0893 44 91 44 M88.5 44.6689 C88.3992 44.7272 88.3006 44.7889 88.2045 44.8539 M54.5 61.75 L54.5 34 M54.5 61.75 C54.5 64.3734 52.3734 66.5 49.75 66.5 L25.25 66.5 C22.6266 66.5 20.5 64.3734 20.5 61.75 M54.5 61.75 C54.5 59.1266 52.3734 57 49.75 57 L25.25 57 C24.6761 57 24.1261 57.1018 23.6168 57.2882 M54.5 61.75 C54.5 64.3734 56.6266 66.5 59.25 66.5 L83.75 66.5 C86.3734 66.5 88.5 64.3734 88.5 61.75 M88.5 61.75 C88.5 59.9118 87.4558 58.3175 85.9283 57.5278 C85.8889 57.5075 85.8493 57.4877 85.8093 57.4684 C85.1863 57.1682 84.4878 57 83.75 57 L59.25 57 C56.6266 57 54.5 59.1266 54.5 61.75 M54.5 34 C54.5 31.2386 52.2614 29 49.5 29 L25.5 29 C22.7386 29 20.5 31.2386 20.5 34 L20.5 44.416 M20.5 44.416 C19.8875 44.1484 19.2111 44 18.5 44 M20.5 44.416 C22.2659 45.1876 23.5 46.9497 23.5 49 M18.5 44 C21.2614 44 23.5 46.2386 23.5 49 M18.5 44 L18 44 C15.2386 44 13 46.2386 13 49 L13 70.5 C13 73.2614 15.2386 75.5 18 75.5 M23.5 49 L23.5 57.3328 M23.5 57.3328 C21.7427 58.0295 20.5 59.7446 20.5 61.75 M23.5 57.3328 C23.5387 57.3174 23.5776 57.3026 23.6168 57.2882 M91 44 C89.9645 44 89.0025 44.3148 88.2045 44.8539 C86.8744 45.7525 86 47.2741 86 49 L86 57.5657 M85.9283 57.5278 C85.9523 57.5402 85.9762 57.5528 86 57.5657 C87.4884 58.3677 88.5 59.9407 88.5 61.75 M91 44 L91.5 44 C94.2614 44 96.5 46.2386 96.5 49 L96.5 70.5 C96.5 73.2614 94.2614 75.5 91.5 75.5 M91.5 75.5 L18 75.5 M91.5 75.5 L90.5 75.5 M18 75.5 L18.5 75.5 M20.5 61.75 C20.5 59.7005 21.798 57.9542 23.6168 57.2882 M18.5 75.5 L26.5 75.5 M18.5 75.5 L18.5 80.5 C18.5 81.3284 19.1716 82 20 82 L23.3921 82 C24.0506 82 24.6321 81.5705 24.8258 80.9411 L26.5 75.5 M26.5 75.5 L82.5 75.5 M90.5 75.5 L82.5 75.5 M90.5 75.5 L90.5 80.5 C90.5 81.3284 89.8284 82 89 82 L85.6079 82 C84.9494 82 84.3679 81.5705 84.1742 80.9411 L82.5 75.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M73.4707 46.8057 L73.4707 28.5242 M73.4707 46.8057 L73.1504 46.8057 M73.4707 46.8057 L82.6039 46.8057 M73.4707 28.5242 L73.425 28.5242 M73.4707 28.5242 L75.6731 28.5242 M62.8987 54.2946 L62.8987 36.7397 M61.5762 61.7837 L61.5762 55.2944 C61.5762 54.7421 62.0239 54.2944 62.5762 54.2944 L62.8987 54.2946 L72.1504 54.2945 M52.3262 61.7837 L52.3262 45.1306 M52.3262 61.7837 L52.0039 61.7837 M52.3262 61.7837 L61.5762 61.7837 M62.8987 36.7397 L52.3262 45.1306 L42.1934 53.1725 L31.6211 61.5632 M42.1934 69.2725 L42.1934 53.1725 M42.1934 69.2725 L41.873 69.2725 M83.6035 69.2725 L83.6024 69.2725 L51.0039 69.2725 L42.1934 69.2725 M83.6031 76.7612 L83.6031 83.25 C83.6031 83.8023 83.1554 84.25 82.6031 84.25 L31.6231 84.2499 M41.873 69.2725 L83.6035 69.2725 L83.6031 76.7612 M83.6031 76.7612 L31.6231 76.7612 M31.6231 76.7612 L40.873 76.7613 L83.6031 76.7612 M31.6231 84.2499 L27.7773 84.2499 C27.2251 84.2499 26.7773 83.8021 26.7773 83.2499 L26.7773 57.4971 C26.7773 56.9448 27.2251 56.4971 27.7773 56.4971 L28.9352 56.4971 M31.6231 84.2499 L31.6231 76.7612 M31.6231 76.7612 L31.6211 61.5632 L31.6231 56.4971 M29.0212 56.4971 L31.6231 56.4971 L31.6228 57.2227 M31.6211 61.5632 L62.8987 36.7397 M31.6211 61.5632 L31.6228 57.2227 M73.425 28.5242 L75.6731 28.5242 M73.425 28.5242 C73.3119 28.5242 73.202 28.5624 73.1134 28.6328 L62.8987 36.7397 M75.6731 28.5242 C76.6463 28.5242 77.4352 27.7353 77.4352 26.7621 C77.4352 25.7889 76.6463 25 75.6731 25 L71.9495 25 C71.7935 25 71.6421 25.0532 71.5204 25.1508 L31.6228 57.2227 M41.873 69.2725 C41.3208 69.2725 40.873 69.7202 40.873 70.2725 L40.873 76.7613 M52.0039 61.7837 L83.6024 61.7837 M52.0039 61.7837 C51.4516 61.7837 51.0039 62.2314 51.0039 62.7837 L51.0039 69.2725 M83.6022 54.2944 L83.6024 61.7837 L83.6024 69.2725 M83.6024 61.7837 L61.5762 61.7837 M83.6022 54.2944 L83.6039 47.8057 C83.6039 47.2534 83.1562 46.8057 82.6039 46.8057 M83.6022 54.2944 L72.1504 54.2945 M73.1504 46.8057 L82.6039 46.8057 M73.1504 46.8057 C72.5981 46.8057 72.1504 47.2534 72.1504 47.8057 L72.1504 54.2945 M28.9352 56.4971 C28.9495 56.4973 28.9638 56.4974 28.9782 56.4974 C28.9925 56.4974 29.0069 56.4973 29.0212 56.4971 M28.9352 56.4971 C27.2519 56.4741 25.8945 55.1024 25.8945 53.4137 C25.8945 51.7107 27.2751 50.3301 28.9782 50.3301 C30.6812 50.3301 32.0618 51.7107 32.0618 53.4137 C32.0618 55.1024 30.7044 56.4741 29.0212 56.4971 M28.9352 56.4971 L29.0212 56.4971" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M64.5372 72.1021 L67.6892 84.7575 C67.847 85.3886 67.3696 86 66.7191 86 L37.6483 86 C37.0414 86 36.5743 85.4636 36.6578 84.8624 L38.9999 68 C40.0716 64 32.5 64.5 32 57 L32 26.5 C32 25.1193 33.1193 24 34.5 24 L45 24 C46.3807 24 47.5 25.1193 47.5 26.5 L47.5 52 M32 57 L47.5 57 M47.5 52 L76.5 52 C77.8807 52 79 53.1193 79 54.5 L79 57 M47.5 52 L47.5 57 M54.9999 71.75 C58.5528 72.6096 61.7379 72.6416 64.5372 72.1021 C73.95 70.2877 79 62.0101 79 57 M79 57 L47.5 57" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 645 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M23.5 31 L86 31 C87.3807 31 88.5 32.1193 88.5 33.5 L88.5 67.5 C88.5 68.8807 87.3807 70 86 70 L23.5 70 C22.1193 70 21 68.8807 21 67.5 L21 33.5 C21 32.1193 22.1193 31 23.5 31 M50 70 L50 78 M59.5 70 L59.5 78 M40 78 L70 78 M42.5466 45.9951 L52.5466 45.9951 M47.5466 45.9951 L47.5 56.0336 M56.75 45.5 L61.75 55.5 L66.75 45.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 504 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M54.5 20.5 L30.5 20.5 C29.1193 20.5 28 21.6193 28 23 L28 82.5 C28 83.8807 29.1193 85 30.5 85 L54.5 85 M54.5 20.5 L54.5 85 M54.5 20.5 L78.5 20.5 C79.8807 20.5 81 21.6193 81 23 L81 82.5 C81 83.8807 79.8807 85 78.5 85 L54.5 85 M79 84.95 L79 88.5 C79 89.0523 78.5523 89.5 78 89.5 L31 89.5 C30.4477 89.5 30 89.0523 30 88.5 L30 84.95 M50.5 48 L50.5 57.5 M58.5 48 L58.5 57.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 552 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M81 40 C81 38.6193 79.8807 37.5 78.5 37.5 L31.5 37.5 C30.1193 37.5 29 38.6193 29 40 M35.75 31 L45.25 31 M31.5 85 L78.5 85 C79.8807 85 81 83.8807 81 82.5 L81 55.5 L81 27.5 C81 26.1193 79.8807 25 78.5 25 L31.5 25 C30.1193 25 29 26.1193 29 27.5 L29 56 L29 82.5 C29 83.8807 30.1193 85 31.5 85 Z M72 60.5 C72 69.8888 64.3888 77.5 55 77.5 C45.6112 77.5 38 69.8888 38 60.5 C38 51.1112 45.6112 43.5 55 43.5 C64.3888 43.5 72 51.1112 72 60.5 Z M66.5 60.5 C66.5 66.8513 61.3513 72 55 72 C48.6487 72 43.5 66.8513 43.5 60.5 C43.5 54.1487 48.6487 49 55 49 C61.3513 49 66.5 54.1487 66.5 60.5 Z M69.5 31 C69.5 32.1046 68.6046 33 67.5 33 C66.3954 33 65.5 32.1046 65.5 31 C65.5 29.8954 66.3954 29 67.5 29 C68.6046 29 69.5 29.8954 69.5 31 Z M77 31 C77 32.1046 76.1046 33 75 33 C73.8954 33 73 32.1046 73 31 C73 29.8954 73.8954 29 75 29 C76.1046 29 77 29.8954 77 31 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 110">
|
||||
<path d="M68.5 61 L68.5 73.5 C68.5 74.3284 69.1716 75 70 75 L87 75 C87.8284 75 88.5 74.3284 88.5 73.5 L88.5 61 L88.4844 47 M26.4844 47 L26.4844 73.5 C26.4844 74.3284 25.8128 75 24.9844 75 L23.9844 75 C23.1559 75 22.4844 74.3284 22.4844 73.5 L22.4844 47 L22.4844 43 L24.866 35.5974 C25.2388 34.6346 26.165 34 27.1974 34 L83.7743 34 C84.8054 34 85.7307 34.633 86.1044 35.5939 L88.4844 43 L88.4844 47 M88.4844 47 L68.5 47 M68.5 47 L68.5 61 M68.5 47 L26.4844 47 M68.5 61 L88.5 61 M76.5 51 L80.5 51 M88.4844 43 L22.4844 43 M22.4844 47 L26.4844 47 M76.5 65 L80.5 65" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 735 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 25">
|
||||
<path d="M34.875 14.7505 C35.2252 14.9053 35.6091 14.9906 36 15.0005 C36.4382 15.0087 36.872 14.9219 37.2641 14.7477 C37.656 14.5735 37.9944 14.317 38.25 14.0005 C38.5056 13.684 38.844 13.4276 39.2359 13.2534 C39.628 13.0791 40.0618 12.9924 40.5 13.0005 C40.9382 12.9924 41.372 13.0791 41.7641 13.2534 C42.156 13.4276 42.4944 13.684 42.75 14.0005 C43.0056 14.317 43.344 14.5735 43.7359 14.7477 C44.128 14.9219 44.5618 15.0087 45 15.0005 C45.4382 15.0087 45.872 14.9219 46.2641 14.7477 C46.656 14.5735 46.9944 14.317 47.25 14.0005 C47.5056 13.684 47.844 13.4276 48.2359 13.2534 C48.628 13.0791 49.0618 12.9924 49.5 13.0005 C49.9382 12.9924 50.372 13.0791 50.7641 13.2534 C51.156 13.4276 51.4944 13.684 51.75 14.0005 C52.0056 14.317 52.344 14.5735 52.7359 14.7477 C53.128 14.9219 53.5618 15.0087 54 15.0005 C54.3909 14.9906 54.7748 14.9053 55.125 14.7505 M34.875 10.7505 C35.2252 10.9053 35.6091 10.9906 36 11.0005 C36.4382 11.0087 36.872 10.9219 37.2641 10.7477 C37.656 10.5735 37.9944 10.317 38.25 10.0005 C38.5056 9.684 38.844 9.4276 39.2359 9.2534 C39.628 9.0791 40.0618 8.9924 40.5 9.0005 C40.9382 8.9924 41.372 9.0791 41.7641 9.2534 C42.156 9.4276 42.4944 9.684 42.75 10.0005 C43.0056 10.317 43.344 10.5735 43.7359 10.7477 C44.128 10.9219 44.5618 11.0087 45 11.0005 C45.4382 11.0087 45.872 10.9219 46.2641 10.7477 C46.656 10.5735 46.9944 10.317 47.25 10.0005 C47.5056 9.684 47.844 9.4276 48.2359 9.2534 C48.628 9.0791 49.0618 8.9924 49.5 9.0005 C49.9382 8.9924 50.372 9.0791 50.7641 9.2534 C51.156 9.4276 51.4944 9.684 51.75 10.0005 C52.0056 10.317 52.344 10.5735 52.7359 10.7477 C53.128 10.9219 53.5618 11.0087 54 11.0005 C54.3909 10.9906 54.7748 10.9053 55.125 10.7505 M2.8125 25 L87.1875 25 C88.7408 25 90 23.8807 90 22.5 L90 2.5 C90 1.1193 88.7408 0 87.1875 0 L2.8125 0 C1.2592 0 0 1.1193 0 2.5 L0 22.5 C0 23.8807 1.2592 25 2.8125 25 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90">
|
||||
<path d="M78.5252 10.0692 C78.5335 9.9054 78.5377 9.7404 78.5377 9.5745 C78.5377 4.2866 74.2612 0 68.9858 0 L21.0141 0 C15.7388 0 11.4623 4.2866 11.4623 9.5745 C11.4623 9.7404 11.4665 9.9054 11.4748 10.0692 M80.086 74.5803 C80.5155 74.6465 80.9554 74.6809 81.4033 74.6809 C86.1511 74.6809 90 70.8229 90 66.0638 L90 18.1915 C90 13.4324 86.1511 9.5745 81.4033 9.5745 C80.3938 9.5745 79.425 9.7489 78.5252 10.0692 C75.1928 11.2557 72.8066 14.4443 72.8066 18.1915 L72.8066 18.3523 M72.8066 18.3523 C71.6366 18.8647 70.3443 19.1489 68.9858 19.1489 L21.0141 19.1489 C19.6557 19.1489 18.3634 18.8647 17.1934 18.3523 M72.8066 18.3523 L72.8066 66.0638 C72.8066 70.3739 75.9635 73.9449 80.086 74.5803 M11.4748 10.0692 C14.8072 11.2557 17.1934 14.4443 17.1934 18.1915 L17.1934 18.3523 M17.1934 18.3523 L17.1934 66.0638 C17.1934 70.3739 14.0365 73.9449 9.914 74.5803 C9.4845 74.6465 9.0446 74.6809 8.5967 74.6809 C3.8489 74.6809 0 70.8229 0 66.0638 L0 18.1915 C0 13.4324 3.8489 9.5745 8.5967 9.5745 C9.6062 9.5745 10.575 9.7489 11.4748 10.0692 M80.086 74.5803 C78.355 83.3709 70.622 90 61.3443 90 L28.6557 90 C19.378 90 11.645 83.3709 9.914 74.5803" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 65 65">
|
||||
<path d="M60.1252 17.0822 C60.1252 16.8061 60.1056 16.5347 60.0677 16.2692 M51.1877 11.3883 L13.8126 11.3883 C10.6715 11.3883 8.1251 8.8389 8.1251 5.6941 C8.1251 2.5494 10.6715 0 13.8126 0 L51.1877 0 C54.3288 0 56.8752 2.5494 56.8752 5.6941 C56.8752 8.8389 54.3288 11.3883 51.1877 11.3883 Z M13.8126 11.3883 L10.5626 11.388 C7.6973 11.388 5.3268 13.5094 4.9327 16.269 M51.1877 11.3883 L54.4377 11.388 C57.3031 11.388 59.6736 13.5095 60.0677 16.2692 M60.125 44.2992 C62.8174 44.2992 65 42.114 65 39.4185 L65 21.1495 C65 18.454 62.8174 16.2688 60.125 16.2688 C60.1059 16.2688 60.0868 16.2689 60.0677 16.2692 M60.125 44.2992 C57.4326 44.2992 55.25 42.114 55.25 39.4185 L55.25 21.1495 C55.25 18.4731 57.4017 16.2999 60.0677 16.2692 M46.7004 58.3142 L52.0002 58.3142 C52.8312 58.3142 53.633 58.1893 54.388 57.9573 C57.71 56.9362 60.1252 53.8403 60.1252 50.1797 L60.125 44.2992 M4.9327 16.269 C4.9135 16.2688 4.8943 16.2687 4.875 16.2687 C2.1826 16.2687 0 18.4538 0 21.1493 L0 39.4183 C0 42.1138 2.1826 44.299 4.875 44.299 C7.5674 44.299 9.75 42.1138 9.75 39.4183 L9.75 21.1493 C9.75 18.4731 7.5985 16.2999 4.9327 16.269 Z M18.4085 58.3142 L13.0001 58.3142 C12.1998 58.3142 11.4264 58.1983 10.6959 57.9824 C7.3309 56.988 4.8751 53.871 4.8751 50.1797 L4.875 44.299 M46.7004 58.3142 L52.5456 64.1662 C53.6394 65.261 55.4072 65.2801 56.524 64.2091 C57.6747 63.1047 57.694 61.2672 56.5669 60.1387 L54.388 57.9573 M46.7004 58.3142 L18.4085 58.3142 M10.6959 57.9824 L8.542 60.1387 C7.4149 61.2672 7.4342 63.1047 8.5849 64.2091 C9.7016 65.2802 11.4694 65.2609 12.5633 64.1662 L18.4085 58.3142" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 170 75">
|
||||
<path d="M0 2.5 C0 1.1193 1.1193 0 2.5 0 L167.5 0 C168.881 0 170 1.1193 170 2.5 L170 72.5 C170 73.8807 168.881 75 167.5 75 L2.5 75 C1.1193 75 0 73.8807 0 72.5 L0 2.5 Z M10 37.5 C10 22.3122 22.3122 10 37.5 10 L132.5 10 C147.688 10 160 22.3122 160 37.5 C160 52.6878 147.688 65 132.5 65 L37.5 65 C22.3122 65 10 52.6878 10 37.5 Z M29 37.5 C29 39.9853 26.9853 42 24.5 42 C22.0147 42 20 39.9853 20 37.5 C20 35.0147 22.0147 33 24.5 33 C26.9853 33 29 35.0147 29 37.5 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 635 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 140">
|
||||
<path d="M0 2.5 C0 1.1193 1.1193 0 2.5 0 L137.705 0 C138.972 0 140 1.0275 140 2.2951 C140 78.3474 78.3474 140 2.2951 140 C1.0275 140 0 138.972 0 137.705 L0 2.5 Z M10 30 C10 18.9543 18.9543 10 30 10 L122.745 10 C123.99 10 125 11.0096 125 12.2549 C125 74.5223 74.5223 125 12.2549 125 C11.0095 125 10 123.99 10 122.745 L10 30 Z M30 25 C30 27.7614 27.7614 30 25 30 C22.2386 30 20 27.7614 20 25 C20 22.2386 22.2386 20 25 20 C27.7614 20 30 22.2386 30 25 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 625 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 200">
|
||||
<path d="M0 10.5845 C0 16.4302 4.7469 21.1691 10.6024 21.1691 L149.398 21.1691 C155.253 21.1691 160 16.4302 160 10.5845 M74.6988 59.6584 L74.6988 44.2627 C74.6988 38.9484 70.3835 34.6404 65.0602 34.6404 L43.8554 34.6404 C38.5322 34.6404 34.2169 38.9484 34.2169 44.2627 L34.2169 59.6584 M128.675 59.6584 L128.675 44.2627 C128.675 38.9484 124.359 34.6404 119.036 34.6404 L97.8313 34.6404 C92.5081 34.6404 88.1928 38.9484 88.1928 44.2627 L88.1928 59.6584 M0 59.6584 L160 59.6584 M160 82.752 L0 82.752 M9.6386 200 L150.361 200 C155.685 200 160 195.692 160 190.378 L160 9.6223 C160 4.3081 155.685 0 150.361 0 L9.6386 0 C4.3153 0 0 4.3081 0 9.6223 L0 190.378 C0 195.692 4.3153 200 9.6386 200 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 863 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 200">
|
||||
<path d="M0 10.533 C0 16.3503 4.7153 21.0661 10.5319 21.0661 L79.4681 21.0661 C85.2847 21.0661 90 16.3503 90 10.533 M65.1064 59.368 L65.1064 44.0472 C65.1064 38.7588 60.8197 34.4718 55.5319 34.4718 L34.4681 34.4718 C29.1803 34.4718 24.8936 38.7588 24.8936 44.0472 L24.8936 59.368 M0 59.368 L90 59.368 M90 82.3492 L0 82.3492 M9.5745 200 L80.4255 200 C85.7134 200 90 195.713 90 190.424 L90 9.5755 C90 4.2871 85.7134 0 80.4255 0 L9.5745 0 C4.2866 0 0 4.2871 0 9.5755 L0 190.424 C0 195.713 4.2866 200 9.5745 200 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 684 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 60">
|
||||
<path d="M23 0.0005 L23 11.5005 C23 12.8812 21.8807 14.0005 20.5 14.0005 L19.5 14.0005 C18.1193 14.0005 17 12.8812 17 11.5005 L17 0.0005 M23 10.0005 L26.5 10.0005 C27.8807 10.0005 29 8.8812 29 7.5005 L29 6.5005 C29 5.1198 27.8807 4.0005 26.5 4.0005 L23 4.0005 M17 10.0005 L13.5 10.0005 C12.1193 10.0005 11 8.8812 11 7.5005 L11 6.5005 C11 5.1198 12.1193 4.0005 13.5 4.0005 L17 4.0005 M2.5 0 L37.5 0 C38.8807 0 40 1.1193 40 2.5 L40 40.6452 C40 51.3345 31.3345 60 20.6452 60 L19.3548 60 C8.6655 60 0 51.3345 0 40.6452 L0 2.5 C0 1.1193 1.1193 0 2.5 0 Z M7.5 18.0005 L32.5 18.0005 C33.8807 18.0005 35 19.1198 35 20.5005 L35 40.8069 C35 48.6458 28.6453 55.0005 20.8065 55.0005 L19.1935 55.0005 C11.3547 55.0005 5 48.6458 5 40.8069 L5 20.5005 C5 19.1198 6.1193 18.0005 7.5 18.0005 Z M24 27.0005 C24 29.2096 22.2091 31.0005 20 31.0005 C17.7909 31.0005 16 29.2096 16 27.0005 C16 24.7913 17.7909 23.0005 20 23.0005 C22.2091 23.0005 24 24.7913 24 27.0005 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 50">
|
||||
<path d="M0 32.4047 C0 42.1223 8.6655 50 19.3548 50 L20.6452 50 C31.3345 50 40 42.1223 40 32.4047 L40 2.2727 C40 1.0175 38.8807 0 37.5 0 L2.5 0 C1.1193 0 0 1.0175 0 2.2727 L0 32.4047 Z M5 32.5514 C5 39.6775 11.3547 45.4545 19.1935 45.4545 L20.8065 45.4545 C28.6453 45.4545 35 39.6775 35 32.5514 L35 11.3636 C35 10.1085 33.8807 9.0909 32.5 9.0909 L7.5 9.0909 C6.1193 9.0909 5 10.1085 5 11.3636 L5 32.5514 Z M25 33.6364 C25 36.1467 22.7614 38.1818 20 38.1818 C17.2386 38.1818 15 36.1467 15 33.6364 C15 31.126 17.2386 29.0909 20 29.0909 C22.7614 29.0909 25 31.126 25 33.6364 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 747 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 35">
|
||||
<path d="M57.7059 21.2353 L45.1008 21.2353 C44.5437 21.2353 44.0093 21.3902 43.6153 21.666 C43.2213 21.9418 43 22.3159 43 22.7059 M47.4121 12.4118 L53.2945 12.4118 M2.5 0 L97.5 0 C98.8807 0 100 1.1193 100 2.5 L100 32.5 C100 33.8807 98.8807 35 97.5 35 L2.5 35 C1.1193 35 0 33.8807 0 32.5 L0 2.5 C0 1.1193 1.1193 0 2.5 0 Z M57.7059 8 L57.7059 25.6471 L45.1008 25.6471 C44.5437 25.6471 44.0093 25.4147 43.6153 25.001 C43.2213 24.5873 43 24.0262 43 23.4412 L43 10.2059 C43 9.6208 43.2213 9.0598 43.6153 8.6461 C44.0093 8.2324 44.5437 8 45.1008 8 L57.7059 8 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 729 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 35">
|
||||
<path d="M39.6926 15.9138 L41.3539 13.919 M42.6062 17.8572 L44.295 15.6837 M2.5 35 L77.5 35 C78.8807 35 80 33.8807 80 32.5 L80 2.5 C80 1.1193 78.8807 0 77.5 0 L2.5 0 C1.1193 0 0 1.1193 0 2.5 L0 32.5 C0 33.8807 1.1193 35 2.5 35 Z M48.8271 17.1185 L47.6393 16.9997 C46.8954 16.9254 46.1943 16.6166 45.6371 16.1181 L40.5781 11.5916 C39.1831 10.3435 36.9606 11.1849 36.7418 13.0439 C36.5376 14.7804 34.5536 15.6699 33.1213 14.6672 L31.4306 13.4837 C30.3613 12.8422 29.0009 13.6124 29.0009 14.8594 L29.0009 19.1871 C29.0009 21.1363 30.5811 22.7165 32.5303 22.7165 L48.5479 22.7165 C50.0976 22.7165 51.3539 21.4602 51.3539 19.9105 C51.3539 18.469 50.2615 17.262 48.8271 17.1185 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 847 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 50">
|
||||
<path d="M0 2.5 C0 1.1193 1.1193 0 2.5 0 L77.5 0 C78.8807 0 80 1.1193 80 2.5 L80 47.5 C80 48.8807 78.8807 50 77.5 50 L2.5 50 C1.1193 50 0 48.8807 0 47.5 L0 2.5 Z M35.6813 33.35 C38.4689 35.55 42.5317 35.55 45.3194 33.35 C48.107 31.15 48.8146 27.38 46.997 24.4125 L40.5066 15 L34.0012 24.4125 C32.186 27.38 32.8936 31.1475 35.6813 33.35 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 511 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 45">
|
||||
<path d="M56 17 L61.625 17 M61.625 17 L67.25 17 M61.625 17 L61.625 27.9688 M72.0312 17 L77.6562 28.25 L83.2812 17 M2.5 45 L137.5 45 C138.881 45 140 43.8807 140 42.5 L140 2.5 C140 1.1193 138.881 0 137.5 0 L2.5 0 C1.1193 0 0 1.1193 0 2.5 L0 42.5 C0 43.8807 1.1193 45 2.5 45 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 448 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
||||
<path d="M41.9118 10.2941 L8.0882 10.2941 C5.2456 10.2941 2.9412 7.9897 2.9412 5.1471 C2.9412 2.3044 5.2456 0 8.0882 0 L41.9118 0 C44.7544 0 47.0588 2.3044 47.0588 5.1471 C47.0588 7.9897 44.7544 10.2941 41.9118 10.2941 Z M8.0882 10.2941 L5.1471 10.2942 C2.3044 10.2942 0 12.5986 0 15.4412 L0 42.6471 C0 46.708 3.292 50 7.3529 50 L42.6471 50 C46.708 50 50 46.708 50 42.6471 L50 15.4412 C50 12.5986 47.6956 10.2942 44.8529 10.2942 L41.9118 10.2941" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 619 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 45 48">
|
||||
<path d="M41.6417 15.9532 C43.2205 18.9284 43.9595 23.0373 43.9595 26.6364 C43.9595 38.4352 34.3521 48 22.5008 48 C10.6496 48 1.0422 38.4352 1.0422 26.6364 C1.0422 23.0373 1.7394 18.9194 3.3182 15.9441 M41.6417 15.9532 C40.2574 16.2589 38.7505 15.8756 37.6736 14.8035 M41.6417 15.9532 C42.4118 15.7831 43.1439 15.3999 43.743 14.8035 M3.472 15.9767 C3.4206 15.9668 3.3693 15.9559 3.3182 15.9441 C2.5629 15.7698 1.8457 15.3895 1.257 14.8035 M37.6736 14.8035 C35.681 12.8197 33.3155 11.2458 30.7121 10.1722 C28.1086 9.0986 25.318 8.5465 22.5 8.5465 C19.682 8.5465 16.8914 9.0986 14.2879 10.1722 C11.6845 11.2458 9.319 12.8197 7.3264 14.8035 C6.2789 15.8463 4.8246 16.2374 3.472 15.9767 M37.6736 14.8035 C39.3496 16.4721 42.067 16.4721 43.743 14.8035 M3.472 15.9767 C2.6605 15.8203 1.8855 15.4292 1.257 14.8035 C-0.419 13.1349 -0.419 10.4295 1.257 8.7609 C4.0466 5.9836 7.3585 3.7808 11.0033 2.2778 C14.6481 0.7747 18.5548 0 22.5 0 C26.4452 0 30.3519 0.7747 33.9967 2.2778 C37.6415 3.7808 40.9534 5.9836 43.743 8.7609 C45.419 10.4295 45.419 13.1349 43.743 14.8035" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 60">
|
||||
<path d="M0 2.5 C0 1.1193 1.1193 0 2.5 0 L117.5 0 C118.881 0 120 1.1193 120 2.5 L120 57.5 C120 58.8807 118.881 60 117.5 60 L2.5 60 C1.1193 60 0 58.8807 0 57.5 L0 2.5 Z M106 13 L106 7 C106 6.4477 106.4477 6 107 6 L113 6 C113.5523 6 114 6.4477 114 7 L114 13 C114 13.5523 113.5523 14 113 14 L107 14 C106.4477 14 106 13.5523 106 13 Z M106 53 L106 47 C106 46.4477 106.4477 46 107 46 L113 46 C113.5523 46 114 46.4477 114 47 L114 53 C114 53.5523 113.5523 54 113 54 L107 54 C106.4477 54 106 53.5523 106 53 Z M6 13 L6 7 C6 6.4477 6.4477 6 7 6 L13 6 C13.5523 6 14 6.4477 14 7 L14 13 C14 13.5523 13.5523 14 13 14 L7 14 C6.4477 14 6 13.5523 6 13 Z M6 53 L6 47 C6 46.4477 6.4477 46 7 46 L13 46 C13.5523 46 14 46.4477 14 47 L14 53 C14 53.5523 13.5523 54 13 54 L7 54 C6.4477 54 6 53.5523 6 53 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 954 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 60">
|
||||
<path d="M0 30 C0 13.4315 13.4315 0 30 0 L90 0 C106.569 0 120 13.4315 120 30 C120 46.5685 106.569 60 90 60 L30 60 C13.4315 60 0 46.5685 0 30 Z M82 13 L82 7 C82 6.4477 82.4477 6 83 6 L89 6 C89.5523 6 90 6.4477 90 7 L90 13 C90 13.5523 89.5523 14 89 14 L83 14 C82.4477 14 82 13.5523 82 13 Z M82 53 L82 47 C82 46.4477 82.4477 46 83 46 L89 46 C89.5523 46 90 46.4477 90 47 L90 53 C90 53.5523 89.5523 54 89 54 L83 54 C82.4477 54 82 53.5523 82 53 Z M30 13 L30 7 C30 6.4477 30.4477 6 31 6 L37 6 C37.5523 6 38 6.4477 38 7 L38 13 C38 13.5523 37.5523 14 37 14 L31 14 C30.4477 14 30 13.5523 30 13 Z M30 53 L30 47 C30 46.4477 30.4477 46 31 46 L37 46 C37.5523 46 38 46.4477 38 47 L38 53 C38 53.5523 37.5523 54 37 54 L31 54 C30.4477 54 30 53.5523 30 53 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 913 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80">
|
||||
<path d="M0 40 C0 17.9086 17.9086 0 40 0 C62.0914 0 80 17.9086 80 40 C80 62.0914 62.0914 80 40 80 C17.9086 80 0 62.0914 0 40 M57.9495 25.6076 L53.7069 21.365 C53.3164 20.9745 53.3164 20.3413 53.7069 19.9508 L57.9495 15.7082 C58.34 15.3177 58.9732 15.3177 59.3637 15.7082 L63.6063 19.9508 C63.9968 20.3413 63.9968 20.9745 63.6063 21.365 L59.3637 25.6076 C58.9732 25.9981 58.34 25.9981 57.9495 25.6076 Z M57.9495 63.8682 L53.7069 59.6256 C53.3164 59.235 53.3164 58.6019 53.7069 58.2114 L57.9495 53.9688 C58.34 53.5782 58.9732 53.5782 59.3637 53.9688 L63.6063 58.2114 C63.9968 58.6019 63.9968 59.235 63.6063 59.6256 L59.3637 63.8682 C58.9732 64.2587 58.34 64.2587 57.9495 63.8682 Z M19.9494 25.6068 L15.7068 21.3642 C15.3163 20.9737 15.3163 20.3405 15.7068 19.95 L19.9494 15.7074 C20.3399 15.3169 20.9731 15.3169 21.3636 15.7074 L25.6062 19.95 C25.9967 20.3405 25.9967 20.9737 25.6062 21.3642 L21.3636 25.6068 C20.9731 25.9973 20.3399 25.9973 19.9494 25.6068 Z M19.9492 63.868 L15.7066 59.6254 C15.3161 59.2348 15.3161 58.6017 15.7066 58.2112 L19.9492 53.9686 C20.3397 53.578 20.9729 53.578 21.3634 53.9686 L25.606 58.2112 C25.9965 58.6017 25.9965 59.2348 25.606 59.6254 L21.3634 63.868 C20.9729 64.2585 20.3397 64.2585 19.9492 63.868 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 60">
|
||||
<path d="M0 10 C0 4.4771 4.4771 0 10 0 L110 0 C115.523 0 120 4.4771 120 10 L120 50 C120 55.5228 115.523 60 110 60 L10 60 C4.4771 60 0 55.5228 0 50 L0 10 Z M106 13 L106 7 C106 6.4477 106.4477 6 107 6 L113 6 C113.5523 6 114 6.4477 114 7 L114 13 C114 13.5523 113.5523 14 113 14 L107 14 C106.4477 14 106 13.5523 106 13 Z M106 53 L106 47 C106 46.4477 106.4477 46 107 46 L113 46 C113.5523 46 114 46.4477 114 47 L114 53 C114 53.5523 113.5523 54 113 54 L107 54 C106.4477 54 106 53.5523 106 53 Z M6 13 L6 7 C6 6.4477 6.4477 6 7 6 L13 6 C13.5523 6 14 6.4477 14 7 L14 13 C14 13.5523 13.5523 14 13 14 L7 14 C6.4477 14 6 13.5523 6 13 Z M6 53 L6 47 C6 46.4477 6.4477 46 7 46 L13 46 C13.5523 46 14 46.4477 14 47 L14 53 C14 53.5523 13.5523 54 13 54 L7 54 C6.4477 54 6 53.5523 6 53 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 941 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 60">
|
||||
<path d="M26.6667 6.7692 L2.5 6.7692 C1.1193 6.7692 0 7.8885 0 9.2692 L0 12.7308 C0 14.1115 1.1193 15.2308 2.5 15.2308 L26.6667 15.2308 M26.6667 6.7692 L26.6667 2.5 C26.6667 1.1193 27.786 0 29.1667 0 L52.6111 0 C53.9918 0 55.1111 1.1193 55.1111 2.5 L55.1111 6.7692 M26.6667 6.7692 L55.1111 6.7692 M55.1111 6.7692 L77.5 6.7692 C78.8807 6.7692 80 7.8885 80 9.2692 L80 12.7308 C80 14.1115 78.8807 15.2308 77.5 15.2308 L55.1111 15.2308 M26.6667 15.2308 L26.6667 19.5 C26.6667 20.8807 27.786 22 29.1667 22 L52.6111 22 C53.9918 22 55.1111 20.8807 55.1111 19.5 L55.1111 15.2308 M26.6667 15.2308 L55.1111 15.2308 M93.8438 60 L117.5 60 C118.881 60 120 58.8807 120 57.5 L120 2.5 C120 1.1193 118.881 0 117.5 0 L93.8438 0 C92.463 0 91.3438 1.1193 91.3438 2.5 L91.3438 57.5 C91.3438 58.8807 92.463 60 93.8438 60 Z M9.5 55 L56.5 55 C57.8807 55 59 53.8807 59 52.5 L59 41.5 C59 40.1193 57.8807 39 56.5 39 L9.5 39 C8.1193 39 7 40.1193 7 41.5 L7 52.5 C7 53.8807 8.1193 55 9.5 55 Z M75 55 C77.2091 55 79 53.2091 79 51 L79 43 C79 40.7909 77.2091 39 75 39 C72.7909 39 71 40.7909 71 43 L71 51 C71 53.2091 72.7909 55 75 55 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 50">
|
||||
<path d="M15 32 L15 30 M19.0009 36 L21 36 M15 42 L15 40 M9.0009 36 L11 36 M3.125 50 L26.875 50 C28.6009 50 30 48.6009 30 46.875 L30 3.125 C30 1.3991 28.6009 0 26.875 0 L3.125 0 C1.3991 0 0 1.3991 0 3.125 L0 46.875 C0 48.6009 1.3991 50 3.125 50 Z M22 15 C22 18.866 18.866 22 15 22 C11.134 22 8 18.866 8 15 C8 11.134 11.134 8 15 8 C18.866 8 22 11.134 22 15 Z M20 36 C20 38.7614 17.7614 41 15 41 C12.2386 41 10 38.7614 10 36 C10 33.2386 12.2386 31 15 31 C17.7614 31 20 33.2386 20 36 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 655 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 70">
|
||||
<path d="M0 2.5 C0 1.1193 1.1193 0 2.5 0 L137.5 0 C138.881 0 140 1.1193 140 2.5 L140 67.5 C140 68.8807 138.881 70 137.5 70 L2.5 70 C1.1193 70 0 68.8807 0 67.5 L0 2.5 Z M6 13 L6 7 C6 6.4477 6.4477 6 7 6 L13 6 C13.5523 6 14 6.4477 14 7 L14 13 C14 13.5523 13.5523 14 13 14 L7 14 C6.4477 14 6 13.5523 6 13 Z M126 13 L126 7 C126 6.4477 126.4477 6 127 6 L133 6 C133.5523 6 134 6.4477 134 7 L134 13 C134 13.5523 133.5523 14 133 14 L127 14 C126.4477 14 126 13.5523 126 13 Z M6 63 L6 57 C6 56.4477 6.4477 56 7 56 L13 56 C13.5523 56 14 56.4477 14 57 L14 63 C14 63.5523 13.5523 64 13 64 L7 64 C6.4477 64 6 63.5523 6 63 Z M126 63 L126 57 C126 56.4477 126.4477 56 127 56 L133 56 C133.5523 56 134 56.4477 134 57 L134 63 C134 63.5523 133.5523 64 133 64 L127 64 C126.4477 64 126 63.5523 126 63 Z M23 60 L117 60" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 969 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 160">
|
||||
<path d="M160 65.5 C160 66.8807 158.881 68 157.5 68 L95 68 C81.1929 68 70 79.1929 70 93 L70 157.5 C70 158.881 68.8807 160 67.5 160 L2.5 160 C1.1193 160 0 158.881 0 157.5 L0 2.5 C0 1.1193 1.1193 0 2.5 0 L157.5 0 C158.881 0 160 1.1193 160 2.5 L160 65.5 Z M6 13 L6 7 C6 6.4477 6.4477 6 7 6 L13 6 C13.5523 6 14 6.4477 14 7 L14 13 C14 13.5523 13.5523 14 13 14 L7 14 C6.4477 14 6 13.5523 6 13 Z M146 13 L146 7 C146 6.4477 146.4477 6 147 6 L153 6 C153.5523 6 154 6.4477 154 7 L154 13 C154 13.5523 153.5523 14 153 14 L147 14 C146.4477 14 146 13.5523 146 13 Z M146 61 L146 55 C146 54.4477 146.4477 54 147 54 L153 54 C153.5523 54 154 54.4477 154 55 L154 61 C154 61.5523 153.5523 62 153 62 L147 62 C146.4477 62 146 61.5523 146 61 Z M6 153 L6 147 C6 146.4477 6.4477 146 7 146 L13 146 C13.5523 146 14 146.4477 14 147 L14 153 C14 153.5523 13.5523 154 13 154 L7 154 C6.4477 154 6 153.5523 6 153 Z M56 153 L56 147 C56 146.4477 56.4477 146 57 146 L63 146 C63.5523 146 64 146.4477 64 147 L64 153 C64 153.5523 63.5523 154 63 154 L57 154 C56.4477 154 56 153.5523 56 153 Z M110 58 L96.3636 58 C91.5883 58 86.8597 58.9406 82.4479 60.768 C78.036 62.5955 74.0273 65.274 70.6507 68.6507 C67.274 72.0273 64.5955 76.036 62.768 80.4479 C60.9406 84.8597 60 89.5883 60 94.3636 L60 108" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
|
||||
<path d="M20 41.9429 C20.3592 42.1215 20.7529 42.2199 21.1538 42.2314 C21.6033 42.2408 22.0482 42.1407 22.4503 41.9396 C22.8523 41.7386 23.1994 41.4427 23.4615 41.0775 C23.7237 40.7124 24.0707 40.4165 24.4728 40.2155 C24.8749 40.0144 25.3198 39.9143 25.7692 39.9237 C26.2186 39.9143 26.6636 40.0144 27.0657 40.2155 C27.4677 40.4165 27.8148 40.7124 28.0769 41.0775 C28.339 41.4427 28.6861 41.7386 29.0882 41.9396 C29.4902 42.1407 29.9352 42.2408 30.3846 42.2314 C30.834 42.2408 31.279 42.1407 31.681 41.9396 C32.0831 41.7386 32.4302 41.4427 32.6923 41.0775 C32.9544 40.7124 33.3015 40.4165 33.7036 40.2155 C34.1056 40.0144 34.5506 39.9143 35 39.9237 C35.4494 39.9143 35.8944 40.0144 36.2964 40.2155 C36.6985 40.4165 37.0456 40.7124 37.3077 41.0775 C37.5698 41.4427 37.9169 41.7386 38.319 41.9396 C38.721 42.1407 39.166 42.2408 39.6154 42.2314 C40.0164 42.2199 40.41 42.1215 40.7692 41.9429 M20 37.3277 C20.3592 37.5063 20.7529 37.6047 21.1538 37.6162 C21.6033 37.6256 22.0482 37.5254 22.4503 37.3244 C22.8523 37.1234 23.1994 36.8275 23.4615 36.4623 C23.7237 36.0971 24.0707 35.8012 24.4728 35.6002 C24.8749 35.3992 25.3198 35.2991 25.7692 35.3085 C26.2186 35.2991 26.6636 35.3992 27.0657 35.6002 C27.4677 35.8012 27.8148 36.0971 28.0769 36.4623 C28.339 36.8275 28.6861 37.1234 29.0882 37.3244 C29.4902 37.5254 29.9352 37.6256 30.3846 37.6162 C30.834 37.6256 31.279 37.5254 31.681 37.3244 C32.0831 37.1234 32.4302 36.8275 32.6923 36.4623 C32.9544 36.0971 33.3015 35.8012 33.7036 35.6002 C34.1056 35.3992 34.5506 35.2991 35 35.3085 C35.4494 35.2991 35.8944 35.3992 36.2964 35.6002 C36.6985 35.8012 37.0456 36.0971 37.3077 36.4623 C37.5698 36.8275 37.9169 37.1234 38.319 37.3244 C38.721 37.5254 39.166 37.6256 39.6154 37.6162 C40.0164 37.6047 40.41 37.5063 40.7692 37.3277 M34.9998 27.2308 L34.9998 18 C31.4275 21.5338 31.274 24.3992 31.2911 27.2308 L34.9998 27.2308 Z M34.9998 27.2308 L34.9998 31.8462 L34.2581 31.8462 L34.2581 29.5385 M26.8405 18.7692 L26.8405 23.3846 M26.8405 23.3846 L26.8405 31.8462 M26.8405 23.3846 C26.2503 23.3846 25.6843 23.1415 25.267 22.7087 C24.8497 22.2759 24.6152 21.689 24.6152 21.0769 L24.6152 18.7692 M26.8405 23.3846 C27.4307 23.3846 27.9967 23.1415 28.414 22.7087 C28.8313 22.2759 29.0658 21.689 29.0658 21.0769 L29.0658 18.7692 M2.5 60 L57.5 60 C58.8807 60 60 58.8807 60 57.5 L60 2.5 C60 1.1193 58.8807 0 57.5 0 L2.5 0 C1.1193 0 0 1.1193 0 2.5 L0 57.5 C0 58.8807 1.1193 60 2.5 60 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
|
||||
<path d="M30.8408 23.9231 C30.8408 23.9231 29.8187 26.0121 29.7032 27.5495 C29.5506 29.5818 30.9329 30.4963 30.8408 32.5357 C30.7639 34.2418 29.7032 36.6154 29.7032 36.6154 M25.0713 23.9231 C25.0713 23.9231 24.0491 26.0121 23.9337 27.5495 C23.781 29.5818 25.1633 30.4963 25.0713 32.5357 C24.9943 34.2418 23.9337 36.6154 23.9337 36.6154 M36.6104 23.9231 C36.6104 23.9231 35.5882 26.0121 35.4727 27.5495 C35.3201 29.5818 36.7024 30.4963 36.6104 32.5357 C36.5334 34.2418 35.4727 36.6154 35.4727 36.6154 M2.5 60 L57.5 60 C58.8807 60 60 58.8807 60 57.5 L60 2.5 C60 1.1193 58.8807 0 57.5 0 L2.5 0 C1.1193 0 0 1.1193 0 2.5 L0 57.5 C0 58.8807 1.1193 60 2.5 60 Z M43.5385 30.2692 C43.5385 37.5976 37.5976 43.5385 30.2692 43.5385 C22.9408 43.5385 17 37.5976 17 30.2692 C17 22.9408 22.9408 17 30.2692 17 C37.5976 17 43.5385 22.9408 43.5385 30.2692 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1012 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 70 120">
|
||||
<path d="M13.6111 31.8884 C13.6111 29.0479 11.4347 26.7451 8.75 26.7451 L4.8611 26.7451 C2.1764 26.7451 0 29.0478 0 31.8884 C0 34.729 2.1764 37.0317 4.8611 37.0317 L8.75 37.0317 C11.4347 37.0317 13.6111 34.729 13.6111 31.8884 Z M13.6111 31.8884 L15.5556 31.8884 C16.6294 31.8884 17.5 32.8095 17.5 33.9457 L17.5 46.2896 C17.5 47.4259 18.3706 48.3469 19.4444 48.3469 L21.3889 48.3469 M48.6111 49.3756 L50.5556 49.3756 C51.6294 49.3756 52.5 50.2967 52.5 51.4329 L52.5 63.7769 C52.5 64.9131 53.3706 65.8342 54.4444 65.8342 L56.3889 65.8342 M49.6421 16.4301 C49.8068 13.4672 49.1767 10.6587 47.952 8.2293 C45.4922 3.35 40.634 0 35 0 C29.366 0 24.5078 3.35 22.048 8.2293 M47.952 8.2293 L62.2232 8.2293 C63.2971 8.2293 64.1676 9.1504 64.1676 10.2866 L64.1676 14.4012 C64.1676 15.5374 63.2971 16.4585 62.2232 16.4585 L49.6421 16.4301 M49.6421 16.4301 L45.866 84.3501 M22.048 8.2293 L7.7787 8.2293 C6.7048 8.2293 5.8343 9.1504 5.8343 10.2866 L5.8343 14.4012 C5.8343 15.5374 6.7048 16.4585 7.7787 16.4585 L20.3579 16.4301 M22.048 8.2293 C20.8233 10.6587 20.1932 13.4672 20.3579 16.4301 M20.3579 16.4301 L24.134 84.3501 M24.134 84.3501 L16.4172 84.3501 C15.281 84.3501 14.3599 83.429 14.3599 82.2928 L14.3599 81.6598 C14.3599 79.7369 12.8867 78.1781 11.0693 78.1781 C9.252 78.1781 7.7787 79.7369 7.7787 81.6598 L7.7787 104.4421 C7.7787 108.1271 9.6425 111.5311 12.6644 113.3651 L22.7946 119.5111 C24.4864 120.5381 26.6451 119.8931 27.5795 118.0821 C28.4688 116.3581 27.9229 114.1991 26.339 113.1761 L17.153 107.2411 C15.4181 106.1201 14.3599 104.1241 14.3599 101.9731 L14.3599 94.6367 C14.3599 93.5005 15.281 92.5794 16.4172 92.5794 L24.5915 92.5794 M24.134 84.3501 L45.866 84.3501 M24.5915 92.5794 L45.4085 92.5794 L53.5847 92.5794 C54.7209 92.5794 55.642 93.5005 55.642 94.6367 L55.642 101.9731 C55.642 104.1241 54.5838 106.1201 52.8489 107.2411 L43.6629 113.1761 C42.079 114.1991 41.5331 116.3581 42.4224 118.0821 C43.3568 119.8931 45.5155 120.5381 47.2073 119.5111 L57.3375 113.3651 C60.3594 111.5311 62.2232 108.1271 62.2232 104.4421 L62.2232 81.6598 C62.2232 79.7369 60.7499 78.1781 58.9326 78.1781 C57.1152 78.1781 55.642 79.7369 55.642 81.6598 L55.642 82.2928 C55.642 83.429 54.7209 84.3501 53.5847 84.3501 L45.866 84.3501 M45.4085 92.5794 L45.2931 94.6544 C44.9725 100.4211 40.4594 104.9231 35 104.9231 C29.5406 104.9231 25.0275 100.4211 24.7069 94.6544 L24.5915 92.5794 M29.6788 36.3713 L26.7748 17.8019 C25.9908 12.7888 29.6457 8.2293 34.4482 8.2293 L35.5518 8.2293 C40.3543 8.2293 44.0092 12.7888 43.2252 17.8019 L40.3212 36.3713 C39.8904 39.126 37.639 41.1463 35 41.1463 C32.361 41.1463 30.1096 39.126 29.6788 36.3713 Z M65.1389 69.9489 L61.25 69.9489 C58.5653 69.9489 56.3889 67.6461 56.3889 64.8056 C56.3889 61.965 58.5653 59.6622 61.25 59.6622 L65.1389 59.6622 C67.8236 59.6622 70 61.965 70 64.8056 C70 67.6461 67.8236 69.9489 65.1389 69.9489 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 40">
|
||||
<path d="M104.516 34.1865 L104.516 37.8022 C104.516 39.016 103.397 40 102.016 40 L17.9839 40 C16.6032 40 15.4839 39.016 15.4839 37.8022 L15.4839 34.1865 M104.516 34.1865 L15.4839 34.1865 M104.516 34.1865 L117.5 34.1865 C118.881 34.1865 120 33.2025 120 31.9886 L120 2.1978 C120 0.984 118.881 0 117.5 0 L2.5 0 C1.1193 0 0 0.984 0 2.1978 L0 31.9886 C0 33.2025 1.1193 34.1865 2.5 34.1865 L15.4839 34.1865 M59.9992 15.6396 C62.9823 11.9458 60.2128 6.9042 58.7206 5.6558 C58.7206 9.4468 55.8629 11.5728 54.0319 13.1439 C52.2034 14.7161 51.0488 16.822 51.0488 19.3836 C51.0488 23.5196 55.0561 26.8716 59.9992 26.8716 C64.9424 26.8716 68.9497 23.5196 68.9497 19.3836 C68.9497 17.4719 67.3744 14.4671 65.9666 13.1439 C63.3019 16.8879 61.8021 16.8879 59.9992 15.6396 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 933 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 65">
|
||||
<path d="M27.4202 21.3572 L29.9916 22.7501 L32.563 21.3572 M29.9916 18.5715 L29.9916 27.6251 L33.8487 30.0208 M37.613 24.5163 L37.7853 27.6252 L40.1844 29.3412 M41.1262 25.5358 L33.8889 30.0626 L33.9018 34.8791 M40.1844 35.6592 L37.7853 37.3752 L37.613 40.484 M41.1259 39.4644 L33.8886 34.9376 L30.0443 37.3584 M32.563 43.643 L29.9916 42.2501 L27.4202 43.643 M29.9916 46.4283 L29.9916 37.3747 L26.1345 34.979 M22.3702 40.484 L22.1979 37.3752 L19.7988 35.6592 M18.8571 39.4643 L26.0944 34.9375 L26.0816 30.121 M19.7988 29.3412 L22.1979 27.6252 L22.3702 24.5163 M18.8571 25.5358 L26.0944 30.0626 L29.9387 27.6418 M2.1429 65 L57.8571 65 C59.0406 65 60 63.9607 60 62.6786 L60 2.3214 C60 1.0393 59.0406 0 57.8571 0 L2.1429 0 C0.9594 0 0 1.0393 0 2.3214 L0 62.6786 C0 63.9607 0.9594 65 2.1429 65 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 965 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 50">
|
||||
<path d="M41.7863 0 L57.5 0 C58.8807 0 60 1.1193 60 2.5 L60 47.5 C60 48.2192 59.6963 48.8674 59.2102 49.3235 C58.7631 49.743 58.1616 50 57.5 50 L2.5 50 C1.8373 50 1.2348 49.7421 0.7874 49.3213 C0.3027 48.8653 0 48.218 0 47.5 L0 2.5 C0 1.1193 1.1193 0 2.5 0 L18.2148 0 M18.2148 0 L41.7863 0 M18.2148 0 L18.2148 23.0357 M41.7863 0 L41.7863 23.0357 M59.2102 49.3235 L41.7863 23.0357 M0.7874 49.3213 L18.2148 23.0357 M41.7863 23.0357 L18.2148 23.0357" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 620 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
|
||||
<path d="M0 2.5 C0 1.1193 1.1193 0 2.5 0 L57.5 0 C58.8807 0 60 1.1193 60 2.5 L60 57.5 C60 58.8807 58.8807 60 57.5 60 L2.5 60 C1.1193 60 0 58.8807 0 57.5 L0 2.5 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 335 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90">
|
||||
<path d="M46.0305 41.4695 C46.0305 42.8502 47.1498 43.9695 48.5305 43.9695 L87.5 43.9695 C88.8807 43.9695 90 45.0888 90 46.4695 L90 87.5 C90 88.8807 88.8807 90 87.5 90 L2.5 90 C1.1193 90 0 88.8807 0 87.5 L0 2.5 C0 1.1193 1.1193 0 2.5 0 L43.5305 0 C44.9112 0 46.0305 1.1193 46.0305 2.5 L46.0305 41.4695 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 477 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 50">
|
||||
<path d="M39.4138 6 L51.5 6 C52.8807 6 54 7.1193 54 8.5 L54 41.5 C54 42.8807 52.8807 44 51.5 44 L8.5 44 C7.1193 44 6 42.8807 6 41.5 L6 8.5 C6 7.1193 7.1193 6 8.5 6 L20.5862 6 M33 0 L33 13.5 C33 14.8807 31.8807 16 30.5 16 L29.5 16 C28.1193 16 27 14.8807 27 13.5 L27 0 M33 9 L36.5 9 C37.8807 9 39 7.8807 39 6.5 L39 5.5 C39 4.1193 37.8807 3 36.5 3 L33 3 M27 9 L23.5 9 C22.1193 9 21 7.8807 21 6.5 L21 5.5 C21 4.1193 22.1193 3 23.5 3 L27 3 M2.5 50 L57.5 50 C58.8807 50 60 48.8807 60 47.5 L60 2.5 C60 1.1193 58.8807 0 57.5 0 L2.5 0 C1.1193 0 0 1.1193 0 2.5 L0 47.5 C0 48.8807 1.1193 50 2.5 50 Z M34 26 C34 28.2091 32.2092 30 30 30 C27.7909 30 26 28.2091 26 26 C26 23.7909 27.7909 22 30 22 C32.2092 22 34 23.7909 34 26 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 887 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 50">
|
||||
<path d="M21 0 L2.5 0 C1.1193 0 0 1.1193 0 2.5 L0 47.5 C0 48.8807 1.1193 50 2.5 50 L87.5 50 C88.8807 50 90 48.8807 90 47.5 L90 2.5 C90 1.1193 88.8807 0 87.5 0 L27 0 M21 0 L27 0 M21 0 L21 3 M27 0 L27 3 M33 6 L39.5 6 C40.8807 6 42 7.1193 42 8.5 L42 41.5 C42 42.8807 40.8807 44 39.5 44 L8.5 44 C7.1193 44 6 42.8807 6 41.5 L6 8.5 C6 7.1193 7.1193 6 8.5 6 L15 6 M33 6 L33 6.5 C33 7.8807 31.8807 9 30.5 9 L27 9 M33 6 L33 5.5 C33 4.1193 31.8807 3 30.5 3 L27 3 M15 6 L15 5.5 C15 4.1193 16.1193 3 17.5 3 L21 3 M15 6 L15 6.5 C15 7.8807 16.1193 9 17.5 9 L21 9 M27 9 L27 13.5 C27 14.8807 25.8807 16 24.5 16 L23.5 16 C22.1193 16 21 14.8807 21 13.5 L21 9 M27 9 L27 3 M21 3 L21 9 M54 11 L78 11 M28 26 C28 28.2091 26.2092 30 24 30 C21.7909 30 20 28.2091 20 26 C20 23.7909 21.7909 22 24 22 C26.2092 22 28 23.7909 28 26 Z M50.5 44 L81.5 44 C82.8807 44 84 42.8807 84 41.5 L84 8.5 C84 7.1193 82.8807 6 81.5 6 L50.5 6 C49.1193 6 48 7.1193 48 8.5 L48 41.5 C48 42.8807 49.1193 44 50.5 44 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 35">
|
||||
<path d="M0.8573 34.4168 L58.7306 0.6573 M0.8556 0.5832 L58.7287 34.3426 M2.5 35 L57.5 35 C58.8807 35 60 33.8807 60 32.5 L60 2.5 C60 1.1193 58.8807 0 57.5 0 L2.5 0 C1.1193 0 0 1.1193 0 2.5 L0 32.5 C0 33.8807 1.1193 35 2.5 35 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 400 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
|
||||
<path d="M0.0918 29.5328 L29.8628 0.7876 M1 59 L59 30 M0.5508 0.7876 L31 28 M0 29 L59 59 M28.187 0 L2.5 0 C1.1193 0 0 1.1193 0 2.5 L0 57.5 C0 58.8807 1.1193 60 2.5 60 L57.5 60 C58.8807 60 60 58.8807 60 57.5 L60 31.813 C60 30.4323 58.8807 29.313 57.5 29.313 L33.187 29.313 C31.8063 29.313 30.687 28.1937 30.687 26.813 L30.687 2.5 C30.687 1.1193 29.5677 0 28.187 0 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 538 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 40">
|
||||
<path d="M2.5 0 L47.5 0 C48.8807 0 50 1.1193 50 2.5 L50 37.5 C50 38.8807 48.8807 40 47.5 40 L2.5 40 C1.1193 40 0 38.8807 0 37.5 L0 2.5 C0 1.1193 1.1193 0 2.5 0 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 335 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
|
||||
<path d="M2.5 0 L57.5 0 C58.8807 0 60 1.1193 60 2.5 L60 57.5 C60 58.8807 58.8807 60 57.5 60 L2.5 60 C1.1193 60 0 58.8807 0 57.5 L0 2.5 C0 1.1193 1.1193 0 2.5 0 M25.8 19 C23.5 20.7 23.5 23 25.8 24.6 M30.4 19 C28.1 20.7 28.1 23 30.4 24.6 M35 19 C32.7 20.7 32.7 23 35 24.6 M23.9 28.1 L36.9 28.1 C39.5 28.1 41.4 30.6 40.6 33.1 L38.9 38.8 C38.4164 40.4453 36.9148 41.5817 35.2 41.6 L25.6 41.6 C23.8852 41.5817 22.3836 40.4453 21.9 38.8 L20.2 33.1 C19.4 30.6 21.3 28.1 23.9 28.1" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 646 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
|
||||
<path d="M24.8565 7.1036 C23.3393 6.4889 21.861 6.2197 20.1144 6.2197 C17.5291 6.2197 15.1227 6.9635 13.1097 8.2427 M24.8565 7.1036 C24.8486 7.1282 24.8408 7.1528 24.833 7.1775 C24.4784 8.3107 24.3516 9.5373 24.492 10.8261 M24.7189 11.8989 C25.4216 12.2159 26.1278 12.435 26.8306 12.5658 C28.2723 12.834 29.7782 12.7271 31.1798 12.3005 C36.0951 10.8043 40 5.5028 40 0.4008 C32.7482 -1.164 26.4989 2.0163 24.8565 7.1036 M31.1798 12.3005 C32.2787 14.1356 32.9238 16.3835 32.9238 18.6594 C32.9238 22.3582 31.1618 25.7326 28.5231 28.0113 M28.5231 28.0113 C27.9938 26.9122 27.2807 25.9351 26.4047 25.125 M28.5231 28.0113 C30.1086 31.3144 30.0424 35.7276 27.7495 40 C23.4949 38.8929 19.8113 35.1747 18.6045 31.0566 M18.6045 31.0566 C12.3429 30.241 7.3049 24.9634 7.3049 18.6594 C7.3049 17.9614 7.3147 17.0906 7.4285 16.4239 M20.9442 22.6955 C20.7352 22.6725 20.523 22.6552 20.3079 22.6438 C19.0728 24.0591 18.4221 25.649 18.241 27.2799 C18.1017 28.5347 18.2403 29.8138 18.6045 31.0566 M7.4285 16.4239 C3.3129 14.4751 0.1578 9.9573 0 5.4927 C5.3308 4.1546 10.2031 5.4399 13.1097 8.2427 M7.4285 16.4239 C8.5421 16.9512 9.7877 17.2955 11.0017 17.3946 M7.4285 16.4239 C8.5408 16.9499 9.7893 17.2955 11.0017 17.3946 M15.1727 11.1876 C16.6107 10.2736 18.2691 9.7736 20.1161 9.7736 C21.6177 9.7736 23.2417 10.2035 24.492 10.8261 M13.1097 8.2427 C13.993 9.0945 14.6948 10.0865 15.1727 11.1876 C15.7474 12.5119 15.9984 13.994 15.8518 15.5799 M11.0017 17.3946 C10.9664 17.8312 10.9664 18.2251 10.9664 18.6591 C10.9664 22.8709 14.1908 26.3647 18.241 27.2799 M11.0017 17.3946 C12.5591 17.5114 14.1646 17.2322 15.7333 16.4477 C15.7861 16.1553 15.8254 15.8659 15.8518 15.5799 M26.4047 25.125 C28.1867 23.5038 29.2657 21.2175 29.2657 18.6591 C29.2657 16.3523 28.3148 14.1452 26.8306 12.5658 M26.4047 25.125 C24.9837 23.8111 23.134 22.9369 20.9442 22.6955 M24.492 10.8261 C24.5406 11.0343 24.6125 11.4719 24.7189 11.8989 M20.9442 22.6955 C20.7289 21.6447 20.588 20.489 20.5185 19.1193 M20.9442 22.6955 C21.4497 25.1613 22.3657 27.0488 23.7357 29.7661 M20.5185 19.1193 C18.7937 17.7132 17.3214 16.5831 15.8518 15.5799 M20.5185 19.1193 C21.0287 16.5423 22.6096 14.1427 24.7189 11.8989 M15.8518 15.5799 C13.4713 13.9548 11.0975 12.6623 7.6684 11.0683 M24.7189 11.8989 C26.893 9.5861 29.6286 7.4386 32.3322 5.4326" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 140">
|
||||
<path d="M19 0 L181 0 C182.6569 0 184 1.3431 184 3 L184 137 C184 138.6569 182.6569 140 181 140 L19 140 C17.3431 140 16 138.6569 16 137 L16 3 C16 1.3431 17.3431 0 19 0 M29 0 L29 140 M171 0 L171 140 M0 3 L16 3 M0 18 L16 18 M0 33 L16 33 M0 48 L16 48 M0 63 L16 63 M0 78 L16 78 M0 93 L16 93 M0 108 L16 108 M0 123 L16 123 M0 137 L16 137 M184 3 L200 3 M184 18 L200 18 M184 33 L200 33 M184 48 L200 48 M184 63 L200 63 M184 78 L200 78 M184 93 L200 93 M184 108 L200 108 M184 123 L200 123 M184 137 L200 137 M51 70 L101 46 L151 70 L101 94 L51 70 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 710 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 35">
|
||||
<path d="M6 29 L50 17.5 M94 6 L50 17.5 M6 6 L50 17.5 M94 29 L50 17.5 M2.5 0 L97.5 0 C98.8807 0 100 1.1193 100 2.5 L100 32.5 C100 33.8807 98.8807 35 97.5 35 L2.5 35 C1.1193 35 0 33.8807 0 32.5 L0 2.5 C0 1.1193 1.1193 0 2.5 0 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 400 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 25">
|
||||
<path d="M16 0 L16 3.5 C16 4.8807 14.8807 6 13.5 6 L12.5 6 C11.1193 6 10 4.8807 10 3.5 L10 0 M90 0 L90 3.5 C90 4.8807 88.8807 6 87.5 6 L86.5 6 C85.1193 6 84 4.8807 84 3.5 L84 0 M2.5 0 L97.5 0 C98.8807 0 100 1.1193 100 2.5 L100 22.5 C100 23.8807 98.8807 25 97.5 25 L2.5 25 C1.1193 25 0 23.8807 0 22.5 L0 2.5 C0 1.1193 1.1193 0 2.5 0 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 508 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90">
|
||||
<path d="M0 2.5 C0 1.1193 1.1193 0 2.5 0 L87.5 0 C88.8807 0 90 1.1193 90 2.5 L90 87.5 C90 88.8807 88.8807 90 87.5 90 L2.5 90 C1.1193 90 0 88.8807 0 87.5 L0 2.5 Z M10 12.5 C10 11.1193 11.1193 10 12.5 10 L77.5 10 C78.8807 10 80 11.1193 80 12.5 L80 77.5 C80 78.8807 78.8807 80 77.5 80 L12.5 80 C11.1193 80 10 78.8807 10 77.5 L10 12.5 Z M29 24.5 C29 26.9853 26.9853 29 24.5 29 C22.0147 29 20 26.9853 20 24.5 C20 22.0147 22.0147 20 24.5 20 C26.9853 20 29 22.0147 29 24.5 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 641 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 45">
|
||||
<path d="M33 0.0004 L33 10.3505 C33 11.5931 31.8807 12.6005 30.5 12.6005 L29.5 12.6005 C28.1193 12.6005 27 11.5931 27 10.3505 L27 0.0004 M39 9.0005 L39 5.8504 C39 4.6078 37.8807 3.6004 36.5 3.6004 L33 3.6004 M27 3.6004 L23.5 3.6004 C22.1193 3.6004 21 4.6078 21 5.8504 L21 9.0005 M26.9512 9 L7.5 9 C6.1193 9 5 10.0074 5 11.25 L5 30.6 C5 36.0676 9.9249 40.5 16 40.5 L44 40.5 C50.0751 40.5 55 36.0676 55 30.6 L55 11.25 C55 10.0074 53.8807 9 52.5 9 L33.0488 9 M2.5 0 L57.5 0 C58.8807 0 60 1.0074 60 2.25 L60 31.5 C60 38.9559 53.2843 45 45 45 L15 45 C6.7157 45 0 38.9559 0 31.5 L0 2.25 C0 1.0074 1.1193 0 2.5 0 Z M34 25.2 C34 27.1882 32.2091 28.8 30 28.8 C27.7909 28.8 26 27.1882 26 25.2 C26 23.2118 27.7909 21.6 30 21.6 C32.2091 21.6 34 23.2118 34 25.2 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 924 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 90">
|
||||
<path d="M90 17.0079 L90 79.3701 M90 17.0079 L90 4.252 M90 17.0079 C90 19.3562 88.0812 21.2598 85.7143 21.2598 L27.8571 21.2598 C26.3186 21.2598 24.8559 20.9381 23.5335 20.3588 M90 17.0079 C90 19.3562 91.9188 21.2598 94.2857 21.2598 L152.143 21.2598 C153.681 21.2598 155.144 20.9381 156.467 20.3588 M90 79.3701 C90 85.2408 85.203 90 79.2857 90 L27.8571 90 C22.3527 90 17.8177 85.8818 17.2117 80.5815 M90 79.3701 C90 85.2408 94.7969 90 100.714 90 L152.143 90 C157.647 90 162.182 85.8818 162.788 80.5815 M90 4.252 C90 1.9037 88.0812 0 85.7143 0 L27.8571 0 C21.9398 0 17.1429 4.7592 17.1429 10.6299 C17.1429 10.9334 17.1557 11.2339 17.1808 11.531 M90 4.252 C90 1.9037 91.9188 0 94.2857 0 L152.143 0 C158.06 0 162.857 4.7592 162.857 10.6299 C162.857 10.9334 162.844 11.2339 162.819 11.531 M162.819 11.531 C164.142 10.9516 165.604 10.6299 167.143 10.6299 L169.286 10.6299 C175.203 10.6299 180 15.3891 180 21.2598 L180 70.8661 C180 76.7369 175.203 81.4961 169.286 81.4961 L167.143 81.4961 C165.592 81.4961 164.119 81.1693 162.788 80.5815 M162.788 80.5815 C159.041 78.9257 156.429 75.1986 156.429 70.8661 L156.429 21.2598 C156.429 20.9564 156.441 20.6558 156.467 20.3588 C156.801 16.4037 159.318 13.0646 162.819 11.531 M17.1808 11.531 C20.6817 13.0646 23.1989 16.4037 23.5335 20.3588 C23.5586 20.6558 23.5714 20.9564 23.5714 21.2598 L23.5714 70.8661 C23.5714 75.1986 20.959 78.9257 17.2117 80.5815 C15.8812 81.1693 14.4076 81.4961 12.8571 81.4961 L10.7143 81.4961 C4.7969 81.4961 0 76.7369 0 70.8661 L0 21.2598 C0 15.3891 4.7969 10.6299 10.7143 10.6299 L12.8571 10.6299 C14.3956 10.6299 15.8584 10.9516 17.1808 11.531 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 260 170">
|
||||
<path d="M92.5424 17.6061 L92.5424 83.6288 M92.5424 17.6061 L92.5424 4.4015 M92.5424 17.6061 C92.5424 20.0369 90.5694 22.0076 88.1356 22.0076 L28.6441 22.0076 C27.0621 22.0076 25.558 21.6745 24.1983 21.0748 M92.5424 17.6061 C92.5424 20.0369 94.5154 22.0076 96.9492 22.0076 L163.051 22.0076 C165.485 22.0076 167.458 20.0369 167.458 17.6061 M92.5424 83.6288 C92.5424 89.706 87.6099 94.6325 81.5254 94.6325 L28.6441 94.6325 C22.9841 94.6325 18.321 90.3695 17.6979 84.8827 M92.5424 83.6288 C92.5424 89.706 97.4748 94.6325 103.559 94.6325 L156.441 94.6325 C162.525 94.6325 167.458 89.706 167.458 83.6288 M167.458 17.6061 L167.458 83.6288 M167.458 17.6061 L167.458 4.4015 M167.458 17.6061 C167.458 20.0369 169.431 22.0076 171.864 22.0076 L231.356 22.0076 C232.938 22.0076 234.442 21.6745 235.802 21.0748 M167.458 83.6288 L167.458 158.996 C167.458 165.073 172.39 170 178.475 170 L231.356 170 C237.016 170 241.679 165.737 242.302 160.25 L242.302 84.8827 M92.5424 4.4015 C92.5424 1.9706 90.5694 0 88.1356 0 L28.6441 0 C22.5596 0 17.6271 4.9266 17.6271 11.0038 C17.6271 11.3179 17.6403 11.629 17.6661 11.9365 M92.5424 4.4015 C92.5424 1.9706 94.5154 0 96.9492 0 L163.051 0 C165.485 0 167.458 1.9706 167.458 4.4015 M167.458 4.4015 C167.458 1.9706 169.431 0 171.864 0 L231.356 0 C237.44 0 242.373 4.9266 242.373 11.0038 C242.373 11.311 242.398 11.6185 242.373 11.9194 M242.302 84.8827 C243.67 85.4913 245.185 85.8295 246.78 85.8295 L248.983 85.8295 C255.068 85.8295 260 80.9029 260 74.8257 L260 22.0076 C260 15.9303 255.068 11.0038 248.983 11.0038 L246.78 11.0038 C241.01 11.0038 236.276 15.4342 235.802 21.0748 C235.776 21.3823 235.763 21.6934 235.763 22.0076 L235.763 74.8257 C235.763 79.3106 238.449 83.1688 242.302 84.8827 Z M17.6661 11.9365 C16.3064 11.3368 14.8023 11.0038 13.2203 11.0038 L11.017 11.0038 C4.9325 11.0038 0 15.9303 0 22.0076 L0 74.8257 C0 80.9029 4.9325 85.8295 11.0169 85.8295 L13.2203 85.8295 C14.8146 85.8295 16.3298 85.4913 17.6979 84.8827 C21.551 83.1688 24.2373 79.3106 24.2373 74.8257 L24.2373 22.0076 C24.2373 21.6934 24.2241 21.3823 24.1983 21.0748 C23.8542 16.9807 21.2659 13.5241 17.6661 11.9365 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |