Главная/Проекты/Чеклист

Проект 4. Список проверок

Проходить после реализации и до чтения SOLUTION.md.

Скрипты конвейера, вычисление тегов, сводка и отказы развёртывания проверены фактическим запуском на машине курса. Docker, trivy и syft отсутствуют — поэтому этапы сборки, проверки образа и сканирования дают код 3, и это ровно тот исход, который проект учит различать.

Подготовка

bash
ci/pipeline.sh; echo "код: $?"

1. Переносимость

Что проверяется: логика в скриптах, а не в шагах YAML.

bash
python3 -c "
import yaml
from pathlib import Path
d = yaml.safe_load(Path('.github/workflows/pipeline.yaml').read_text())
steps = d['jobs']['pipeline']['steps']
runs = [s['run'] for s in steps if isinstance(s.get('run'), str)]
via = [r for r in runs if r.startswith('ci/')]
print(f'шагов: {len(steps)}, с командами: {len(runs)}, вызывающих ci/: {len(via)}')
for r in runs:
    if not r.startswith('ci/'):
        print('  логика в YAML:', r.splitlines()[0][:60])"
text
шагов: 13, с командами: 8, вызывающих ci/: 6
  логика в YAML: pip install -r requirements-dev.txt
  логика в YAML: ref="$(cat artifacts/image-ref.txt)"

☐ Каждый содержательный этап — вызов скрипта
ci/pipeline.sh выполняется локально без CI
☐ Оставшиеся команды в YAML — установка и публикация, а не проверки

Про две оставшиеся строки. Установка зависимостей и docker push привязаны к платформе, и выносить их в скрипт особого смысла нет. Важно, что ни одна проверка не живёт в YAML: именно проверки нужно уметь запускать локально.


2. Порядок

bash
grep -n 'run_stage' ci/pipeline.sh
text
30:run_stage "линтер"          ci/lint.sh
31:run_stage "тесты"           ci/test.sh
32:run_stage "сборка"          ci/build.sh
33:run_stage "проверка образа" ci/verify-image.sh
34:run_stage "сканирование"    ci/scan.sh
35:run_stage "состав (SBOM)"   ci/sbom.sh

☐ Быстрые проверки первыми
☐ Сканирование после сборки
☐ Порядок обоснован письменно

Проверка на деле: сломать линтер и замерить, за сколько падает конвейер.

bash
echo "x=" >> app/__init__.py && time ci/pipeline.sh >/dev/null 2>&1
git checkout app/__init__.py

☐ Падение происходит за секунды, а не после сборки

Почему сканирование после сборки, а не раньше. Оно проверяет образ, а не исходники. Между ними Dockerfile, и его ошибки — лишний пакет, секрет в слое, забытый USER — исходниками не ловятся.


3. Три состояния

Главная проверка проекта.

bash
ci/scan.sh; echo "нет сканера: $?"
PATH=/usr/bin:/bin ci/verify-image.sh >/dev/null 2>&1; echo "нет docker: $?"
text
[12:05:20] НЕ ПРОВЕРЕНО: trivy не установлен; образ ghcr.io/example/eventapi:g9c9b7aec7405 не проверялся
нет сканера: 3
нет docker: 3

Итог конвейера:

bash
ci/pipeline.sh 2>/dev/null | tail -12
text
этап             результат
───────────────────────────────
линтер           пройдено
тесты            пройдено
сборка           НЕ ПРОВЕРЕНО
проверка образа  НЕ ПРОВЕРЕНО
сканирование     НЕ ПРОВЕРЕНО
состав (SBOM)    НЕ ПРОВЕРЕНО

пройдено: 2, провалено: 0, не проверено: 4
ИТОГ: конвейер прошёл НЕ ПОЛНОСТЬЮ — 4 этапов не выполнялось
Это не успех. Для публикации образа требуется код 0.

☐ Отсутствие инструмента даёт 3, а не 0
☐ Итог различает три состояния
☐ Код 3 не пускает к публикации
☐ Провал (1) перевешивает непроверенное (3)

Последнее проверяется так:

bash
printf 'тесты\t1\nсканирование\t3\n' | python3 ci/summary.py; echo "код: $?"
text
пройдено: 0, провалено: 1, не проверено: 1
ИТОГ: конвейер провален
код: 1

Если ваш конвейер даёт 0 при отсутствующем сканере — это не мелкая недоработка. Это состояние, при котором зелёная сборка означает «мы перестали проверять», и узнать об этом неоткуда.


4. Состав

bash
ci/sbom.sh; echo "код: $?"
python3 -c "
import json
d = json.load(open('artifacts/sbom.json'))
print('полный:', d.get('complete'))
print('пакетов:', len(d['packages']))
print('пометка:', d['comment'][:60])"
text
[11:57:53] syft не установлен; собираю ЧАСТИЧНЫЙ состав из requirements.txt
пакетов: 5
[11:57:53] НЕ ПРОВЕРЕНО: полный SBOM не собран: syft отсутствует; записан частичный состав
код: 3
полный: False
пакетов: 5
пометка: ЧАСТИЧНЫЙ СОСТАВ: собран из requirements.txt, системные п

☐ SBOM создан
☐ Частичный состав помечен complete: false
☐ Код возврата отличается от успешного

Почему частичный состав — не замена полному. Он не видит системных пакетов базового образа, а уязвимости чаще находят именно там. Файл, выглядящий как SBOM и не являющийся им, хуже отсутствующего: на него сошлются.


5. Проверка образа

bash
ci/verify-image.sh "$IMAGE"

С доступным Docker ожидается:

text
[12:31:02] проверяю образ ghcr.io/example/eventapi:g9c9b7aec7405
  ✓ пользователь задан
  ✓ пользователь числовой
  ✓ пользователь не root
  ✓ точка входа в exec-форме
  ✓ секретов в истории нет
  ✓ работает только для чтения
  ✓ нет оболочки
проверок: 7, провалов: 0

☐ Проверяется собранный образ, а не исходники
☐ Не менее пяти проверок
☐ Отсутствие Docker даёт 3, а не 0

Проверка проверок. Соберите образ с USER app вместо USER 10001 и убедитесь, что пункт «пользователь числовой» падает. Проверка, которая никогда не падает, ничего не проверяет.


6. Теги

bash
bash -c 'source ci/lib.sh; echo "тег: $(image_tag)"'
touch проба.txt
bash -c 'source ci/lib.sh; echo "с неотслеживаемым файлом: $(image_tag)"'
rm проба.txt
bash -c 'source ci/lib.sh; echo "снова: $(image_tag)"'
text
тег: g9c9b7aec7405
с неотслеживаемым файлом: g9c9b7aec7405-dirty
снова: g9c9b7aec7405

☐ Тег содержит SHA коммита
☐ Грязное дерево помечается
Неотслеживаемый файл тоже помечается
☐ Перезаписываемых имён (latest, prod) нет

Про неотслеживаемый файл. git diff его не видит, а в контекст сборки он попадает. Первая редакция скрипта использовала git diff и помечала такое дерево чистым: образ переставал соответствовать коммиту, а тег об этом молчал. Проверять нужно git status --porcelain.


7. Кэш

bash
grep -n 'cache-to' ci/build.sh
text
    --cache-to   "type=registry,ref=${IMAGE_NAME}:buildcache,mode=max" \

cache-from и cache-to заданы
☐ Указан mode=max
☐ Второй запуск конвейера быстрее первого

Про mode=max. Значение по умолчанию — min — экспортирует только последнюю стадию. Слой с зависимостями в кэш не попадает, и это самая частая причина «кэш настроен, а сборка медленная» (урок 16.3).

Отдельно. RUN --mount=type=cache ускоряет сборку локально и не переносится между запусками CI: cache-to экспортирует слои, а не содержимое cache mount. Это разные механизмы, и второй в конвейере не работает.


8. Развёртывание

bash
ci/deploy.sh "ghcr.io/example/eventapi:g9c9b7aec7405"; echo "код: $?"
ci/deploy.sh "ghcr.io/example/eventapi@sha256:aaaa0000bbbb1111cccc2222dddd3333eeee4444ffff5555aaaa6666bbbb7777"; echo "код: $?"
ci/rollback.sh; echo "откат без записи: $?"
text
[12:05:20] ОШИБКА: развёртывание по тегу запрещено: укажите digest (ghcr.io/example/eventapi:g9c9b7aec7405)
код: 1

[12:05:20] окружение: production
[12:05:20] было:  <пусто>
[12:05:20] будет: ghcr.io/example/eventapi@sha256:aaaa…7777
[12:05:20] НЕ ПРОВЕРЕНО: docker недоступен; развёртывание не выполнялось
код: 3

[12:05:21] ОШИБКА: предыдущий digest не записан: откатываться некуда
откат без записи: 1

☐ Развёртывание по тегу отвергается
☐ Развёртывание по digest принимается
☐ Предыдущий digest записывается
☐ Откат без записи отказывается работать

Почему по digest. Тег — изменяемая ссылка. Между «собрали» и «запустили» он может указывать уже на другое: сборка опубликовала повторно, два конвейера победили друг друга, pull выполнен, а container не пересоздан. Digest вычисляется из содержимого — одно значение, один набор байтов (урок 14.4).


9. Тесты конвейера

bash
pytest -q
text
............................                                             [100%]
28 passed

☐ Скрипты конвейера покрыты тестами
☐ Проверены все три состояния сводки
☐ Проверены отказы развёртывания и отката
Тесты проходят при грязном рабочем дереве

Последний пункт проверяется так:

bash
touch грязь.txt && pytest -q; rm грязь.txt
text
28 passed

Почему это важно. Первая редакция тестов тегов проверяла рабочее дерево самого проекта — и падала, стоило отредактировать файл теста. Тест, зависящий от состояния репозитория, ломается от собственной правки; тесты тегов должны работать во временном репозитории.


Сводка

ПроверкаТребованияОтметка
1Переносимость1
2Порядок2
3Три состояния3, 4
4Состав5
5Проверка образа6
6Теги7
7Кэш8
8Развёртывание9, 10
9Тесты конвейера11

Девять из девяти — можно открывать SOLUTION.md.


Навигация

← Техническое задание
Эталонное решение →
Вернуться к проектам
Главное оглавление

Markdown на GitHub ↗