حصہ 2: پیشگی Python CI/CD پائپ لائن کو صنعتی بنائیں اور محفوظ بنائیں
شائع شدہ: 18 July 2025
-
هدف : سادہ پائلائن کو مضبوط صنعتی پائلائن میں تبدیل کریں، جو.packaging اور سافٹ ویئر انجیئرنگ کے حديث معیارات کے مطابق ہو۔
مقدمہ
اسسلسلہ کی پہلی حصہ میں، ہم نے ایک سادہ لیکن مؤثر سیآئی/سیڈی پائلائن بنایا تاکہ پائتھن پیکج کے ٹیسٹس اور PyPI پر اشاعت خودکار بنایی جا سکے۔
اب یہ پائلائن کو پروفيشينل بنانا کا وقت آ گايا ہے۔ اس دوسری حصہ میں ہم:
-
مُعاصر pyproject.toml معيار کی طرف مائگریشن کریں
-
معیاری اور حفاظتی اوزار (Black, Mypy, Bandit, Safety) شامل کریں،
-
ملٹی ورژن ٹیسٹس تیار کرن,
-
ٹیسٹ PyPI کے ذریعے تدريجی ڈپلویمنت شامل کریں،
-
ویژننگ کو خودکار بنائیں اور پائپ لائن کی نگرانی بہتر بنائیں۔
اپنے آپ کو تیار کریں؛ ایک فاعل پائیپ لائن سے پیشہ ورانہ CI/CD انفراسٹrakچر پر منتقل ہوں۔
پایتھن ایپلیکیشن کو PyPI پر ڈپلوئی کرنے کے لیے مضبوط CI/CD پائپلائن کی ضرورت ہوتی ہے جو ٹیسٹس، بنائ اور پیکیجز کی اشاعت کو خودکار بناتی ہے۔ یہ مضامین مکمل pipeline کی تنظیم کا تفصیل دیتا ہے جِسے GitHub Actions سے استعمال کر کے Python CLI application کے لیے کیا جاتا ہے، اور اس کے لیے modern Python ecosystem کی اچھی اصولوں پر مشتمل ہے۔
CI/CD پائلائن کی معماری
ہم بنانے والا پاپ لائن چند مرحلوں والی رویہ پر مبنی ہے :
@startuml
!theme plain
title پائپ لائن سی آئی ڈی Python سے PyPI
skinparam backgroundColor #f8f9fa
skinparam componentStyle rectangle
rectangle "ڈویلاپر" as dev
rectangle "گیٹ ہب ریپازٹری" as repo {
rectangle ".github/workflows/" as workflows
rectangle "آزمائشیں/" as tests
rectangle "setup.py / pyproject.toml" as setup
rectangle "requirements.txt" as req
}
rectangle "GitHub ایکشنز" as actions {
rectangle "ٹیسٹ رانر" as test_runner
rectangle "بنانا" as build
rectangle "سکیورٹی سکن" as security
rectangle "معیارات کی جانچ" as quality
}
rectangle "پائی پی آئی" as pypi {
rectangle "ٹیسٹ PyPI" as test_pypi
rectangle "پروڈکشن PyPI" as prod_pypi
}
rectangle "صارفین" as users
dev --> repo : push/PR
repo --> actions : trigger workflow
actions --> test_runner : run tests
actions --> security : security checks
actions --> quality : code quality
actions --> build : build package
build --> test_pypi : deploy (pre-release)
build --> prod_pypi : deploy (release)
prod_pypi --> users : install package
@enduml
پروجیکٹ کی سٹرکچر
ایک Python CLI ایپلیکیشن جسے توزیع کے لیے تیار کیا گیا ہے، ایک معيار کے مطابق ایک ساخت کی پیروی کرنی ہوگی :
playlist-downloader/
├── .github/
│ └── workflows/
│ ├── ci.yml
│ ├── release.yml
│ └── security.yml
├── src/
│ └── playlist_downloader/
│ ├── __init__.py
│ ├── cli.py
│ ├── core/
│ └── adapters/
├── tests/
│ ├── unit/
│ ├── integration/
│ └── conftest.py
├── docs/
├── pyproject.toml
├── requirements.txt
├── requirements-dev.txt
├── MANIFEST.in
├── README.md
├── LICENSE
└── CHANGELOG.md
pyproject.toml کے ساتھ پیکج کی ترتیب
فائل`pyproject.toml`یہ پائتھن پیکیجیز کو کنفیجر کرنے کا مُعصِر معیار ہے :
[build-system]
requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2"]
build-backend = "setuptools.build_meta"
[project]
name = "playlist-downloader"
authors = [
{name = "Christophe Hérolivier", email = "[email protected]"},
]
description = "CLI tool for YouTube playlist management"
readme = "README.md"
requires-python = ">=3.8"
keywords = ["youtube", "playlist", "cli", "downloader"]
license = {text = "MIT"}
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: End Users/Desktop",
"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",
"Topic :: Multimedia :: Sound/Audio",
"Topic :: Utilities",
]
dependencies = [
"typer>=0.9.0",
"yt-dlp>=2023.7.6",
"google-api-python-client>=2.0.0",
"google-auth-oauthlib>=1.0.0",
"pyyaml>=6.0",
"rich>=13.0.0",
]
dynamic = ["version"]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-cov>=4.0.0",
"pytest-mock>=3.10.0",
"black>=23.0.0",
"flake8>=6.0.0",
"mypy>=1.0.0",
"pre-commit>=3.0.0",
"tox>=4.0.0",
]
test = [
"pytest>=7.0.0",
"pytest-cov>=4.0.0",
"pytest-mock>=3.10.0",
]
[project.urls]
Homepage = "https://github.com/cheroliv/playlist-downloader"
Documentation = "https://github.com/cheroliv/playlist-downloader#readme"
Repository = "https://github.com/cheroliv/playlist-downloader.git"
"Bug Tracker" = "https://github.com/cheroliv/playlist-downloader/issues"
[project.scripts]
playlist-downloader = "playlist_downloader.cli:main"
[tool.setuptools_scm]
write_to = "src/playlist_downloader/_version.py"
[tool.setuptools.packages.find]
where = ["src"]
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--cov=src/playlist_downloader",
"--cov-report=html",
"--cov-report=term-missing",
"--cov-fail-under=85",
]
[tool.black]
line-length = 88
target-version = ['py38']
include = '\.pyi?$'
extend-exclude = '''
/(
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
'''
[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_unreachable = true
strict_equality = true
[[tool.mypy.overrides]]
module = [
"yt_dlp.*",
"googleapiclient.*",
"google_auth_oauthlib.*",
]
ignore_missing_imports = true
CI/CD ورک فلو - ٹیسٹس اور کیفیت
وورکفلو مرکزی (ci.yml) پایتھن کے کئی وژنز پر ٹیسٹس چلاتا ہے:
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Cache dependencies
uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.cache/pre-commit
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
- name: Lint with flake8
run: |
flake8 src tests --count --select=E9,F63,F7,F82 --show-source --statistics
flake8 src tests --count --exit-zero --max-complexity=10 --max-line-length=88 --statistics
- name: Check code formatting with Black
run: black --check src tests
- name: Type checking with mypy
run: mypy src
- name: Run tests with pytest
run: |
pytest tests/ -v --cov=src/playlist_downloader \
--cov-report=xml --cov-report=term-missing
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
if: matrix.python-version == '3.11'
with:
file: ./coverage.xml
flags: unittests
name: codecov-umbrella
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install bandit[toml] safety
- name: Run security checks with bandit
run: bandit -r src/ -f json -o bandit-report.json
- name: Check dependencies with safety
run: safety check --json --output safety-report.json
- name: Upload security reports
uses: actions/upload-artifact@v3
if: always()
with:
name: security-reports
path: |
bandit-report.json
safety-report.json
build:
needs: [test, security]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install build dependencies
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Build package
run: python -m build
- name: Check package with twine
run: twine check dist/*
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: dist
path: dist/
ریلیز اور ڈپلوائمنٹ ورک فلو
ریلز کا ورکفلو (release.yml) یہ PyPI پر خودکار اشاعہ کی ترتیبات کرتا ہے :
name: Release
on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
default: 'test'
type: choice
options:
- test
- production
env:
PYTHON_VERSION: "3.11"
jobs:
release:
runs-on: ubuntu-latest
environment:
name: ${{ github.event.inputs.environment || (startsWith(github.ref, 'refs/tags/') && 'production' || 'test') }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Build package
run: python -m build
- name: Check package
run: twine check dist/*
- name: Publish to Test PyPI
if: github.event.inputs.environment == 'test' || (startsWith(github.ref, 'refs/tags/') && contains(github.ref, 'rc'))
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
run: |
twine upload --repository testpypi dist/*
- name: Publish to PyPI
if: github.event.inputs.environment == 'production' || (startsWith(github.ref, 'refs/tags/') && !contains(github.ref, 'rc'))
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
twine upload dist/*
- name: Create GitHub Release
if: startsWith(github.ref, 'refs/tags/')
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: ${{ contains(github.ref, 'rc') }}
post-release:
needs: release
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Test installation from PyPI
run: |
sleep 60 # Attendre la propagation sur PyPI
pip install playlist-downloader
playlist-downloader --version
- name: Update documentation
run: |
# Script pour mettre à jour la documentation
echo "Documentation updated for version ${GITHUB_REF#refs/tags/}"
معماروی ڈائاگرام
سیکوانس ڈائاگرام - ریلیس پروسیس
@startuml
!theme plain
title PyPI کے طرف رلیز کی ترتیب
actor Developer as dev
participant "گٹهاب" as gh
participant "گیٹ ہب ایکشنز" as ga
participant "امتحان PyPI" as tpypi
participant "پیپái" as pypi
participant "صارفین" as users
dev -> gh : git push --tags v1.2.3
gh -> ga : trigger release workflow
ga -> ga : checkout code
ga -> ga : setup Python environment
ga -> ga : install dependencies
ga -> ga : run tests
ga -> ga : build package (wheel + sdist)
ga -> ga : check package with twine
alt Pre-release (rc tag)
ga -> tpypi : upload to Test PyPI
tpypi -> ga : confirm upload
else Stable release
ga -> pypi : upload to PyPI
pypi -> ga : confirm upload
end
ga -> gh : create GitHub release
ga -> ga : test installation from PyPI
users -> pypi : pip install playlist-downloader
pypi -> users : download package
@enduml
حالت کا ڈائاگرام - پیکیج کا زندگی چکر
@startuml
!theme plain
title پایتھن پکیج کی حالت
[*] --> Development
Development --> Testing : commit/PR
Testing --> Development : tests fail
Testing --> Built : tests pass
Built --> TestPyPI : pre-release tag
Built --> PyPI : stable tag
TestPyPI --> PyPI : validation OK
PyPI --> Published
Published --> [*]
state Development {
[*] --> Coding
Coding --> LocalTesting
LocalTesting --> Coding : fix issues
LocalTesting --> ReadyForCI : all tests pass
}
state Testing {
[*] --> CITests
CITests --> SecurityScan
SecurityScan --> QualityCheck
QualityCheck --> BuildValidation
}
@enduml
ڈپلویمنٹ ڈائن - سیآئی/ڈیای انفراسٹرکچر
@startuml
!theme plain
title ڈپلویمنٹ انفراسٹرکچر
node "GitHub" {
component "ریپازٹری" as repo
component "کرویاں چلانے والا" as runner
component "رازوں کا سٹور" as secrets
}
node "PyPI انفراسٹرکچر" {
component "PyPI" as pypi
component "ٹیسٹ PyPI" as testpypi
database "پیکیج انڈکس" as index
}
node "ڈویلپر مشین" {
component "گٹ کلائنٹ" as git
component "پائیٹھن ماحول" as python
component "IDE" as ide
}
node "صارف کا ماحول" {
component "پپ" as pip_client
component "Python Runtime" as py_runtime
}
git --> repo : push code/tags
repo --> runner : trigger workflows
runner --> secrets : read API tokens
runner --> testpypi : upload pre-release
runner --> pypi : upload release
pypi --> index : store package
pip_client --> pypi : download package
py_runtime <-- pip_client : install package
@enduml
پائلائن کے آبجیکٹس اور ماڈلز
کلاس ڈائگرام - سی آئی/سی ڈی مودلز
@startuml
!theme plain
title CI/CD پائلائن کے ماڈلز
class PipelineConfig {
+python_versions: List[str]
+test_environments: List[str]
+security_checks: bool
+coverage_threshold: float
+validate()
}
class BuildArtifact {
+name: str
+version: str
+wheel_path: str
+sdist_path: str
+checksums: Dict[str, str]
+validate_integrity()
}
class TestResult {
+test_suite: str
+python_version: str
+passed: int
+failed: int
+coverage: float
+duration: float
+is_success(): bool
}
class SecurityReport {
+bandit_issues: List[Issue]
+safety_vulnerabilities: List[Vulnerability]
+severity_level: str
+is_secure(): bool
}
class DeploymentTarget {
+name: str
+url: str
+api_token: str
+environment: str
+deploy(artifact: BuildArtifact)
}
class ReleaseManager {
+version: str
+changelog: str
+artifacts: List[BuildArtifact]
+test_results: List[TestResult]
+security_report: SecurityReport
+deploy_to_test()
+deploy_to_production()
+create_github_release()
}
PipelineConfig ||--o{ TestResult
ReleaseManager *-- BuildArtifact
ReleaseManager *-- SecurityReport
ReleaseManager o-- DeploymentTarget
DeploymentTarget ..> BuildArtifact : uses
@enduml
رازوں کی تنصیب
پائپ لائن کو کام کرنے کے لیے، آپ کو گٹ ہب میں درج ذیل سیکرٹس کنفیجر کرنے ہوں گے :
GitHub Actions کے راز
# Dans Settings > Secrets and variables > Actions
# Token PyPI pour la production
PYPI_API_TOKEN=pypi-...
# Token Test PyPI pour les pré-releases
TEST_PYPI_API_TOKEN=pypi-...
# Token GitHub pour créer les releases
GITHUB_TOKEN=(automatiquement fourni)
# Token Codecov (optionnel)
CODECOV_TOKEN=...
PyPI ٹوکنز کی تولید
# 1. Créer un compte sur PyPI et Test PyPI
# 2. Aller dans Account Settings > API tokens
# 3. Créer un token avec scope "Entire account" ou spécifique au projet
# 4. Format du token : pypi-AgEIcHlwaS5vcmc...
لوکل ڈویلپمنٹ اسکریپٹس
تطبیق کو آسان بنانے کے لیے فائدہ مند اسکرپٹس بنائیں :
Makefile
.PHONY: install test lint format security build clean release-test release-prod
install:
pip install -e ".[dev]"
test:
pytest tests/ -v --cov=src/playlist_downloader
lint:
flake8 src tests
mypy src
format:
black src tests
security:
bandit -r src/
safety check
build:
python -m build
twine check dist/*
clean:
rm -rf build/ dist/ *.egg-info/
find . -type d -name __pycache__ -delete
find . -name "*.pyc" -delete
release-test: clean build
twine upload --repository testpypi dist/*
release-prod: clean build
twine upload dist/*
pre-commit: format lint test security
@echo "✅ Prêt pour commit"
اسکریپت ورژن
#!/usr/bin/env python3
"""Script pour gérer les versions du projet."""
import sys
import subprocess
from pathlib import Path
def get_current_version():
"""Récupère la version actuelle depuis git."""
try:
result = subprocess.run(
["git", "describe", "--tags", "--abbrev=0"],
capture_output=True,
text=True,
check=True
)
return result.stdout.strip()
except subprocess.CalledProcessError:
return "0.0.0"
def create_version_tag(version, message=None):
"""Crée un tag de version."""
if not version.startswith('v'):
version = f'v{version}'
tag_message = message or f"Release {version}"
subprocess.run(["git", "tag", "-a", version, "-m", tag_message], check=True)
print(f"✅ Tag {version} créé")
# Push le tag
subprocess.run(["git", "push", "origin", version], check=True)
print(f"✅ Tag {version} poussé vers origin")
if __name__ == "__main__":
if len(sys.argv) < 2:
current = get_current_version()
print(f"Version actuelle: {current}")
print("Usage: python version.py <new_version> [message]")
sys.exit(1)
new_version = sys.argv[1]
message = sys.argv[2] if len(sys.argv) > 2 else None
create_version_tag(new_version, message)
اچھی مشقیں اور تجاویز
ورژننگ سمنتيک
سیمنٹیک ورژننگ (SemVer) استعمال کریں :
-
MAJOR.MINOR.PATCH(ex: 1.2.3) -
MAJOR: نَ سازگار تبدیلیں -
MINOR: matching نئی خصوصیات -
PATCH: متوافقت باگ فیکس
شاخیوں کی استراتژی
main ──●──●──●──●──●────●── (releases stables)
/ / /
develop ──●──●──●──●──●──●──●──●── (développement)
/ / /
feature/xxx ●──●──●──●──●──/ (fonctionnalités)
آزمائش اور کوریج
-
کد کی سب سے کم ڈھانک : 85%
-
کسبی منطق کے لیے یونٹ ٹیسٹس
-
ایڈاپٹرز کے لیے انٹیگریٹنگ ٹیسٹس
-
CLI کے لیے ایند-ٹو-ایند ٹس
امان
-
خودکار اسکان انحصارات (سلامتی)
-
اسٹیٹک تجزیہ کوڈ (Bandit)
-
کوڈ میں کبھی راز نہیں
-
PyPI کے مخصوص ٹوکنز استعمال
نگرانی اور مشاہدہ
پائپ لائن کے میٹرکس
# .github/workflows/metrics.yml
name: Pipeline Metrics
on:
workflow_run:
workflows: ["CI", "Release"]
types: [completed]
jobs:
metrics:
runs-on: ubuntu-latest
steps:
- name: Collect metrics
run: |
echo "Pipeline: ${{ github.event.workflow_run.name }}"
echo "Status: ${{ github.event.workflow_run.conclusion }}"
echo "Duration: ${{ github.event.workflow_run.updated_at - github.event.workflow_run.created_at }}"
# Envoyer vers système de monitoring
نٹیفیکیشن
# Ajout dans les workflows pour notifications
- name: Notify on failure
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
text: "❌ Pipeline failed for ${{ github.repository }}"
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
نتیجہ
یہ مکمل CI/CD پائلن Python کے لیے مہیا کرتا ہے :
-
مکمل خودکار: کوڈ کی تصدیق سے شائع کرنے تک
-
سکیورٹی: خودکار اسแกن اور رازوں کی محفوظ انتظام
-
معیارملٹی ورژن ٹیسٹس، لنٹنگ اور کوڈ کی کوریج
-
اعتمادیت: درجہ وار اِعمال Test PyPI کے ذریعے
-
ترسیل: artifacts, رپورٹس اور releases GitHub
ان ممارسات کو اپنانے سے Python CLI ایپلیکیشنز کے لیے ایک مضبوط اور پیشہ ورانہ ڈلیوری عمل بنتا ہے، جو ان کی مرمت اور ترقی کو آسان بناتا ہے اور لمدоро تک کے منصوبوں کے لیے دیرباز بناتا ہے۔