hermes-hub/.github/workflows/ci.yml
ochenstarik-ui 7fb8c6a6c5 feat(ci): матрица Windows + Linux; распаковка обновления ограничена каталогом
P1 после зелёного main.

1. CI-матрица. Обе джобы стояли на windows-latest, и это дорого обошлось:
   инвариант A37 не держался на Windows, а четыре теста молча предполагали
   Linux. Прогон на одной системе не показывал ни того, ни другого. Проект
   работает на Linux и активно получает Linux-правки — теперь обе системы
   проверяются одинаковым набором.

2. Zip-slip из аудита НЕ ВОСПРОИЗВОДИТСЯ — измерено, а не принято на веру.
   Архив с "../", с абсолютным путём и с записью-ссылкой распакован через
   zipfile.extractall: ничего за пределы каталога не вышло, абсолютный путь
   стал относительным, "../" схлопнулись, а запись-ссылка легла обычным
   файлом. CPython санирует пути сам.

   Но это свойство реализации, а не обещание формата, и распаковка идёт в
   корень установки. Граница сделана собственным инвариантом: каждая запись
   проверяется до записи на диск, отклоняются абсолютные пути, выход через
   "..", ссылки и записи не-файлового типа. Инвариант закреплён тестом, а не
   оставлен на усмотрение стандартной библиотеки.

Тесты: 776 -> 777 passed, 2 skipped, 4 deselected. ruff check . чисто.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 19:01:30 +07:00

82 lines
2.5 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Матрица из двух систем.
#
# Обе джобы стояли на windows-latest, и это дорого обошлось: инвариант A37 не
# держался на Windows, а тесты установки и остановки процессов молча
# предполагали Linux. Прогон на одной системе не показывал ни того, ни другого.
# Проект работает на Linux и активно получает Linux-правки, поэтому обе системы
# проверяются одинаковым набором.
jobs:
test:
name: Clean Runner Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev,web]"
- name: Code Quality (ruff)
run: |
ruff check .
- name: Run Hermetic Offline Pytest Suite
run: |
pytest -v
- name: Run Automated Release Gate
run: |
python scripts/release_gate.py
headless:
name: Headless Run (${{ matrix.os }}, no GUI dependencies)
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install dependencies without GUI extras
run: |
python -m pip install --upgrade pip
pip install -e ".[dev,web]"
pip uninstall -y customtkinter
# A test module importing customtkinter at module scope aborts collection
# of the WHOLE session instead of skipping itself, which takes the release
# gate down with it. This job fails if that guard is ever dropped again.
- name: Suite must skip, not abort, without GUI dependencies
run: |
pytest -q