Настоящий workflow

Измените код файла main.yaml: теперь workflow будет проверять код проекта на соответствие PEP8 и запускать тесты.
main.yaml
Скопировать кодYAML
name: Flask-app workflow on: [push] jobs: tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: 3.8 - name: Install dependencies run: | python -m pip install --upgrade pip pip install flake8 pytest pip install -r requirements.txt - name: Lint with flake8 run: flake8 . - name: Test with pytest run: pytest tests.py
В коде workflow для клонирования репозитория применён предустановленный action checkout, а для установки Python-окружения — action setup-python. Команды для выполнения этих операций можно прописать вручную прямо в workflow, но проще применить готовые скрипты.
Детальнее познакомиться с этими actions можно здесь:
checkout: https://github.com/actions/checkout
setup-python: https://github.com/actions/setup-python
Для последовательного запуска команд применяется такой синтаксис:
Скопировать кодYAML
run: | команда1 команда2 команда3
В шаге Install dependencies (англ. «установка зависимостей») сначала обновляется pip, затем устанавливаются необходимые пакеты для тестов. После этого идёт установка всех зависимостей из requirements.txt.

Тесты для flask-проекта

В головной директории проекта создайте файл tests.py и добавьте туда пару тестов, чтобы проверить, как всё работает.
Первый тест проверяет, что при GET-запросе на корневой URL возвращается строка «У меня получилось!».
Второй тест проверяет, что при POST-запросе к главной странице проекта возвращается ошибка «код 405, метод не разрешен».
Скопировать кодPYTHON
import unittest import hello as tested_app class FlaskAppTests(unittest.TestCase): def setUp(self): tested_app.app.config['TESTING'] = True self.app = tested_app.app.test_client() def test_get(self): r = self.app.get('/') self.assertEqual(r.data.decode('utf-8'), 'У меня получилось!') def test_post(self): r = self.app.post('/') self.assertEqual(r.status_code, 405) if __name__ == '__main__': unittest.main()
Отлично! Теперь создайте Dockerfile в корневой директории проекта.
Скопировать кодPYTHON
FROM python:3.8-alpine COPY ./ /app RUN pip install -r /app/requirements.txt EXPOSE 5000 CMD python /app/hello.py
Теперь сбилдите образ командой docker build ., затем запустите и зайдите в браузере на http://0.0.0.0:5000
Скопировать кодPYTHON
docker run -p 5000:5000 <CONTAINER ID>
Теперь можно запушить все файлы в репозиторий и убедиться, что ошибок нет.

Автоматическая сборка и пуш docker-образа

Если тесты прошли успешно и код обновлён — нужно обновить docker-образ с проектом на DockerHub.
По умолчанию все jobs запускаются одновременно. Но чаще требуется последовательный запуск: например, задача tests должна быть запущена только после успешного выполнения задачи build, а deploy — только по окончании tests.
Для определения последовательности запуска есть ключ needs, в нём указывается имя того job, после выполнения которого должна запуститься описываемая задача.
Скопировать кодYAML
name: CI on: push: branches: [ main ] ... jobs: tests: runs-on: ubuntu-latest steps: - ... build: runs-on: ubuntu-latest needs: tests # не запускать сразу, ждать, пока выполнится tests steps: - ... deploy: runs-on: ubuntu-latest needs: build # не запускать сразу, ждать, пока выполнится build steps: - ...
Добавьте в workflow новый job для автоматической пересборки и обновления образа на DockerHub. Он должен выполниться только после успешного прохождения тестов.
Скопировать кодYAML
build_and_push_to_docker_hub: name: Push Docker image to Docker Hub runs-on: ubuntu-latest needs: tests steps: - name: Check out the repo uses: actions/checkout@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Login to Docker uses: docker/login-action@v1 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Push to Docker Hub uses: docker/build-push-action@v2 with: push: true tags: <логин-на-docker-hub>/<репозиторий-на-docker-hub>:latest
Приватные данные, переменные DOCKER_USERNAME и DOCKER_PASSWORD, нужно сохранить в надёжном месте. Для этого в GitHub Actions есть система Secrets.

Как хранить секреты в GitHub Actions

На платформе GitHub Actions можно хранить секреты (переменные окружения, которые содержат приватные данные) в зашифрованном виде прямо в репозитории, в системе Secrets, это очень удобно.
Создавать Secrets можно только в собственных репозиториях.
  1. Перейдите в настройки репозитория Settings, выберите на панели слева Secrets, нажмите New secret:
image
  1. Задайте имя секрета и его значение и нажмите Add secret. Сохраните переменные DOCKER_USERNAME и DOCKER_PASSWORD с необходимыми значениями.
image
Секрет появится в списке:
image
Обращаться к нему можно так: ${{ secrets.<имя-токена> }}
Правила именования Secrets:
  • Названия могут содержать цифры и символы английского алфавита в любом регистре, а также символ нижнего подчеркивания. Рекомендуется использовать символы верхнего регистра, как для констант.
  • Названия не могут начинаться с цифры и с префикса GITHUB_

Deploy

Ваш проект проверяется тестами, образ автоматически обновляется в Docker Hub, теперь можно настроить автоматический деплой проекта: проект должен деплоиться, когда в Git происходит пуш в ветку main.
Чтобы обновить код на боевом сервере, нужно подтянуть туда новый Docker-образ с сервера GitHub Actions, выполнив на сервере команду docker pull, остановить старый контейнер и запустить новый.
Этот процесс можно автоматизировать с помощью GitHub Actions, применив специальный action для выполнения ssh-команд.
Ваш боевой сервер на Яндекс.Облаке должен получить данные с «незнакомого» временного сервера GitHub Actions. Для безопасного соединения нужно добавить на сервер GitHub Actions ssh-ключ с боевого сервера.
Сохраните ключ с боевого сервера в Secrets на GitHub Actions:
  1. Скопируйте приватный ключ со своего сервера в Яндекс.Облаке:
    Скопировать кодPYTHON
    cat ~/.ssh/id_rsa
  2. На GitHub Actions сохраните ключ в Secrets в переменную SSH_KEY.
  3. На GitHub Actions сохраните ещё две переменные:
    • в переменной USER сохраните имя пользователя для подключения к серверу
    • в переменной HOST сохраните IP-адрес вашего сервера. Если при создании ssh-ключа вы использовали фразу-пароль, то добавьте также переменную PASSPHRASE.
  4. Добавьте ещё один job в workflow:
    Скопировать кодYAML
    deploy: runs-on: ubuntu-latest needs: build_and_push_to_docker_hub steps: - name: executing remote ssh commands to deploy uses: appleboy/ssh-action@master with: host: ${{ secrets.HOST }} username: ${{ secrets.USER }} key: ${{ secrets.SSH_KEY }} passphrase: ${{ secrets.PASSPHRASE }} # если ваш ssh-ключ защищен фразой-паролем source: "docker-compose.yaml, nginx/default.conf" target: "~/app/" script: | sudo docker pull <имя-пользователя>/<имя-репозитория> sudo docker stop $(sudo docker ps -a -q) sudo docker run --rm -d -p 5000:5000 <имя-пользователя>/<имя-репозитория>
Готово!
Запустите workflow и убедитесь, что всё работает.
После этого попробуйте изменить что-то в коде, например добавьте ещё два восклицательных знака (не забудьте также добавить их в тесты!), затем выполните git push — и через пару минут код обновится на сервере.
Вы можете открыть в репозитории вкладку Actions и наблюдать за выполнением всех стадий.

Отправка отчета

Остался финальный шаг. Неудобно каждый раз отслеживать процесс на GitHub и ждать завершения всех шагов, чтобы посмотреть результат. Можно попросить бота отправить оповещение об успешном выполнении всех шагов workflow.
А у вас как раз уже есть готовый бот! Хватит ему бездельничать.
В маркетплейсе GitHub Actions есть специальный action для отправки уведомлений в Telegram: https://github.com/marketplace/actions/telegram-message-notify
Добавьте четвёртый шаг в workflow:
Скопировать кодYAML
send_message: runs-on: ubuntu-latest needs: deploy steps: - name: send message uses: appleboy/telegram-action@master with: to: ${{ secrets.TELEGRAM_TO }} token: ${{ secrets.TELEGRAM_TOKEN }} message: ${{ github.workflow }} успешно выполнен!
Зайдите в Settings → Secrets в вашем репозитории и установите секретные переменные:
  • чтобы бот отправил сообщение именно вам — в переменную TELEGRAM_TO добавьте ID своего телеграм-аккаунта. Узнать свой ID можно у бота @userinfobot.
  • в переменную TELEGRAM_TOKEN добавьте токен вашего бота. Получить этот токен можно у бота @BotFather.
Если вы хотите отправлять сообщение в Slack или куда-то ещё — найдите нужный action в маркетплейсе GitHub Actions и подключите его по инструкции.
Вот теперь точно всё! Ваш workflow состоит из четырёх шагов:
  1. Тестирование проекта
  2. Сборка и публикация образа
  3. Автоматический деплой
  4. Отправка уведомления в персональный чат
Ну разве не чудесно?
Настройка workflow потребовала от вас времени и усилий, однако в процессе работы над проектом от автоматизации процессов вы получите многократный выигрыш.