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>
82 lines
2.5 KiB
YAML
82 lines
2.5 KiB
YAML
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
|