Uvod

Циљ : Pomoći čitaču u postavljanju minimalističkog, ali funkcionalnog CI/CD pipelinea za Python aplikaciju, sa automatskim deploy-om na PyPI.

Automatizacija procesa razvoja postala je neophodna u modernim projektima. Dobro dizajniran CI/CD pipeline ne samo što otkriva regresije ranije u ciklusu razvoja, već i potpuno automatizuje proces raspoređivanja.

У овом чланку ћемо испитати како подешати потпуни са GitHub Actions за Python апликацију, од континуиране интеграције (CI) до континуиране доставе (CD) на PyPI.

Архитектура пиплайна

Naš pipeline se sastoji od dvaju različitih workflow:

  1. CI PipelineIzvršen na svakom push-u i pull request-u

  2. CD Pipeline: Pokreće se samo tokom GitHub release-ova

@startuml
!theme plain

package "Repozitorijum GitHub" {
  [Source Code] as source
  [GitHub Actions] as actions
}

package "CI cevovod" {
  [Checkout] as checkout_ci
  [Setup Python] as python_ci
  [Install Dependencies] as deps_ci
  [Linting (Ruff)] as lint
  [Unit Tests] as tests
}

package "CD Pipeline" {
  [Checkout] as checkout_cd
  [Setup Python] as python_cd
  [Build Package] as build
  [Publish to PyPI] as pypi
}

source --> actions : Push/PR
actions --> checkout_ci
checkout_ci --> python_ci
python_ci --> deps_ci
deps_ci --> lint
lint --> tests

source --> actions : Release
actions --> checkout_cd : On Release
checkout_cd --> python_cd
python_cd --> build
build --> pypi

@enduml

Konfiguracija CI pipeline-a

Struktura CI radnog toka

CI radni tok je dizajniran da validira svaki doprinos kodu. Evo njene potpune konfiguracije:

name: CI/CD Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.x'

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install ruff pytest pytest-mock

    - name: Run Linting (Ruff)
      run: ruff check .

    - name: Run Tests (Pytest)
      run: pytest

Analiza koraka CI

Pokretači (on)

----
----
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
----

Pajplajn se pokreće na: - Svaki push na granu`main` - svaki pull request ka`main`

Ovaj pristup garantuje da glavni kod ostane stabilan i da svaki doprinos bude proveren pre integracije.

==== 2. Izvršno okruženje

----

runs-on: ubuntu-latest

Ubuntu Latest nudi dobar kompromis između performansi, cene i kompatibilnosti za većinu Python projekata.

==== 3. Check‑out koda

```yaml
----
----
- name: Checkout code
  uses: actions/checkout@v4
----

Akcija`checkout@v4`Преузима изворни код из репозиторија. Верзија v4 донесе надоградње у области перформанси и безбедности.

==== 4. Конфигурација Python

----

- name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x'

Коришћење`'3.x'`omogućuje automatsko korišćenje najnovije stabilne verzije Python 3, pojednostavljajući održavanje.

==== 5. Инсталација зависности

```yaml
----
----
- name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt
    pip install ruff pytest pytest-mock
----

Овај чекор : - Ažuriraj pip na najnoviju verziju - Instaliraj zavisnosti projekta - Dodaj alate za razvoj (linting i testovi)

==== 6. Lintiranje sa Ruff

----

- name: Run Linting (Ruff) run: ruff check .

**Ruff**Ултра-брз Python линтер написан на Rust. Он комбинује функционалности неколико алата (Flake8, Black, isort) у један ефикасан алат.

==== 7. Izvršavanje testova

```yaml
----
----
- name: Run Tests (Pytest)
  run: pytest
----

Pytest izvršava celi skup testova, osiguravajući da promene ne uvedu regresije.

== Конфигурација ЦИД пайплайна

=== Struktura CD workflow-a

CD workflow se pokreće samo tokom GitHub release-ova i automatizuje objavu na PyPI:

[source,yaml]
----
name: Publish to PyPI

on:
  release:
    types:
      - published

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.x'

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install setuptools wheel twine

    - name: Build and publish
      env:
        TWINE_USERNAME: __token__
        TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
      run: |
        python setup.py sdist bdist_wheel
        twine upload dist/*
----

=== Analiza koraka CD

==== Pokretač Release

----

on: release: types: - published

CD pipeline se pokreće samo prilikom objavljivanja GitHub release-a. Ovaj pristup osigurava preciznu kontrolu nad deploy-ovanjem.

==== 2. Instalacija alata za gradnju

```yaml
----
----
pip install setuptools wheel twine
----

- **setuptools**: Alati za pakiranje Python - **wheel**: Moderni format distribucije Pythona - **konopac**: Осигурен алат за аплоадинг ка PyPI

==== 3. Конфигурација аутентификације

----

env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}

Аутентификација користи PyPI API токен складиштењен као GitHub тајна, који је безбеднији од традиционалних учредња.

==== 4. Izgradnja i objava

```yaml
----
----
run: |
  python setup.py sdist bdist_wheel
  twine upload dist/*
----

- `sdist`: Kreiraj izvornu distribuciju - `bdist_wheel`Kreiraj wheel (binarna distribucija) - `twine upload`Objavljuje distribucije na PyPI

== Конфигурација пајтон пакета

=== Struktura fajla setup.py

Da bi pipeline funkcionisao, vaš projekat mora da uključuje fajl.`setup.py`:

[source,python]
----
from setuptools import setup, find_packages

with open("README.adoc", "r", encoding="utf-8") as fh:
    long_description = fh.read()

setup(
    name="playlist-downloader",
    version="1.0.0",
    author="Votre Nom",
    author_email="[email protected]",
    description="CLI tool for managing YouTube playlists",
    long_description=long_description,
    long_description_content_type="text/plain",
    url="https://github.com/cheroliv/playlist-downloader",
    packages=find_packages(),
    classifiers=[
        "Development Status :: 4 - Beta",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: 3.10",
        "Programming Language :: Python :: 3.11",
    ],
    python_requires=">=3.8",
    install_requires=[
        "typer>=0.9.0",
        "yt-dlp>=2023.1.6",
        "google-api-python-client>=2.70.0",
        "google-auth-oauthlib>=0.7.1",
        "pymonad>=2.4.0",
        "pyyaml>=6.0",
    ],
    entry_points={
        "console_scripts": [
            "playlist-downloader=cli:app",
        ],
    },
)
----

=== Kljucne tačke setup.py

1. **Metapodaci**: Ime, verzija, autor, opis
2. **зависност**: Списак потребних пакета
3. **Улазне тачке**: Izložene CLI komande
4. **Klasifikatori**Metapodaci za PyPI

== Заштита преко GitHub secretова

=== Конфигурација Token PyPI

1. **Kreirati API token na PyPI**:

- Prijavite se na PyPI - Idite na Account Settings > API tokens - Креирајте нови токен са одговарајућим овозбедицама

1. **Додај секрет у GitHub**:

- Podešavanja repozitorija > Tajne i varijable > Akcije - Kreirajte novu tajnu nazvanu`PYPI_API_TOKEN` - Унесите ваш PyPI токен

[plantuml, secrets-flow, svg]
----
@startuml
!theme plain

actor Developer as dev
participant "GitHub репозиторијум" as repo
participant "GitHub Actions" as actions
participant PyPI

dev -> repo : Configure PYPI_API_TOKEN secret
repo -> actions : Trigger CD pipeline on release
actions -> actions : Access secret securely
actions -> PyPI : Authenticate with token
PyPI -> PyPI : Validate and publish package

note over actions, PyPI
  Token never exposed in logs
  Automatic rotation possible
end note

@enduml
----

== Radni tok potpunog raspoređivanja

=== sekvenca implementacije

[plantuml, deployment-sequence, svg]
----
@startuml
!theme plain

actor Developer as dev
participant "Lokalni Git" as git
participant "GitHub" as github
participant "GitHub akcije" as actions
participant PyPI
participant "Krajnji korisnici" as users

dev -> git : git tag v1.0.0
dev -> git : git push origin v1.0.0
git -> github : Push tag
dev -> github : Create release from tag
github -> actions : Trigger CD pipeline
actions -> actions : Checkout code
actions -> actions : Setup Python environment
actions -> actions : Install build tools
actions -> actions : Build distributions (sdist + wheel)
actions -> PyPI : Upload to PyPI with token
PyPI -> PyPI : Validate and publish
users -> PyPI : pip install playlist-downloader

note over dev, github
  Release creation can be automated
  or done manually through GitHub UI
end note

@enduml
----

=== Stanja pipeline-a

[plantuml, pipeline-states, svg]
----
@startuml
!theme plain

[*] --> Idle

Idle --> CI_Running : Push/PR created
CI_Running --> CI_Success : All checks pass
CI_Running --> CI_Failed : Linting/Tests fail
CI_Success --> Idle : Merge completed
CI_Failed --> Idle : Fix and retry

Idle --> CD_Running : Release published
CD_Running --> CD_Success : Package published
CD_Running --> CD_Failed : Build/Upload error
CD_Success --> Idle : Package available on PyPI
CD_Failed --> Idle : Fix and retry release

note on link #red : Blocks merge
note on link #green : Allows deployment

@enduml
----

== Dobre prakse i optimizacije

=== Upravljanje verzijama

Koristite semantičke Git tagove:

----

git tag -a v1.2.3 -m "Release version 1.2.3" git push origin v1.2.3

=== 2. Testovi matrice

Za testiranje na verzijama Pythona:

```yaml
----
----
strategy:
  matrix:
    python-version: [3.8, 3.9, "3.10", "3.11"]
----

=== 3. Keš zavisnosti

Ubrzajte gradnje sa kešom:

----

- name: Cache pip dependencies uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}

=== 4. Okruženja za deploy

Koristite GitHub okruženja za kontrolisana objavljivanja:

```yaml
----
----
jobs:
  deploy:
    environment: production
    runs-on: ubuntu-latest
----

== Случај употребе и архитектура

=== Дијаграм случајева коришћења

[plantuml, use-cases, svg]
----
@startuml
!theme plain

left to right direction

actor "razvijalac" as dev
actor "GitHub Actions" as ga
actor "Krajnji korisnik" as user

package "CI/CD Sistem" {
  usecase "Pokreni lintanje" as lint
  usecase "Izvrši testove" as test
  usecase "Izgradite paket" as build
  usecase "Objavi na PyPI" as publish
  usecase "Kreiraj izdanje" as release
}

dev --> lint : Pushes code
dev --> test : Pushes code
dev --> release : Creates release
ga --> build : On release trigger
ga --> publish : After successful build
user --> publish : Downloads package

lint .> test : triggers
test .> build : on success
build .> publish : on success

@enduml
----

=== Архитектура деплојмента

[plantuml, deployment-architecture, svg]
----
@startuml
!theme plain

cloud "GitHub" {
  [Source Repository]
  [GitHub Actions]
  [Secrets Store]
}

cloud "PyPI" {
  [Package Registry]
  [Distribution Files]
}

node "CI/CD cijevovod" {
  [Linting]
  [Testing]
  [Building]
  [Publishing]
}

[Source Repository] --> [GitHub Actions] : Triggers
[GitHub Actions] --> [Linting]
[Linting] --> [Testing]
[Testing] --> [Building]
[Building] --> [Publishing]
[Publishing] --> [Package Registry] : Uploads
[Secrets Store] --> [Publishing] : Provides token

note as N1
  Secure token-based
  authentication
end note

[Secrets Store] .. N1

@enduml
----

== Надзор и Debugging

=== logovi i monitoring

GitHub Actions nudi detaljne zapise za svaki korak. Za debugiranje:

1. **Ispitajte logove**svake korak
2. **Uključite debug**sa`ACTIONS_STEP_DEBUG`
3. **Koristi artefakte**Да сачувате fajlove za build

----

- name: Upload build artifacts uses: actions/upload-artifact@v3 if: failure() with: name: build-logs path: build/

=== Obaveštenja

Dodajte Slack ili e‑mail obaveštenja:

```yaml
----
----
- name: Notify on failure
  if: failure()
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    webhook_url: ${{ secrets.SLACK_WEBHOOK }}
----

== Zaključak

Postavljanje jakog CI/CD pipelinea sa GitHub Actions radikalno menja iskustvo razvoja. Automatizovanjem lintovanja, testova i deploy-a, vi :

- **Смањите грешке**у производњу - **Ubržajte ciklusi**od razvoja - **Poboljšajte poverenje**u vašim releasima - **Olakšajte saradnju**у тим

Ovaj pipeline se može prilagoditi različitim vrstama Python projekata prilagođavanjem alata za lintiranje, okvira za testiranje ili destinacija za implementaciju.

Početni ulaganje u podešavanje ovih radnih tokova se brzo vraća zbog uštede vremena i smanjivanja ručnih grešaka tokom implementacija.

== Dodatni resursi

- https://docs.github.com/en/actions[Документација GitHub Actions] - https://packaging.python.org/[Python vodič za pakovanje] - https://docs.pytest.org/[Dokumentacija Pytest] - https://docs.astral.sh/ruff/[Документација Ruff] - https://twine.readthedocs.io/[Документация Twine]

✅ Funkcionalni pipeline postignut ! Sada imate jednostavni CI/CD pipeline koji vam omogućava da automatizujete svoje testove i da objavite svoj Python paket na PyPI izravno iz GitHub Actions.

Ovakav pipeline ostaje svestrano minimalistički. Još ne pokriva neke neophodne aspekte u profesionalnom kontekstu :

Тестови више верзија Питона,

Automatska analiza bezbednosti,

Postupno raspoređivanje putem Test PyPI,

Набљудање и метрике конвејера,

Automatizacija verzioniranja i uvođenje modernih dobrih praksi (pyproject.toml).

U sledećem delu, prelazimo na viši nivo. Naučit ćete da preobrazite ovaj osnovni pipeline u stvarni industrijski lanac implementacije, čvrst i siguran, spreman za Python projekte u proizvodnji.
----

Повезани чланци