الجزء 1: إعداد مسار CI/CD بسيط لـ Python و PyPI
Publié le 17 July 2025
Sommaire
مقدمة
الهدف : مرافقة القارئ في إعداد Pipeline CI/CD بسيط لكنه وظيفي لتطبيق Python، مع نشر تلقائي على PyPI.
أصبحت أتمتة عمليات التطوير لا غنى عنها في المشاريع الحديثة. يتيح خط أنابيب CI/CD مصمم جيدًا ليس فقط اكتشاف التراجعات مبكرًا في دورة التطوير، بل أيضًا أتمتة عملية النشر بالكامل.
في هذا المقال، سنستكشف كيفية إعداد pipeline كامل باستخدام GitHub Actions لتطبيق Python، من التكامل المستمر (CI) إلى النشر المستمر (CD) على PyPI.
بنية خط الأنابيب
يتكون خط أنابيبنا من سير عملين مختلفين :
-
سلسلة التكامل المستمر: يتم تنفيذه على كل push و pull request
-
خط أنابيب التسليم المستمر: يتم تشغيله فقط عند إصدارات GitHub
إعداد خط أنابيب التكامل المستمر (CI)
هيكل سير عمل التكامل المستمر
سير عمل CI مصمم للتحقق من كل مساهمة في الكود. إليك تكوينه الكامل:
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
تحليل مراحل CI
1. المشغلات (on)
----
----
on:
push:
branches:
- main
pull_request:
branches:
- main
----
يعمل الـ pipeline عند : - كل دفع على الفرع`main` - كل طلب سحب نحو`main`
هذا النهج يضمن أن يظل الكود الرئيسي مستقرًا وأن يتم التحقق من أي مساهمة قبل الدمج.
==== 2. بيئة التنفيذ
----
runs-on: ubuntu-latest
يوفر أحدث نسخة من Ubuntu توازنًا جيدًا بين الأداء والتكلفة والتوافق لمعظم مشاريع بايثون.
==== 3. استخراج الكود
```yaml
----
----
- name: Checkout code
uses: actions/checkout@v4
----
الإجراء`checkout@v4`يسترجع كود المصدر من المستودع. الإصدار v4 يجلب تحسينات في الأداء والأمان.
==== 4. إعداد بايثون
----
- name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x'
استخدام`'3.x'`يسمح باستخدام الإصدار المستقر الأخير من Python 3 تلقائيًا، مما يبسط الصيانة.
==== 5. تثبيت التبعيات
```yaml
----
----
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install ruff pytest pytest-mock
----
هذه الخطوة: حدّث pip إلى أحدث نسخة - ثبّت تبعيات المشروع - أضف أدوات التطوير (التدقيق والاختبارات)
==== 6. لنتينغ باستخدام Ruff
----
- name: Run Linting (Ruff) run: ruff check .
**رف**هو linter Python فائق السرعة المكتوب بلغة Rust. إنه يجمع ميزات عدة أدوات (Flake8, Black, isort) في أداة واحدة فعالة
==== 7. تنفيذ الاختبارات
```yaml
----
----
- name: Run Tests (Pytest)
run: pytest
----
Pytest ينفذ مجموعة الاختبارات بالكامل، مما يضمن أن التعديلات لا تُدخل أي تراجعات.
== تكوين خط أنابيب النشر (CD)
=== هيكل Workflow CD
يتم تشغيل سير عمل CD فقط أثناء إصدارات GitHub ويؤتمت النشر إلى 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/*
----
=== تحليل الخطوات CD
==== 1. مُشغّل Release
----
on: release: types: - published
لا يبدأ خط أنابيب CI/CD إلا عند نشر إصدار على GitHub. هذا النهج يضمن تحكمًا دقيقًا في النشر.
==== 2. تثبيت أدوات البناء
```yaml
----
----
pip install setuptools wheel twine
----
- **setuptools**أدوات حزم بايثون - **عجلة**: صيغة توزيع بايثون حديثة - **خيط**أداة آمنة للرفع إلى PyPI
==== 3. تكوين المصادقة
----
env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
تستخدم المصادقة رمز API الخاص بـ PyPI المخزن كسرٍّ في GitHub، وهو أكثر أمانًا من بيانات الاعتماد التقليدية.
==== 4. البناء والنشر
```yaml
----
----
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
----
- `sdist`أنشئ توزيع المصدر - `bdist_wheel`: أنشئ wheel (توزيع ثنائي) - `twine upload`: ينشر التوزيعات على PyPI
== تكوين حزمة بايثون
=== هيكل ملف setup.py
لضمان عمل المسار، يجب أن يحتوي مشروعك على ملف`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",
],
},
)
----
=== النقاط الرئيسية لملف setup.py
1. **بيانات التعريف**الاسم, الإصدار, المؤلف, الوصف
2. **الاعتمادات**قائمة الحزم المطلوبة
3. **نقاط الدخول**: أوامر CLI المكشوفة
4. **المصنفات**: بيانات تعريفية لـ PyPI
== التأمين باستخدام أسرار GitHub
=== تكوين توكن PyPI
1. **إنشاء رمز API على PyPI**:
- سجّل الدخول إلى PyPI - انتقل إلى إعدادات الحساب > رموز API - أنشئ رمزًا جديدًا بأذونات مناسبة
1. **إضافة السر في GitHub** :
- إعدادات المستودع > الأسرار والمتغيرات > الإجراءات - أنشئ سرًا جديدًا باسم`PYPI_API_TOKEN` - الصق رمز PyPI الخاص بك
[plantuml, secrets-flow, svg]
----
@startuml
!theme plain
actor Developer as dev
participant "مستودع جيت هاب" 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
----
== سير عمل النشر الكامل
=== سلسلة النشر
[plantuml, deployment-sequence, svg]
----
@startuml
!theme plain
actor Developer as dev
participant "Git محلي" as git
participant "جيت هاب" as github
participant "إجراءات جيت هب" as actions
participant PyPI
participant "المستخدمون النهائيون" 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
----
=== حالات خط الأنابيب
[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
----
== الممارسات الجيدة والتحسينات
=== 1. إدارة الإصدارات
استخدم وسوم Git دلالية:
----
git tag -a v1.2.3 -m "Release version 1.2.3" git push origin v1.2.3
=== 2. اختبارات المصفوفة
لاختبار على إصدارات متعددة من Python :
```yaml
----
----
strategy:
matrix:
python-version: [3.8, 3.9, "3.10", "3.11"]
----
=== 3. ذاكرة التخزين المؤقت للتبعيات
سرّع عمليات البناء باستخدام التخزين المؤقت :
----
- name: Cache pip dependencies uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
=== 4. بيئات النشر
استخدم بيئات GitHub للنشر المتحكم فيه :
```yaml
----
----
jobs:
deploy:
environment: production
runs-on: ubuntu-latest
----
== حالة الاستخدام والبنية
=== مخطط حالة الاستخدام
[plantuml, use-cases, svg]
----
@startuml
!theme plain
left to right direction
actor "مطوّر" as dev
actor "GitHub إجراءات" as ga
actor "المستخدم النهائي" as user
package "نظام CI/CD" {
usecase "تشغيل التدقيق" as lint
usecase "تنفيذ الاختبارات" as test
usecase "إنشاء الحزمة" as build
usecase "انشر على PyPI" as publish
usecase "إنشاء إصدار" 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 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
Secure token-based
authentication
end note
[Secrets Store] .. N1
@enduml
----
== المراقبة وتصحيح الأخطاء
=== السجلات والمراقبة
يوفر GitHub Actions سجلات مفصلة لكل خطوة. لتصحيح الأخطاء :
1. **افحص السجلات**من كل خطوة
2. **فعّل التصحيح**مع`ACTIONS_STEP_DEBUG`
3. **استخدم القطع الأثرية**لحفظ ملفات البناء
----
- name: Upload build artifacts uses: actions/upload-artifact@v3 if: failure() with: name: build-logs path: build/
=== الإشعارات
أضف إشعارات Slack أو البريد الإلكتروني :
```yaml
----
----
- name: Notify on failure
if: failure()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
----
== الخاتمة
إعداد مسار CI/CD قوي باستخدام GitHub Actions يُحدث تحولًا جذريًا في تجربة التطوير. من خلال أتمتة التدقيق، والاختبارات والنشر، أنت :
- **قلل الأخطاء**في الإنتاج - **سرّع الدورات**من التطوير - **حسّنوا الثقة**في إصداراتكم - **يسهّل التعاون**في فريق
يمكن تعديل هذا الخط الأنابيب ليتناسب مع مختلف أنواع مشاريع بايثون عن طريق ضبط أدوات التحقق من الصياغة (أدوات اللنتينغ)، أطر عمل الاختبار، أو وجهات النشر.
الاستثمار الأولي في إعداد هذه سير عمل يُسترد بسرعة من خلال توفير الوقت وتقليل الأخطاء اليدوية أثناء النشر.
== مصادر إضافية
- https://docs.github.com/en/actions[توثيق GitHub Actions] - https://packaging.python.org/[دليل تغليف بايثون] - https://docs.pytest.org/[توثيق Pytest] - https://docs.astral.sh/ruff/[توثيق Ruff] - https://twine.readthedocs.io/[توثيق Twine]
تم الوصول إلى خط أنابيب وظيفي ! الآن لديك خط أنابيب CI/CD بسيط يتيح لك أتمتة اختباراتك ونشر حزمة Python الخاصة بك على PyPI مباشرةً من GitHub Actions.
ومع ذلك، فإن هذا الـpipeline يبقى بسيطًا عن قصد. لم يغطِ بعد بعض الجوانب الأساسية في سياق مهني:
اختبارات الإصدارات المتعددة لـ Python,
تحليل أمان تلقائي,
نشر تدريجي عبر Test PyPI,
مراقبة ومقاييس القناة,
أتمتة التحكم بالإصدار ودمج الممارسات الحديثة المثلى (pyproject.toml).
في الجزء التالي، سننتقل إلى المستوى الأعلى. ستتعلم كيف تحوّل هذا pipeline الأساسي إلى سلسلة نشر صناعية حقيقية، قوية وآمنة، جاهزة لمشاريع Python الإنتاجية.
----