Андрей Трошин Телеграм @Andrey_Totshin Разберем роль CI в жизненном цикле разработки (что в себя включает эта часть процесса, какие задачи решает) и на практике настроим CI для приложения API сервера. В прошлой статье 🏃 Работаем нон-стоп: непрерывная интеграция и непрерывное развертывание кода (CI/CD) мы обзорно познакомились с процессом CICD. Настало время углубиться и расшириться в детали. Начнем с разбора первой половины процесса – это CI (Continuous Deployment). Разберем цели этого процесса, инструменты и на практическом примере реализуем кейс. Роль CI в жизненном цикле разработки Вспомним как выглядит процесс CI. Цели, которые преследует этот процесс следующие: Фиксация всех изменений версий через систему контроля версий. Компиляция кода и обработка ошибок при компиляции. Автоматическое тестирование выполнения кода. Создание артефактов (оповещения, документация и т. д.). Самое главное – это все выполняется полностью в автоматическом режиме. Человек участвует только в первоначальной настройке процесса, то есть при создании unit-тестов. Такой подход гораздо быстрее ручного тестирования, так как позволяет максимально избежать ошибок по вине человека. В итоге внесенное изменение проходит через тесты и если тесты не прошли, всегда можно вернуться на предыдущий коммит в системе контроля версии. В общем-то и все, предлагаю приступить к реализации этого процесса. Настраиваем CI в GitHub Нам потребуется система СКВ (система контроля версий), а также среда, где будет компилироваться наш код и запускаться тесты. Все эти задачи (и еще много чего интересного) полностью предоставляет сервис GitHub. Кейс: Настроить CI с помощью функционала сервиса GitHub для API сервера написанного на Python. Подробно описывать код самого сервера и как залить его на git не буду. Тема статьи о другом. Имеем в репозитории Git следующую структуру файлов: Цветом выделены файлы, с которыми будем работать. Разберем для чего каждый файл. app.py from fastapi import FastAPI import uvicorn from typing import Optional from pydantic import BaseModel import secrets from fastapi import Depends, FastAPI, HTTPException, status from fastapi.security import HTTPBasic, HTTPBasicCredentials class Item(BaseModel): name: str description: Optional[str] = None price: int tax: Optional[float] = None app = FastAPI() security = HTTPBasic() def get_current_username(credentials: HTTPBasicCredentials = Depends(security)): correct_username = secrets.compare_digest(credentials.username, "test") correct_password = secrets.compare_digest(credentials.password, "test") if not (correct_username and correct_password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect email or password", headers={"WWW-Authenticate": "Basic"}, ) return credentials.username @app.get("/") def read_root(credentials: HTTPBasicCredentials = Depends(get_current_username)): return {"Hi": "from API Server)"} @app.get("/test/{item_id}") async def read_item(item_id: int, credentials: HTTPBasicCredentials = Depends(get_current_username)): print(item_id) return {"item_id": item_id} @app.get("/check") def hello(): return "Hello World" @app.post("/items/") async def create_item(item: Item, credentials: HTTPBasicCredentials = Depends(get_current_username)): return item Это основной файл нашего приложения (API сервера). Сервер имеет три endpoint. Мы будем работать с / и check. Endpoint /check необходим как раз для тестов. Пройдемся по основному функционалу приложения. Метод get_current_username необходим для базовой аутентификации по логину и паролю. Далее у нас идут четыре endpoint: @app.get(“/”) @app.get(“/test”) @app.get(“/items”) @app.get(“/check”) Endpoint – это точки входа для API обращений. Когда у нас будут отрабатывать автоматические тесты, мы постучимся /chek и если получим код возврата 200, значит все хорошо и сервис работает. Следующий у нас файл requirements.txt: requirements.txt uvicorn==0.14.0 pydantic==1.8.2 fastapi==0.66.1 requests==2.26.0 pytest==6.2.5 Тут перечислены зависимости по библиотекам для запуска нашего приложения. Если мы в дальнейшем захотим использовать дополнительный функционал из внешней библиотеке, необходимо добавить имя и версию. Makefile install: pip install --upgrade pip && pip install -r requirements.txt test: python -m pytest -vv test_hello.py Этот файл интереснее. В нем прописаны команды для сборки нашего приложения. Видим, что сначала устанавливаются все библиотеки и вторым шагом запускается файл test_hello.py – это наши тесты. test_hello.py from fastapi.testclient import TestClient from app import app client = TestClient(app) def test_valid_id(): response = client.get("/check") assert response.status_code == 200 Собственно через get стучимся на эндпоинт /check и если код возврата 200, то все ок – наш сервер как минимум отвечает по этому эндпоинту. Это минимальный вариант теста, можно его расширить и настроить под свое приложение. Смотрим в документацию В итоге у нас есть файл с приложением, файл с необходимыми зависимостями, файл с тестами и Makefile для последовательности запуска этого хозяйства. Вопрос на внимательность: чего не хватает? Если вспомнить цели, которых добивается процесс CI (там было что-то про автоматический запуск), то все это делается через описание PipeLine в файле main.yaml. Обратите внимание на расположение этого файла. Корень вашего репозитория, в нем директория .github/workflows/. main.yaml name: Run Test on: push: branches: - main jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python 3.10.1 uses: actions/setup-python@v1 with: python-version: 3.10.1 - name: Install dependencies run: | make install - name: Test run: | make test На языке YAML описывается: если у нас есть новый коммит в ветке main, необходимо выполнить Job c шагами 1-3. Использовать Python 3.10.1 Выполнить команды из блока install файла Makefile. Выполнить команды из блока test файла Makefile. Если на всем этом длинном пути нашего PipeLine не возникнет ошибок, то у нас соберется наше приложение, запустятся тесты. Проследить это можно на вкладке Action вашего репозитория. Если провалиться в детали – то можно посмотреть логи каждого шага Поздравляю, тесты прошли успешно – код успешно запускается без привязки к прод. окружению. И теперь запуск всей цепочки действий (наш PipeLine) будет происходить при каждом коммите в репозиторий. *** Материалы по теме 🏃 Работаем нон-стоп: непрерывная интеграция и непрерывное развертывание кода (CI/CD)
Телеграм @Andrey_Totshin Разберем роль CI в жизненном цикле разработки (что в себя включает эта часть процесса, какие задачи решает) и на практике настроим CI для приложения API сервера. В прошлой статье 🏃 Работаем нон-стоп: непрерывная интеграция и непрерывное развертывание кода (CI/CD) мы обзорно познакомились с процессом CICD. Настало время углубиться и расшириться в детали. Начнем с разбора первой половины процесса – это CI (Continuous Deployment). Разберем цели этого процесса, инструменты и на практическом примере реализуем кейс. Роль CI в жизненном цикле разработки Вспомним как выглядит процесс CI. Цели, которые преследует этот процесс следующие: Фиксация всех изменений версий через систему контроля версий. Компиляция кода и обработка ошибок при компиляции. Автоматическое тестирование выполнения кода. Создание артефактов (оповещения, документация и т. д.). Самое главное – это все выполняется полностью в автоматическом режиме. Человек участвует только в первоначальной настройке процесса, то есть при создании unit-тестов. Такой подход гораздо быстрее ручного тестирования, так как позволяет максимально избежать ошибок по вине человека. В итоге внесенное изменение проходит через тесты и если тесты не прошли, всегда можно вернуться на предыдущий коммит в системе контроля версии. В общем-то и все, предлагаю приступить к реализации этого процесса. Настраиваем CI в GitHub Нам потребуется система СКВ (система контроля версий), а также среда, где будет компилироваться наш код и запускаться тесты. Все эти задачи (и еще много чего интересного) полностью предоставляет сервис GitHub. Кейс: Настроить CI с помощью функционала сервиса GitHub для API сервера написанного на Python. Подробно описывать код самого сервера и как залить его на git не буду. Тема статьи о другом. Имеем в репозитории Git следующую структуру файлов: Цветом выделены файлы, с которыми будем работать. Разберем для чего каждый файл. app.py from fastapi import FastAPI import uvicorn from typing import Optional from pydantic import BaseModel import secrets from fastapi import Depends, FastAPI, HTTPException, status from fastapi.security import HTTPBasic, HTTPBasicCredentials class Item(BaseModel): name: str description: Optional[str] = None price: int tax: Optional[float] = None app = FastAPI() security = HTTPBasic() def get_current_username(credentials: HTTPBasicCredentials = Depends(security)): correct_username = secrets.compare_digest(credentials.username, "test") correct_password = secrets.compare_digest(credentials.password, "test") if not (correct_username and correct_password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect email or password", headers={"WWW-Authenticate": "Basic"}, ) return credentials.username @app.get("/") def read_root(credentials: HTTPBasicCredentials = Depends(get_current_username)): return {"Hi": "from API Server)"} @app.get("/test/{item_id}") async def read_item(item_id: int, credentials: HTTPBasicCredentials = Depends(get_current_username)): print(item_id) return {"item_id": item_id} @app.get("/check") def hello(): return "Hello World" @app.post("/items/") async def create_item(item: Item, credentials: HTTPBasicCredentials = Depends(get_current_username)): return item Это основной файл нашего приложения (API сервера). Сервер имеет три endpoint. Мы будем работать с / и check. Endpoint /check необходим как раз для тестов. Пройдемся по основному функционалу приложения. Метод get_current_username необходим для базовой аутентификации по логину и паролю. Далее у нас идут четыре endpoint: @app.get(“/”) @app.get(“/test”) @app.get(“/items”) @app.get(“/check”) Endpoint – это точки входа для API обращений. Когда у нас будут отрабатывать автоматические тесты, мы постучимся /chek и если получим код возврата 200, значит все хорошо и сервис работает. Следующий у нас файл requirements.txt: requirements.txt uvicorn==0.14.0 pydantic==1.8.2 fastapi==0.66.1 requests==2.26.0 pytest==6.2.5 Тут перечислены зависимости по библиотекам для запуска нашего приложения. Если мы в дальнейшем захотим использовать дополнительный функционал из внешней библиотеке, необходимо добавить имя и версию. Makefile install: pip install --upgrade pip && pip install -r requirements.txt test: python -m pytest -vv test_hello.py Этот файл интереснее. В нем прописаны команды для сборки нашего приложения. Видим, что сначала устанавливаются все библиотеки и вторым шагом запускается файл test_hello.py – это наши тесты. test_hello.py from fastapi.testclient import TestClient from app import app client = TestClient(app) def test_valid_id(): response = client.get("/check") assert response.status_code == 200 Собственно через get стучимся на эндпоинт /check и если код возврата 200, то все ок – наш сервер как минимум отвечает по этому эндпоинту. Это минимальный вариант теста, можно его расширить и настроить под свое приложение. Смотрим в документацию В итоге у нас есть файл с приложением, файл с необходимыми зависимостями, файл с тестами и Makefile для последовательности запуска этого хозяйства. Вопрос на внимательность: чего не хватает? Если вспомнить цели, которых добивается процесс CI (там было что-то про автоматический запуск), то все это делается через описание PipeLine в файле main.yaml. Обратите внимание на расположение этого файла. Корень вашего репозитория, в нем директория .github/workflows/. main.yaml name: Run Test on: push: branches: - main jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python 3.10.1 uses: actions/setup-python@v1 with: python-version: 3.10.1 - name: Install dependencies run: | make install - name: Test run: | make test На языке YAML описывается: если у нас есть новый коммит в ветке main, необходимо выполнить Job c шагами 1-3. Использовать Python 3.10.1 Выполнить команды из блока install файла Makefile. Выполнить команды из блока test файла Makefile. Если на всем этом длинном пути нашего PipeLine не возникнет ошибок, то у нас соберется наше приложение, запустятся тесты. Проследить это можно на вкладке Action вашего репозитория. Если провалиться в детали – то можно посмотреть логи каждого шага Поздравляю, тесты прошли успешно – код успешно запускается без привязки к прод. окружению. И теперь запуск всей цепочки действий (наш PipeLine) будет происходить при каждом коммите в репозиторий. *** Материалы по теме 🏃 Работаем нон-стоп: непрерывная интеграция и непрерывное развертывание кода (CI/CD)
В прошлой статье 🏃 Работаем нон-стоп: непрерывная интеграция и непрерывное развертывание кода (CI/CD) мы обзорно познакомились с процессом CICD. Настало время углубиться и расшириться в детали. Начнем с разбора первой половины процесса – это CI (Continuous Deployment). Разберем цели этого процесса, инструменты и на практическом примере реализуем кейс.
Вспомним как выглядит процесс CI.
Цели, которые преследует этот процесс следующие:
Самое главное – это все выполняется полностью в автоматическом режиме. Человек участвует только в первоначальной настройке процесса, то есть при создании unit-тестов. Такой подход гораздо быстрее ручного тестирования, так как позволяет максимально избежать ошибок по вине человека. В итоге внесенное изменение проходит через тесты и если тесты не прошли, всегда можно вернуться на предыдущий коммит в системе контроля версии. В общем-то и все, предлагаю приступить к реализации этого процесса.
Нам потребуется система СКВ (система контроля версий), а также среда, где будет компилироваться наш код и запускаться тесты. Все эти задачи (и еще много чего интересного) полностью предоставляет сервис GitHub.
Кейс: Настроить CI с помощью функционала сервиса GitHub для API сервера написанного на Python.
Подробно описывать код самого сервера и как залить его на git не буду. Тема статьи о другом.
Имеем в репозитории Git следующую структуру файлов:
Цветом выделены файлы, с которыми будем работать. Разберем для чего каждый файл.
app.py
from fastapi import FastAPI import uvicorn from typing import Optional from pydantic import BaseModel import secrets from fastapi import Depends, FastAPI, HTTPException, status from fastapi.security import HTTPBasic, HTTPBasicCredentials class Item(BaseModel): name: str description: Optional[str] = None price: int tax: Optional[float] = None app = FastAPI() security = HTTPBasic() def get_current_username(credentials: HTTPBasicCredentials = Depends(security)): correct_username = secrets.compare_digest(credentials.username, "test") correct_password = secrets.compare_digest(credentials.password, "test") if not (correct_username and correct_password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect email or password", headers={"WWW-Authenticate": "Basic"}, ) return credentials.username @app.get("/") def read_root(credentials: HTTPBasicCredentials = Depends(get_current_username)): return {"Hi": "from API Server)"} @app.get("/test/{item_id}") async def read_item(item_id: int, credentials: HTTPBasicCredentials = Depends(get_current_username)): print(item_id) return {"item_id": item_id} @app.get("/check") def hello(): return "Hello World" @app.post("/items/") async def create_item(item: Item, credentials: HTTPBasicCredentials = Depends(get_current_username)): return item
Это основной файл нашего приложения (API сервера). Сервер имеет три endpoint. Мы будем работать с / и check. Endpoint /check необходим как раз для тестов.
/
check
/check
Пройдемся по основному функционалу приложения. Метод get_current_username необходим для базовой аутентификации по логину и паролю. Далее у нас идут четыре endpoint:
get_current_username
endpoint
Endpoint – это точки входа для API обращений. Когда у нас будут отрабатывать автоматические тесты, мы постучимся /chek и если получим код возврата 200, значит все хорошо и сервис работает.
/chek
200
Следующий у нас файл requirements.txt:
requirements.txt
uvicorn==0.14.0 pydantic==1.8.2 fastapi==0.66.1 requests==2.26.0 pytest==6.2.5
Тут перечислены зависимости по библиотекам для запуска нашего приложения. Если мы в дальнейшем захотим использовать дополнительный функционал из внешней библиотеке, необходимо добавить имя и версию.
Makefile
install: pip install --upgrade pip && pip install -r requirements.txt test: python -m pytest -vv test_hello.py
Этот файл интереснее. В нем прописаны команды для сборки нашего приложения. Видим, что сначала устанавливаются все библиотеки и вторым шагом запускается файл test_hello.py – это наши тесты.
test_hello.py
from fastapi.testclient import TestClient from app import app client = TestClient(app) def test_valid_id(): response = client.get("/check") assert response.status_code == 200
Собственно через get стучимся на эндпоинт /check и если код возврата 200, то все ок – наш сервер как минимум отвечает по этому эндпоинту. Это минимальный вариант теста, можно его расширить и настроить под свое приложение. Смотрим в документацию
В итоге у нас есть файл с приложением, файл с необходимыми зависимостями, файл с тестами и Makefile для последовательности запуска этого хозяйства. Вопрос на внимательность: чего не хватает?
Если вспомнить цели, которых добивается процесс CI (там было что-то про автоматический запуск), то все это делается через описание PipeLine в файле main.yaml.
PipeLine
main.yaml
Обратите внимание на расположение этого файла. Корень вашего репозитория, в нем директория .github/workflows/.
.github/workflows/
name: Run Test on: push: branches: - main jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python 3.10.1 uses: actions/setup-python@v1 with: python-version: 3.10.1 - name: Install dependencies run: | make install - name: Test run: | make test
На языке YAML описывается: если у нас есть новый коммит в ветке main, необходимо выполнить Job c шагами 1-3.
main
Job
install
test
Если на всем этом длинном пути нашего PipeLine не возникнет ошибок, то у нас соберется наше приложение, запустятся тесты. Проследить это можно на вкладке Action вашего репозитория.
Если провалиться в детали – то можно посмотреть логи каждого шага
Поздравляю, тесты прошли успешно – код успешно запускается без привязки к прод. окружению. И теперь запуск всей цепочки действий (наш PipeLine) будет происходить при каждом коммите в репозиторий.
***
ΠΠ°Ρ Π°Π΄ΡΠ΅Ρ email Π½Π΅ Π±ΡΠ΄Π΅Ρ ΠΎΠΏΡΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½. ΠΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΡΠ΅ ΠΏΠΎΠ»Ρ ΠΏΠΎΠΌΠ΅ΡΠ΅Π½Ρ *
Π‘ΠΎΡ ΡΠ°Π½ΠΈΡΡ ΠΌΠΎΡ ΠΈΠΌΡ, email ΠΈ Π°Π΄ΡΠ΅Ρ ΡΠ°ΠΉΡΠ° Π² ΡΡΠΎΠΌ Π±ΡΠ°ΡΠ·Π΅ΡΠ΅ Π΄Π»Ρ ΠΏΠΎΡΠ»Π΅Π΄ΡΡΡΠΈΡ ΠΌΠΎΠΈΡ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠ΅Π².
Δ
ΠΡΠΎΡ ΡΠ°ΠΉΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ Akismet Π΄Π»Ρ Π±ΠΎΡΡΠ±Ρ ΡΠΎ ΡΠΏΠ°ΠΌΠΎΠΌ. Π£Π·Π½Π°ΠΉΡΠ΅, ΠΊΠ°ΠΊ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡΡΡ Π²Π°ΡΠΈ Π΄Π°Π½Π½ΡΠ΅ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠ΅Π².