Пара 17 / декабрь
CI: автоматические проверки изменения Создать проверяемый pipeline с тестами и сборкой и объяснить границу доверия CI.
Создать проверяемый pipeline с тестами и сборкой и объяснить границу доверия CI. Continuous Integration означает частое объединение изменений с автоматической обратной связью. Workflow запускается по событию, job выполняет шаги на runner, шаг может вызвать команду или action. Для StudyPulse проверяем unittest и сборку образа. Зелёный pipeline означает, что конкретные проверки прошли для конкретного SHA, а не что всё на свете исправно. Ранние быстрые тесты дают обратную связь до дорогой сборки. Если нет внешнего хостинга, тот же маршрут работает через bash ci/check.sh локально — идея важнее доступности платформы.
Контекст Каждое изменение проходит одинаковые необходимые проверки.
Создать проверяемый pipeline с тестами и сборкой и объяснить границу доверия CI.
Continuous Integration означает частое объединение изменений с автоматической обратной связью. Workflow запускается по событию, job выполняет шаги на runner, шаг может вызвать команду или action. Для StudyPulse проверяем unittest и сборку образа. Зелёный pipeline означает, что конкретные проверки прошли для конкретного SHA, а не что всё на свете исправно. Ранние быстрые тесты дают обратную связь до дорогой сборки. Если нет внешнего хостинга, тот же маршрут работает через bash ci/check.sh локально — идея важнее доступности платформы.
CI — общий проверяемый маршрут Каждое изменение проходит одинаковые необходимые проверки.
01 Commit / PR
→ 02 Тесты
→ 03 Сборка
→ 04 Отчёт для SHA
Аналогия: единый контроль качества партии перед передачей дальше.
Continuous Integration означает частое объединение изменений с автоматической обратной связью. Workflow запускается по событию, job выполняет шаги на runner, шаг может вызвать команду или action. Для StudyPulse проверяем unittest и сборку образа. Зелёный pipeline означает, что конкретные проверки прошли для конкретного SHA, а не что всё на свете исправно. Ранние быстрые тесты дают обратную связь до дорогой сборки. Если нет внешнего хостинга, тот же маршрут работает через bash ci/check.sh локально — идея важнее доступности платформы.
Тестирует поведение, а не уверенность автора Проверяем контракт и случаи отказа.
01 Контракт
→ 02 Позитивный случай
→ 03 Границы / ошибки
→ 04 Провал → исправление
Аналогия: проверка торможения автомобиля полезнее подписи «тормоза предусмотрены».
Тест должен поймать важный дефект: неверный вход, неожиданный диапазон score, недоступный healthz или неверный артефакт. В проекте есть тесты вычисления и реальных HTTP-запросов. Тест, повторяющий ту же формулу тем же кодом, слабее проверки конкретного ожидаемого результата и границ. Не заменяем тесты красивым статусом или успешным импортом. На паре намеренно меняем коэффициент в копии и видим красный тест; затем исправляем. Ошибка сборки и ошибка теста имеют разные причины и вывод.
Runner и секреты Чужой код в CI не должен получать производственные полномочия.
01 Недоверенный PR
→ 02 Runner для проверки
→ 03 Минимальные права
→ 04 Без deploy-секретов
Аналогия: приёмная для посетителей не должна выдавать ключи от серверной.
Workflow запускает команды из репозитория и зависит от действий платформы. permissions: contents: read ограничивает встроенный токен для проверки. Не используем pull_request_target для выполнения недоверенного кода с секретами. Перед production фиксируем actions по SHA и обновляем их осознанно; теги удобны, но меняются. Учебный workflow не публикует образ и не подключается к серверу: он не нуждается в deploy-key. Не запускаем сторонние PR на runner с доступом к личному Docker socket и production-сети. Это объяснение границы доверия, без построения сложной корпоративной инфраструктуры.
Команды и наблюдения Ubuntu / Bash, lab
bash ci/check.sh
python3 -m unittest discover -s tests -v
docker build -t studypulse:ci .
git diff -- .github/workflows/ci.yml
# Change a tested behavior in a disposable copy, rerun, then restore itЛокальный CI не требует GitHub-аккаунта. Публикация репозитория внешнему сервису — необязательное упражнение студента. Ожидаемое наблюдение: Тесты проходят на исходном проекте; намеренный дефект даёт красный результат; сборка проверяется отдельно.
Разбор результата Тесты проходят на исходном проекте; намеренный дефект даёт красный результат; сборка проверяется отдельно.
Локальный CI не требует GitHub-аккаунта. Публикация репозитория внешнему сервису — необязательное упражнение студента.
Базовый tests/test_app.py проверяет диапазон, типы, boolean/NaN и HTTP-статусы. Не исправляйте тест под ошибочный код. После возврата проверки входа запустите bash ci/check.sh и git diff перед commit. При недоступности registry Docker отделите проблему скачивания образа от результата Python-тестов, зафиксируйте оба статуса честно.
На macOS Локальные тесты и сборка доступны. CI на ubuntu-24.04 остаётся Linux-средой, даже если разработчик работает на Mac. Различия регистра файлов и ARM/AMD64 нужно учитывать до выпуска.
python3 -m unittest discover -s tests -v
bash ci/check.shКоманды macOS необходимо проверить на конкретном устройстве. Локальные тесты и сборка доступны. CI на ubuntu-24.04 остаётся Linux-средой, даже если разработчик работает на Mac. Различия регистра файлов и ARM/AMD64 нужно учитывать до выпуска.
Красный → зелёный Запустите предоставленный ci/check.sh. В отдельной ветке измените обработку hours=-1 так, чтобы она стала ошибочной. Найдите тест, который обнаружил дефект; объясните его смысл. Исправьте код, повторите тесты и сборку; оформите fix: commit. Работа в парах: один оператор, второй проверяет гипотезу. Смена ролей в середине. Базовый tests/test_app.py проверяет диапазон, типы, boolean/NaN и HTTP-статусы. Не исправляйте тест под ошибочный код. После возврата проверки входа запустите bash ci/check.sh и git diff перед commit. При недоступности registry Docker отделите проблему скачивания образа от результата Python-тестов, зафиксируйте оба статуса честно.
Что должно получиться Вывод упавшей и успешной проверки для конкретного commit.
Тест ловит реальный дефект контракта; в workflow нет production-секретов.
Базовый tests/test_app.py проверяет диапазон, типы, boolean/NaN и HTTP-статусы. Не исправляйте тест под ошибочный код. После возврата проверки входа запустите bash ci/check.sh и git diff перед commit. При недоступности registry Docker отделите проблему скачивания образа от результата Python-тестов, зафиксируйте оба статуса честно.
Ошибка для диагностики Сборку объявляют доказательством корректности входных данных.
Симптом → Гипотеза → Проверка
Базовый tests/test_app.py проверяет диапазон, типы, boolean/NaN и HTTP-статусы. Не исправляйте тест под ошибочный код. После возврата проверки входа запустите bash ci/check.sh и git diff перед commit. При недоступности registry Docker отделите проблему скачивания образа от результата Python-тестов, зафиксируйте оба статуса честно.
Основные выводы Что заявленные проверки прошли для конкретного состояния, не абсолютную исправность. Чтобы ошибка или недоверенный код имели меньше полномочий. Что заявленные проверки прошли для конкретного состояния, не абсолютную исправность.
Чтобы ошибка или недоверенный код имели меньше полномочий.
После пары Добавить один осмысленный тест edge-case и объяснить, какой дефект он предотвращает.
Конспект, команды и разбор → Проверьте критерии сдачи и дайте возможность повторной попытки. Первоисточники: https://docs.github.com/en/actions/get-started/understand-github-actions
Пара 18 / декабрь
CD: версии, выпуск и откат Выпустить новую версию локального сервиса и безопасно вернуть предыдущую.
Выпустить новую версию локального сервиса и безопасно вернуть предыдущую. Continuous Delivery поддерживает состояние, которое можно выпустить после решения команды. Continuous Deployment автоматически выпускает прошедшие проверки изменения согласно правилам. На курсе делаем учебный ручной выпуск после CI. Пайплайн должен иметь явный артефакт и SHA: нельзя собирать неопределённый latest уже на сервере и считать это проверенной версией. Продвигаем один и тот же образ между средами, конфигурацию передаём отдельно. Production-развёртывание требует подходящих полномочий и проверок, но здесь все аварии происходят на ноутбуке.
Контекст Готовность к выпуску и автоматический выпуск — разные договорённости.
Выпустить новую версию локального сервиса и безопасно вернуть предыдущую.
Continuous Delivery поддерживает состояние, которое можно выпустить после решения команды. Continuous Deployment автоматически выпускает прошедшие проверки изменения согласно правилам. На курсе делаем учебный ручной выпуск после CI. Пайплайн должен иметь явный артефакт и SHA: нельзя собирать неопределённый latest уже на сервере и считать это проверенной версией. Продвигаем один и тот же образ между средами, конфигурацию передаём отдельно. Production-развёртывание требует подходящих полномочий и проверок, но здесь все аварии происходят на ноутбуке.
Delivery и deployment Готовность к выпуску и автоматический выпуск — разные договорённости.
01 CI
→ 02 Версионированный образ
→ 03 Решение о выпуске
→ 04 Проверка пользователя
Аналогия: партия принята контролем качества, но отправка покупателю — следующий этап.
Continuous Delivery поддерживает состояние, которое можно выпустить после решения команды. Continuous Deployment автоматически выпускает прошедшие проверки изменения согласно правилам. На курсе делаем учебный ручной выпуск после CI. Пайплайн должен иметь явный артефакт и SHA: нельзя собирать неопределённый latest уже на сервере и считать это проверенной версией. Продвигаем один и тот же образ между средами, конфигурацию передаём отдельно. Production-развёртывание требует подходящих полномочий и проверок, но здесь все аварии происходят на ноутбуке.
Идентичность версии Tag удобен человеку; digest задаёт конкретное содержимое образа.
01 Commit SHA
02 Image digest
03 Artifact checksum
04 Конфигурация
Аналогия: название книги и конкретное издание с номером партии — разные идентификаторы.
Используем понятные теги v1 и v2 для обучения, фиксируем image ID или digest как доказательство. Tag может быть переназначен, latest не означает «проверено». В реальном ML-проекте отдельно фиксируются SHA кода и версия/хеш модели: смена модели может изменить ответы без смены кода. Для нашего JSON-артефакта ведём метку MODEL_VERSION и sha256 файла. Метка не доказывает правильность содержимого, поэтому вычисляем контрольную сумму. В журнале выпуска записываем исходную и целевую версии, проверку и план возврата.
Откат и данные Предыдущий образ можно вернуть, но данные требуют совместимости.
01 Предыдущая версия
→ 02 Выпуск новой
→ 03 Smoke-проверка
→ 04 Откат при отказе
Аналогия: вернуть старый кассовый аппарат легко, но формат новых чеков должен быть ему понятен.
Rollback возвращает прежнюю версию приложения или артефакта. Если новая версия необратимо изменила данные, старый код может не работать: план возврата должен учитывать это до выпуска. В курсе формат событий стабилен, поэтому демонстрационный откат не меняет том. Сначала проверяем healthz, затем типичный predict, неверный вход и журнал. Запущенный контейнер — промежуточное наблюдение, окончательное доказательство — ожидаемый пользовательский ответ. Когда ошибка затрагивает данные, backup и восстановление становятся отдельной операцией; это тема декабря.
Команды и наблюдения Ubuntu / Bash, lab
bash ci/check.sh
docker build -t studypulse:v1 .
APP_IMAGE=studypulse:v1 MODEL_VERSION=v1 docker compose up -d --no-build
bash scripts/smoke.sh
APP_IMAGE=studypulse:v1 MODEL_VERSION=v2 docker compose up -d --no-build
bash scripts/smoke.sh
APP_IMAGE=studypulse:v1 MODEL_VERSION=v1 docker compose up -d --no-build
bash scripts/smoke.shСначала показываем безопасную смену конфигурации на одном образе. Для настоящего изменения кода в лабораторной строим второй образ и фиксируем оба ID. Ожидаемое наблюдение: healthz показывает смену метки v1 → v2 → v1; данные в томе сохраняются.
Разбор результата healthz показывает смену метки v1 → v2 → v1; данные в томе сохраняются.
Сначала показываем безопасную смену конфигурации на одном образе. Для настоящего изменения кода в лабораторной строим второй образ и фиксируем оба ID.
Вариант v2: измените коэффициент в отдельной копии на 0.1, обновите соответствующий контракт и тесты осознанно, соберите studypulse:v2. После выпуска ожидаемый score для hours=4 станет 0.4; стандартный smoke базового контракта поймает несовместимость. Верните APP_IMAGE=studypulse:v1 MODEL_VERSION=v1 и выполните базовый smoke: score снова 0.5. Если изменение нежелательно, тесты и контракт не переписывают ради зелёного статуса.
На macOS Вместо sha256sum используйте shasum -a 256. Release и rollback через Docker одинаковы. Не выдавайте локальный ARM64 image ID за доказательство доступности той же версии для AMD64-сервера.
shasum -a 256 models/model.json
docker image inspect studypulse:v1 --format "{{.Id}}"
bash scripts/smoke.shКоманды macOS необходимо проверить на конкретном устройстве. Вместо sha256sum используйте shasum -a 256. Release и rollback через Docker одинаковы. Не выдавайте локальный ARM64 image ID за доказательство доступности той же версии для AMD64-сервера.
Мини-релиз с планом возврата Зафиксируйте commit, image ID и checksum models/model.json. Соберите отдельный v2 с небольшим проверяемым изменением в копии проекта; не подменяйте v1. Выпустите v2 локально, выполните smoke и проверьте события. Верните v1 без удаления тома; заполните release-report.md. Работа в парах: один оператор, второй проверяет гипотезу. Смена ролей в середине. Вариант v2: измените коэффициент в отдельной копии на 0.1, обновите соответствующий контракт и тесты осознанно, соберите studypulse:v2. После выпуска ожидаемый score для hours=4 станет 0.4; стандартный smoke базового контракта поймает несовместимость. Верните APP_IMAGE=studypulse:v1 MODEL_VERSION=v1 и выполните базовый smoke: score снова 0.5. Если изменение нежелательно, тесты и контракт не переписывают ради зелёного статуса.
Что должно получиться Два образа, отчёт о выпуске и проверенный откат.
Возврат реально выполнен; image ID и артефакт записаны; данные не удалены.
Вариант v2: измените коэффициент в отдельной копии на 0.1, обновите соответствующий контракт и тесты осознанно, соберите studypulse:v2. После выпуска ожидаемый score для hours=4 станет 0.4; стандартный smoke базового контракта поймает несовместимость. Верните APP_IMAGE=studypulse:v1 MODEL_VERSION=v1 и выполните базовый smoke: score снова 0.5. Если изменение нежелательно, тесты и контракт не переписывают ради зелёного статуса.
Ошибка для диагностики latest изменился, и невозможно определить, что именно было выпущено.
Симптом → Гипотеза → Проверка
Вариант v2: измените коэффициент в отдельной копии на 0.1, обновите соответствующий контракт и тесты осознанно, соберите studypulse:v2. После выпуска ожидаемый score для hours=4 станет 0.4; стандартный smoke базового контракта поймает несовместимость. Верните APP_IMAGE=studypulse:v1 MODEL_VERSION=v1 и выполните базовый smoke: score снова 0.5. Если изменение нежелательно, тесты и контракт не переписывают ради зелёного статуса.
Основные выводы Нет, различается автоматизация последнего решения о выпуске. Новые данные или миграция могут быть несовместимы со старым кодом. Нет, различается автоматизация последнего решения о выпуске.
Новые данные или миграция могут быть несовместимы со старым кодом.
После пары Сдать ноябрьский чекпоинт: Git-история, Dockerfile, Compose, CI и журнал проверенного отката.
Конспект, команды и разбор → Проверьте критерии сдачи и дайте возможность повторной попытки. Первоисточники: https://docs.github.com/en/actions https://docs.docker.com/build/building/tagging/