Giới thiệu

Mục tiêu: Đồng hành cùng người đọc trong việc thiết lập một pipeline CI/CD tối giản nhưng vẫn hoạt động được cho một ứng dụng Python, với việc triển khai tự động lên PyPI.

Tự động hóa các quy trình phát triển đã trở nên thiết yếu trong các dự án hiện đại. Một pipeline CI/CD được thiết kế tốt không chỉ giúp phát hiện sớm các hồi quy trong chu kỳ phát triển mà còn tự động hóa hoàn toàn quy trình triển khai.

Trong bài viết này, chúng ta sẽ khám phá cách thiết lập một pipeline hoàn chỉnh với GitHub Actions cho một ứng dụng Python, từ tích hợp liên tục (CI) đến triển khai liên tục (CD) trên PyPI.

Kiến trúc Pipeline

Quy trình của chúng tôi gồm hai quy trình làm việc riêng biệt:

  1. Quy trình CI: Chạy trên mỗi push và pull request

  2. Quy trình CD: Chỉ được kích hoạt khi có bản phát hành GitHub

@startuml
!theme plain

package "GitHub Kho lưu trữ" {
  [Source Code] as source
  [GitHub Actions] as actions
}

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

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

source --> actions : Đẩy/PR
actions --> checkout_ci
checkout_ci --> python_ci
python_ci --> deps_ci
deps_ci --> lint
lint --> tests

source --> actions : Phát hành
actions --> checkout_cd : Khi phát hành
checkout_cd --> python_cd
python_cd --> build
build --> pypi

@enduml

Cấu hình Pipeline Tích hợp Liên tục (CI)

Cấu trúc Workflow CI

Quy trình CI được thiết kế để xác thực mọi đóng góp mã nguồn. Dưới đây là cấu hình đầy đủ của nó:

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

Phân tích các bước CI

1. Trình kích hoạt (on)

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

Pipeline được kích hoạt trên: - Mỗi lần push lên nhánh`main` - Mỗi pull request đến`main`

Cách tiếp cận này đảm bảo rằng mã chính vẫn ổn định và mọi đóng góp đều được xác thực trước khi tích hợp.

==== 2. Môi trường thực thi

```yaml
----
runs-on: ubuntu-latest
----

Ubuntu Latest mang lại sự cân bằng tốt giữa hiệu năng, chi phí và khả năng tương thích cho hầu hết các dự án Python.

==== 3. Checkout Mã

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

Hành động`checkout@v4`Lấy mã nguồn của kho lưu trữ. Phiên bản v4 mang lại những cải tiến về hiệu năng và bảo mật.

==== 4. Cấu hình Python

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

Việc sử dụng`'3.x'`cho phép tự động sử dụng phiên bản ổn định mới nhất của Python 3, đơn giản hóa việc bảo trì.

==== 5. Cài đặt các phụ thuộc

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

Bước này: - Cập nhật pip lên phiên bản mới nhất Cài đặt các phần phụ thuộc của dự án - Thêm các công cụ phát triển (linting và kiểm thử)

==== 6. Linting với Ruff

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

**Ruff**là một linter Python siêu nhanh được viết bằng Rust. Nó kết hợp các tính năng của nhiều công cụ (Flake8, Black, isort) thành một công cụ duy nhất có hiệu năng cao.

==== 7. Thực hiện kiểm thử

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

Pytest chạy toàn bộ bộ kiểm thử, đảm bảo rằng các thay đổi không gây ra hồi quy.

== Cấu hình Pipeline Triển khai (CD)

=== Cấu trúc của Quy trình CD

Quy trình CD chỉ được kích hoạt khi có các bản phát hành GitHub và tự động hóa việc xuất bản lên 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/*
----

=== Phân tích các bước CD

==== 1. Kích hoạt Release

```yaml
----
on:
  release:
    types:
      - published
----

Pipeline CD chỉ được kích hoạt khi xuất bản một bản release GitHub. Cách tiếp cận này đảm bảo kiểm soát chính xác các đợt triển khai.

==== 2. Cài đặt Công cụ Build

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

- **setuptools**: Công cụ đóng gói Python - **bánh xe**: Định dạng phân phối Python hiện đại - **twine**: Công cụ an toàn để tải lên PyPI

==== 3. Cấu hình Xác thực

```yaml
----
env:
  TWINE_USERNAME: __token__
  TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
----

Xác thực sử dụng token API PyPI được lưu trữ dưới dạng secret GitHub, an toàn hơn so với thông tin đăng nhập thông thường.

==== 4. Build và Xuất bản

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

- `sdist`: Tạo một bản phân phối nguồn - `bdist_wheel`: Tạo một wheel (bản phân phối nhị phân) - `twine upload`: Xuất bản các bản phân phối lên PyPI

== Cấu hình Gói Python

=== Cấu trúc của setup.py

Để pipeline hoạt động, dự án của bạn phải bao gồm một tệp`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",
        ],
    },
)
----

=== Các điểm chính của setup.py

1. **Siêu dữ liệu**: Tên, phiên bản, tác giả, mô tả
2. **Phụ thuộc**: Danh sách các gói cần thiết
3. **Điểm truy cập**: Các lệnh CLI được hiển thị
4. **Bộ phân loại**: Siêu dữ liệu cho PyPI

== Bảo mật với GitHub Secrets

=== Cấu hình Token PyPI

1. **Tạo một token API trên PyPI** :

- Đăng nhập vào PyPI - Đi đến Cài đặt tài khoản > API token - Tạo một token mới với các quyền phù hợp

1. **Thêm bí mật vào GitHub** :

- Cài đặt kho lưu trữ > Bí mật và biến > Actions - Tạo một bí mật mới có tên`PYPI_API_TOKEN` - Dán token PyPI của bạn

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

actor Developer as dev
participant "GitHub Kho lưu trữ" as repo
participant "GitHub Hành động" as actions
participant PyPI

dev -> repo : Cấu hình bí mật PYPI_API_TOKEN
repo -> actions : Kích hoạt pipeline CD khi phát hành
actions -> actions : Truy cập bí mật một cách an toàn
actions -> PyPI : Xác thực bằng token
PyPI -> PyPI : Xác thực và xuất bản gói

note over actions, PyPI
Token không bao giờ bị lộ trong nhật ký
Có thể luân chuyển tự động
end note

@enduml
----

== Quy trình triển khai hoàn chỉnh

=== Trình tự Triển khai

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

actor Developer as dev
participant "Git cục bộ" as git
participant "GitHub" as github
participant "GitHub Hành động" as actions
participant PyPI
participant "Người dùng cuối" as users

dev -> git : git tag v1.0.0
dev -> git : git push origin v1.0.0
git -> github : Đẩy thẻ
dev -> github : Tạo bản phát hành từ thẻ
github -> actions : Kích hoạt pipeline CD
actions -> actions : Mã thanh toán
actions -> actions : Thiết lập môi trường Python
actions -> actions : Cài đặt công cụ build
actions -> actions : Xây dựng các bản phân phối (sdist + wheel)
actions -> PyPI : Tải lên PyPI bằng token
PyPI -> PyPI : Xác thực và xuất bản
users -> PyPI : pip install playlist-downloader

note over dev, github
Việc tạo bản phát hành có thể được tự động hóa hoặc thực hiện thủ công thông qua giao diện người dùng GitHub
end note

@enduml
----

=== Các trạng thái của Pipeline

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

[*] --> Idle

Idle --> CI_Running : Push/PR đã được tạo
CI_Running --> CI_Success : Tất cả các kiểm tra đều đạt
CI_Running --> CI_Failed : Linting/Tests thất bại
CI_Success --> Idle : Đã hợp nhất xong
CI_Failed --> Idle : Sửa và thử lại

Idle --> CD_Running : Bản phát hành đã được công bố
CD_Running --> CD_Success : Gói đã được xuất bản
CD_Running --> CD_Failed : Lỗi Build/Upload
CD_Success --> Idle : Gói có sẵn trên PyPI
CD_Failed --> Idle : Sửa và thử lại bản phát hành

note on link #red : Gộp các khối
note on link #green : Cho phép triển khai

@enduml
----

== Thực tiễn tốt nhất và Tối ưu hóa

=== 1. Quản lý phiên bản

Sử dụng các thẻ Git ngữ nghĩa:

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

=== 2. Kiểm thử Ma trận

Để kiểm tra trên nhiều phiên bản Python:

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

=== 3. Bộ nhớ đệm các Phụ thuộc

Tăng tốc các bản build bằng bộ nhớ đệm:

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

=== 4. Môi trường triển khai

Hãy sử dụng môi trường GitHub để triển khai có kiểm soát:

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

== Ca sử dụng và Kiến trúc

=== Biểu đồ ca sử dụng

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

left to right direction

actor "Nhà phát triển" as dev
actor "GitHub Hành động" as ga
actor "Người dùng cuối" as user

package "Hệ thống CI/CD" {
  usecase "Chạy Linting" as lint
  usecase "Thực thi kiểm thử" as test
  usecase "Gói bản dựng" as build
  usecase "Xuất bản tới PyPI" as publish
  usecase "Tạo bản phát hành" as release
}

dev --> lint : Đẩy mã
dev --> test : Đẩy mã
dev --> release : Tạo bản phát hành
ga --> build : Khi kích hoạt phát hành
ga --> publish : Sau khi build thành công
user --> publish : Gói tải xuống

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

@enduml
----

=== Kiến trúc Triển khai

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

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

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

node "CI/CD Pipeline" {
  [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
Xác thực bảo mật
dựa trên token
end note

[Secrets Store] .. N1

@enduml
----

== Giám sát và Gỡ lỗi

=== Nhật ký và Giám sát

GitHub Actions cung cấp nhật ký chi tiết cho từng bước. Để gỡ lỗi:

1. **Hãy kiểm tra nhật ký**của mỗi bước
2. **Bật debug**với`ACTIONS_STEP_DEBUG`
3. **Hãy sử dụng các artifacts**để sao lưu các tệp build

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

=== Thông báo

Thêm thông báo Slack hoặc email:

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

== Kết luận

Việc thiết lập một pipeline CI/CD vững chắc với GitHub Actions thay đổi hoàn toàn trải nghiệm phát triển. Bằng cách tự động hóa linting, kiểm thử và triển khai, bạn:

- **Giảm thiểu lỗi**trong sản xuất - **Tăng tốc các chu kỳ**phát triển - **Nâng cao niềm tin**trong các bản phát hành của bạn - **Hãy tạo điều kiện thuận lợi cho việc hợp tác**theo nhóm

Pipeline này có thể được điều chỉnh cho các loại dự án Python khác nhau bằng cách điều chỉnh các công cụ linting, các framework kiểm thử, hoặc các đích triển khai.

Khoản đầu tư ban đầu vào việc cấu hình các quy trình làm việc này sẽ nhanh chóng được hoàn vốn nhờ tiết kiệm thời gian và giảm thiểu lỗi thủ công trong quá trình triển khai.

== Tài nguyên bổ sung

- https://docs.github.com/en/actions[Tài liệu GitHub Actions] - https://packaging.python.org/[Hướng dẫn đóng gói Python] - https://docs.pytest.org/[Tài liệu Pytest] - https://docs.astral.sh/ruff/[Tài liệu Ruff] - https://twine.readthedocs.io/[Tài liệu Twine]

✅ Đã đạt được pipeline hoạt động! Giờ đây bạn đã có một pipeline CI/CD đơn giản cho phép bạn tự động hóa các bài kiểm tra và xuất bản gói Python của mình lên PyPI trực tiếp từ GitHub Actions.

Tuy nhiên, pipeline này vẫn được chủ ý giữ ở mức tối giản. Nó vẫn chưa bao quát một số khía cạnh thiết yếu trong bối cảnh chuyên nghiệp:

Kiểm thử đa phiên bản Python,

Phân tích bảo mật tự động,

Triển khai dần dần qua Test PyPI,

Giám sát và số liệu đo lường của pipeline,

Tự động hóa việc quản lý phiên bản và tích hợp các thực hành tốt hiện đại (pyproject.toml).

Trong phần tiếp theo, chúng ta sẽ nâng lên một tầm cao mới. Bạn sẽ học cách biến pipeline cơ bản này thành một chuỗi triển khai công nghiệp thực thụ, vững chắc và an toàn, sẵn sàng cho các dự án Python trong môi trường sản xuất.

Bài viết liên quan