В предишната ни статия разгледахме контейнеризацията с Docker и как тя може да опрости и подобри процеса на разработка и разгръщане на Python приложения. Днес ще се фокусираме върху друг основен инструмент в арсенала на всеки разработчик – системата за контрол на версиите Git и платформата GitHub. Ще обсъдим най-добрите практики за управление на код, сътрудничество в екип и непрекъсната интеграция (CI) специално за Python проекти. Нека да започваме!

Защо са важни контролът на версиите и колаборацията?

В съвременната софтуерна разработка рядко работим сами. Дори и при лични проекти, често се налага да сътрудничим с други разработчици, дизайнери, тестери и т.н. А в корпоративна среда, ефективната координация между членовете на екипа е от ключово значение за успеха на проекта.

Системите за контрол на версиите (Version Control Systems, VCS) като Git ни позволяват да управляваме промените в кода си във времето, да експериментираме с нови функционалности в отделни клонове (branches) и лесно да сливаме (merge) работата си с тази на колегите. Платформи като GitHub допълнително улесняват сътрудничеството чрез функции като pull requests, issue tracking и code reviews.

Освен това, използването на VCS е важно за:

  • Проследимост: Винаги можем да видим кой, кога и защо е направил дадена промяна в кода.
  • Възстановяемост: Ако нещо се обърка, лесно можем да се върнем към предишна работеща версия.
  • Експериментиране: Можем безопасно да пробваме нови идеи в отделни бранчове, без да засягаме основния код.

Сега, нека разгледаме някои най-добри практики за работа с Git и GitHub конкретно в Python проекти.

Структуриране на Git хранилище

Първата стъпка при стартирането на нов Python проект е да създадем Git хранилище (repository) и да настроим правилно структурата му. Ето една типична структура на Python хранилище:


my-project/
├── .gitignore
├── README.md
├── requirements.txt
├── setup.py
├── src/
│   ├── __init__.py
│   ├── module1.py
│   └── module2.py
└── tests/
    ├── __init__.py
    ├── test_module1.py
    └── test_module2.py

Някои важни елементи тук са:

  • .gitignore: Файл, който казва на Git кои файлове да игнорира (напр. *.pyc, venv/, и др.).
  • README.md: Markdown файл с описание на проекта, инструкции за инсталация, примери за употреба и т.н.
  • requirements.txt: Списък на външните зависимости на проекта и техните версии.
  • setup.py: Файл за инсталиране и дистрибутиране на пакета.
  • src/: Директория с основния код на проекта.
  • tests/: Директория с тестовете за проекта.

Разбира се, точната структура ще зависи от спецификата и мащаба на проекта, но тези елементи са добро начало.

Разклоняване и сливане (Branching and Merging)

Една от най-мощните функции на Git е възможността да създаваме разклонения (branches) на кода си. Разклоненията ни позволяват да работим по нови функционалности или бъгфиксове изолирано, без да засягаме основния (main/master) бранч.

Ето един пример за добър работен процес с разклонения:

Създайте нов бранч за конкретна задача (feature/bugfix):


git checkout -b feature/add-user-authentication

Извършете желаните промени, правейки множество малки и фокусирани commit-и:


git add .
git commit -m "Add user model and registration form"
git add .
git commit -m "Implement login and logout views"

Редовно актуализирайте бранча си с промените от main/master:


git pull origin main

Когато сте готови, изпратете бранча си в GitHub и отворете Pull Request:


git push -u origin feature/add-user-authentication

След одобрение и сливане на Pull Request-a, изтрийте feature бранча локално и в GitHub:


git branch -d feature/add-user-authentication
git push origin --delete feature/add-user-authentication

Този процес гарантира, че main/master бранчът винаги съдържа стабилен код, докато активната разработка се извършва в отделни бранчове.

Semver и означаване на версии

Друга добра практика е използването на семантично версиониране (Semantic Versioning, Semver) за означаване на версиите на вашия Python пакет. Semver определя прост набор от правила за присвояване на версионни номера във формат MAJOR.MINOR.PATCH:

  • MAJOR се увеличава при несъвместими промени в API-то.
  • MINOR се увеличава при добавяне на нови функционалности по обратно съвместим начин.
  • PATCH се увеличава при бъгфиксове, които не променят API-то.

Можете да използвате Git тагове, за да означите конкретни точки в историята като издания (releases):


git tag -a v1.2.0 -m "Release version 1.2.0"
git push origin v1.2.0

В Python е прието също да се включва версионният номер в setup.py файла и да се импортира от самия пакет, например в src/__init__.py:


# src/__init__.py
__version__ = '1.2.0'

По този начин потребителите на пакета ви могат лесно да проверят коя версия използват.

Автоматизиране на задачи с GitHub Actions

Непрекъснатата интеграция и доставка (CI/CD) е основна DevOps практика, която помага за поддържане на високо качество на кода и ускорява процеса на разработка и разгръщане. С GitHub Actions можем лесно да автоматизираме задачи като изпълнение на тестове, линтери и автоматично публикуване на пакети при определени събития (например push към main или нов release).

Ето пример за прост GitHub Actions работен процес за Python проект:


# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:

  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.x'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Run linter
      run: |
        pip install flake8
        flake8 src
    - name: Run tests
      run: |
        pip install pytest
        pytest tests

Този конфиг дефинира работен процес на име „CI“, който се изпълнява при push или pull request към main бранча. Той се състои от една задача (job) build, която върши следното:

  1. Проверява кода от хранилището.
  2. Инсталира Python 3.x.
  3. Инсталира зависимостите на проекта от requirements.txt.
  4. Инсталира и изпълнява flake8 линтер върху src/ директорията.
  5. Инсталира и изпълнява pytest тестовете от tests/ директорията.

Ако някоя от стъпките fail-не (върне нестандартен exit код), целият работен процес се счита за провален. Това е страхотен начин за улавяне на проблеми с форматирането или счупени тестове преди да се слеят в основния бранч.

Можете да разширите този работен процес с допълнителни стъпки като автоматично публикуване в PyPI при издаване на нов release или deploy към staging/production среда.

Заключение

В тази статия разгледахме някои от най-добрите практики за управление на Python код с Git и GitHub, от структурирането на хранилището до стратегии за разклоняване и сливане, семантично версиониране и автоматизация с GitHub Actions.

Важно е да отбележим, че не съществува универсален „правилен“ начин за използване на Git – в крайна сметка най-добрият работен процес е този, който работи добре за вас и вашия екип. Въпреки това, разбирането и прилагането на тези концепции може значително да подобри качеството, надеждността и скоростта на разработката на вашия Python проект.

Помнете, че Git има много мощни функции и може да отнеме време да ги овладеете напълно. Но инвестицията си струва – опитът с Git и GitHub е безценен актив за всеки съвременен софтуерен разработчик.

Надяваме се, че тази статия ви е дала някои полезни идеи за подобряване на вашия Git работен процес. В следващите части ще разгледаме по-задълбочено теми като стратегии за разрешаване на конфликти, интерактивно пребазиране (interactive rebasing), и използване на Git hooks за автоматизиране на задачи.

Categorized in:

DevOps,

Last Update: май 5, 2024