آپدیت پکیجهای Python و Node.js با هوش مصنوعی؛ مدیریت هوشمند Dependencyها
در این آموزش سیستمی میسازیم که Dependencyهای قدیمی Python و Node.js را پیدا میکند، ارتقاها را اولویتبندی میکند و با API درواره یک برنامه بهروزرسانی قابل تست میسازد.
آپدیت Dependencyها یکی از کارهایی است که معمولاً تا زمان بروز مشکل به تعویق میافتد. توسعهدهنده دستور npm outdated یا pip list --outdated را اجرا میکند، با فهرستی طولانی از نسخههای جدید روبهرو میشود و تصمیم میگیرد فعلاً هیچ چیزی را تغییر ندهد.
در حالت دیگر، همه پکیجها یکباره به آخرین نسخه ارتقا پیدا میکنند و مجموعهای از خطاهای جدید ظاهر میشود:
- تستها شکست میخورند.
- Typeها تغییر میکنند.
- یک متد حذف یا Deprecated شده است.
- تنظیمات Framework تغییر کردهاند.
- نسخه جدید پکیج با Runtime پروژه سازگار نیست.
- Peer Dependencyها با یکدیگر تعارض دارند.
- فایل Lock تغییر بزرگی میکند.
- مشخص نیست کدام ارتقا باعث مشکل شده است.
هوش مصنوعی میتواند در تحلیل این فرایند کمک کند؛ اما نباید نسخه پکیجها را از حافظه خود حدس بزند یا همه Dependencyها را بدون آزمایش تغییر دهد.
روش مناسب این است:
- نسخه فعلی و نسخههای قابلدسترسی با ابزار رسمی Package Manager استخراج شوند.
- Dependencyهای مستقیم از غیرمستقیم جدا شوند.
- تغییر Major، Minor و Patch مشخص شود.
- Release Note و Migration Guide رسمی در صورت وجود بررسی شود.
- مدل هوش مصنوعی یک Upgrade Plan ساختاریافته پیشنهاد دهد.
- هر ارتقا در محیط جداگانه اعمال شود.
- نصب، Lint، Type Check، Test و Build اجرا شوند.
- نتیجه هر مرحله ثبت شود.
- ارتقاهای پرریسک جداگانه انجام شوند.
در این مقاله، یک Dependency Upgrade Assistant برای پروژههای Python و Node.js میسازیم و آن را به API هوش مصنوعی درواره متصل میکنیم.
Dependency چیست؟
Dependency یا وابستگی، پکیج یا کتابخانهای است که برنامه برای اجرا، Build یا توسعه به آن نیاز دارد.
در Python:
fastapi
pydantic
httpx
pytest
در Node.js:
{
"dependencies": {
"express": "^5.0.0",
"zod": "^4.0.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"vitest": "^3.0.0"
}
}
Dependencyها معمولاً در چند گروه قرار میگیرند.
Dependency مستقیم
پکیجی که خود پروژه مستقیماً تعریف کرده است:
fastapi
Dependency غیرمستقیم
پکیجی که توسط یکی از Dependencyهای مستقیم نصب شده است.
برای مثال:
پروژه
└── FastAPI
└── Starlette
در این حالت، پروژه FastAPI را مستقیم استفاده میکند و Starlette ممکن است بهصورت غیرمستقیم وارد Dependency Graph شود.
Development Dependency
پکیجی که برای تست، Lint، Build یا توسعه استفاده میشود:
pytest
ruff
typescript
vitest
eslint
Runtime Dependency
پکیجی که برنامه هنگام اجرا به آن نیاز دارد:
fastapi
httpx
express
zod
این تفکیک در برنامهریزی ارتقا اهمیت دارد. تغییر یک Test Runner با تغییر Framework اصلی برنامه اثر یکسانی ندارد.
چرا آپدیت همه پکیجها به آخرین نسخه روش مناسبی نیست؟
دستور زیر ممکن است تغییر بزرگی ایجاد کند:
npm install package-name@latest
یا در Python:
python -m pip install \
--upgrade package-name
آخرین نسخه لزوماً بهترین نسخه برای پروژه فعلی نیست. پیش از ارتقا باید بررسی شود:
- نسخه Runtime پروژه چیست؟
- نسخه جدید از این Runtime پشتیبانی میکند؟
- تغییر Major رخ داده است؟
- Migration Guide وجود دارد؟
- APIهای استفادهشده تغییر کردهاند؟
- Dependencyهای دیگر با نسخه جدید سازگارند؟
- تست کافی برای شناسایی Regression وجود دارد؟
- نسخه موردنظر در محیط Build و Production قابل نصب است؟
هدف سیستم هوشمند نباید «جدیدترین نسخه ممکن» باشد. هدف باید «ارتقای قابلکنترل، قابلاندازهگیری و قابلبازگشت» باشد.
مفهوم Semantic Versioning
بسیاری از پروژهها از الگوی زیر استفاده میکنند:
MAJOR.MINOR.PATCH
برای مثال:
4.7.2
4نسخه Major است.7نسخه Minor است.2نسخه Patch است.
در حالت ایدهآل:
- Patch برای اصلاحهای سازگار است.
- Minor قابلیت جدید سازگار اضافه میکند.
- Major میتواند تغییر ناسازگار داشته باشد.
اما این فقط یک قرارداد رایج است و همه پکیجها دقیقاً به یک شکل از آن پیروی نمیکنند. بنابراین اختلاف Major یک علامت ریسک است، نه اثبات قطعی Breaking Change.
معنی Current، Wanted و Latest در npm
دستور زیر پکیجهای قدیمی را نمایش میدهد:
npm outdated
خروجی نمونه:
Package Current Wanted Latest
express 4.21 4.21 5.1
typescript 5.7 5.9 5.9
vitest 3.0 3.2 4.0
براساس مستندات رسمی npm outdated:
currentنسخه نصبشده است.wantedجدیدترین نسخهای است که محدوده تعریفشده درpackage.jsonرا رعایت میکند.latestنسخهای است که با Tag مربوط به Latest در Registry معرفی شده است.
اگر wanted و latest متفاوت باشند، احتمالاً محدوده فعلی پروژه اجازه نصب نسخه Latest را نمیدهد.
خروجی JSON:
npm outdated --json
این خروجی برای پردازش برنامهای مناسب است.
نکته مهم: npm outdated در صورت وجود پکیج قدیمی ممکن است با Exit Code غیرصفر تمام شود. در Script جمعآوری اطلاعات نباید این وضعیت را با خطای Parse اشتباه گرفت.
پیدا کردن پکیجهای قدیمی Python
برای Python:
python -m pip list --outdated
خروجی JSON:
python -m pip list \
--outdated \
--format=json
طبق مستندات رسمی pip list، گزینه --outdated پکیجهای دارای نسخه جدیدتر را نمایش میدهد و --format=json خروجی قابلپردازش تولید میکند.
نمونه:
[
{
"name": "fastapi",
"version": "0.115.0",
"latest_version": "0.116.0",
"latest_filetype": "wheel"
}
]
برای مشاهده اطلاعات کامل محیط Python:
python -m pip inspect
pip inspect یک گزارش JSON شامل پکیجهای نصبشده و اطلاعات محیط تولید میکند. قالب آن در مستندات رسمی pip inspect تعریف شده است.
معماری سیستم مدیریت Dependency
معماری پیشنهادی:
requirements.txt / pyproject.toml
package.json / package-lock.json
↓
Package Manager
↓
Outdated Inventory
↓
تفکیک مستقیم و غیرمستقیم
↓
تشخیص Major / Minor / Patch
↓
Project Manifest
↓
Release Notes مرتبط
↓
API هوش مصنوعی درواره
↓
Upgrade Plan ساختاریافته
↓
اجرای یک ارتقا
↓
Install / Lint / Type Check / Test / Build
↓
ثبت نتیجه
↓
مرحله بعد
ساختار پروژه
dependency-upgrade-assistant/
├── backend/
│ ├── requirements.txt
│ ├── pyproject.toml
│ └── tests/
├── frontend/
│ ├── package.json
│ ├── package-lock.json
│ └── src/
├── release-notes/
│ ├── python/
│ └── node/
├── scripts/
│ ├── collect_dependencies.py
│ ├── build_upgrade_plan.py
│ └── validate_plan.py
├── upgrade-output/
├── project-manifest.json
├── requirements-tools.txt
└── .env
نصب ابزارهای پروژه
فایل requirements-tools.txt:
openai
python-dotenv
pydantic
packaging
محیط مجازی:
python -m venv .venv
فعالسازی:
source .venv/bin/activate
نصب:
python -m pip install \
-r requirements-tools.txt
تعریف Project Manifest
هوش مصنوعی باید بداند هر Component چگونه نصب، تست و Build میشود.
فایل project-manifest.json:
{
"projectName": "commerce-platform",
"repositoryType": "monorepo",
"components": [
{
"name": "backend",
"path": "backend",
"ecosystem": "python",
"runtime": {
"name": "python",
"supportedVersions": [
"3.11",
"3.12",
"3.13"
]
},
"dependencyFile": "requirements.txt",
"commands": {
"install": "python -m pip install -r requirements.txt",
"dependencyCheck": "python -m pip check",
"lint": "ruff check .",
"test": "pytest",
"typecheck": "mypy app"
}
},
{
"name": "frontend",
"path": "frontend",
"ecosystem": "npm",
"runtime": {
"name": "node",
"supportedVersions": [
"22"
]
},
"dependencyFile": "package.json",
"lockFile": "package-lock.json",
"commands": {
"install": "npm ci",
"test": "npm test",
"typecheck": "npm run typecheck",
"build": "npm run build"
}
}
],
"upgradePolicy": {
"patchBatchSize": 5,
"minorBatchSize": 3,
"majorBatchSize": 1,
"requireTestsAfterEveryBatch": true,
"requireOfficialMigrationGuideForMajor": true,
"doNotInventVersions": true,
"doNotInventCommands": true,
"doNotApplyChangesAutomatically": true
}
}
با این Manifest مدل حق ندارد دستور Build یا نسخه Runtime جدیدی اختراع کند.
جمعآوری Dependencyهای Python و Node.js
فایل scripts/collect_dependencies.py:
from __future__ import annotations
import json
import subprocess
from pathlib import Path
from typing import Any
from packaging.requirements import Requirement
from packaging.version import (
InvalidVersion,
Version,
)
OUTPUT_DIRECTORY = Path("upgrade-output")
def run_command(
command: list[str],
cwd: str,
) -> subprocess.CompletedProcess[str]:
return subprocess.run(
command,
cwd=cwd,
text=True,
capture_output=True,
check=False,
)
def classify_version_change(
current: str,
latest: str,
) -> str:
try:
current_version = Version(current)
latest_version = Version(latest)
except InvalidVersion:
return "unknown"
if (
current_version.major
!= latest_version.major
):
return "major"
if (
current_version.minor
!= latest_version.minor
):
return "minor"
if (
current_version.micro
!= latest_version.micro
):
return "patch"
if current_version != latest_version:
return "prerelease-or-metadata"
return "none"
def normalize_python_name(
name: str,
) -> str:
return (
name.lower()
.replace("_", "-")
.replace(".", "-")
)
def read_python_direct_dependencies(
requirements_path: Path,
) -> dict[str, str]:
dependencies: dict[str, str] = {}
for raw_line in requirements_path.read_text(
encoding="utf-8"
).splitlines():
line = raw_line.strip()
if (
not line
or line.startswith("#")
or line.startswith("-")
):
continue
try:
requirement = Requirement(line)
except Exception:
continue
normalized_name = normalize_python_name(
requirement.name
)
dependencies[normalized_name] = str(
requirement.specifier
)
return dependencies
def collect_python() -> dict[str, Any]:
component_path = Path("backend")
requirements_path = (
component_path / "requirements.txt"
)
direct_dependencies = (
read_python_direct_dependencies(
requirements_path
)
)
outdated_result = run_command(
[
"python",
"-m",
"pip",
"list",
"--outdated",
"--format=json",
],
cwd=str(component_path),
)
if outdated_result.returncode != 0:
raise RuntimeError(
"pip list failed:\n"
+ outdated_result.stderr
)
outdated_packages = json.loads(
outdated_result.stdout
)
inspect_result = run_command(
[
"python",
"-m",
"pip",
"inspect",
],
cwd=str(component_path),
)
if inspect_result.returncode != 0:
raise RuntimeError(
"pip inspect failed:\n"
+ inspect_result.stderr
)
inspect_report = json.loads(
inspect_result.stdout
)
items = []
for package in outdated_packages:
normalized_name = normalize_python_name(
package["name"]
)
current = package["version"]
latest = package["latest_version"]
items.append(
{
"name": package["name"],
"current": current,
"wanted": None,
"latest": latest,
"changeType": (
classify_version_change(
current,
latest,
)
),
"direct": (
normalized_name
in direct_dependencies
),
"declaredRange": (
direct_dependencies.get(
normalized_name
)
),
}
)
return {
"ecosystem": "python",
"component": "backend",
"runtime": (
inspect_report
.get("environment", {})
.get("python_version")
),
"packages": items,
}
def collect_node() -> dict[str, Any]:
component_path = Path("frontend")
package_json = json.loads(
(
component_path / "package.json"
).read_text(encoding="utf-8")
)
direct_dependencies = {
**package_json.get(
"dependencies",
{},
),
**package_json.get(
"devDependencies",
{},
),
**package_json.get(
"peerDependencies",
{},
),
**package_json.get(
"optionalDependencies",
{},
),
}
result = run_command(
[
"npm",
"outdated",
"--json",
],
cwd=str(component_path),
)
if not result.stdout.strip():
outdated = {}
else:
outdated = json.loads(
result.stdout
)
items = []
for name, package in outdated.items():
current = str(
package.get("current", "")
)
wanted = str(
package.get("wanted", "")
)
latest = str(
package.get("latest", "")
)
items.append(
{
"name": name,
"current": current,
"wanted": wanted,
"latest": latest,
"changeType": (
classify_version_change(
current,
latest,
)
),
"direct": (
name in direct_dependencies
),
"declaredRange": (
direct_dependencies.get(name)
),
}
)
return {
"ecosystem": "npm",
"component": "frontend",
"packages": items,
}
def main() -> None:
OUTPUT_DIRECTORY.mkdir(
parents=True,
exist_ok=True,
)
inventory = {
"formatVersion": 1,
"components": [
collect_python(),
collect_node(),
],
}
output_path = (
OUTPUT_DIRECTORY
/ "dependency-inventory.json"
)
output_path.write_text(
json.dumps(
inventory,
ensure_ascii=False,
indent=2,
),
encoding="utf-8",
)
print(
f"Dependency inventory saved to "
f"{output_path}"
)
if __name__ == "__main__":
main()
نکته مهم درباره محیط Python
اسکریپت pip list و pip inspect را در محیط Python فعال اجرا میکند. اگر ابزار مدیریت Dependency شما محیط دیگری ساخته است، باید Python همان محیط را اجرا کنید.
برای مثال:
backend/.venv/bin/python \
-m pip list \
--outdated \
--format=json
در پروژه واقعی بهتر است مسیر Interpreter در Manifest تعریف شود:
{
"pythonExecutable": "backend/.venv/bin/python"
}
سپس Script همان مقدار را استفاده کند.
اجرای Inventory
ابتدا Dependencyهای Backend و Frontend را نصب کنید:
cd backend
python -m pip install -r requirements.txt
cd ..
برای Frontend:
cd frontend
npm ci
cd ..
سپس:
python scripts/collect_dependencies.py
نمونه خروجی:
{
"formatVersion": 1,
"components": [
{
"ecosystem": "python",
"component": "backend",
"runtime": "3.12",
"packages": [
{
"name": "fastapi",
"current": "0.115.0",
"wanted": null,
"latest": "0.116.1",
"changeType": "minor",
"direct": true,
"declaredRange": "==0.115.0"
}
]
},
{
"ecosystem": "npm",
"component": "frontend",
"packages": [
{
"name": "vitest",
"current": "3.2.0",
"wanted": "3.2.4",
"latest": "4.0.0",
"changeType": "major",
"direct": true,
"declaredRange": "^3.2.0"
}
]
}
]
}
نسخهها در این مثال صرفاً نمونه هستند. سیستم واقعی باید خروجی Package Manager را منبع حقیقت بداند.
چرا مدل نباید نسخه Latest را خودش تعیین کند؟
نسخه پکیجها اطلاعات متغیر و وابسته به زمان است. مدل ممکن است:
- نسخه قدیمی پیشنهاد دهد.
- نسخهای را تولید کند که وجود ندارد.
- نسخه Pre-release را Stable فرض کند.
- سازگاری Runtime را اشتباه تشخیص دهد.
- Latest Tag را با بیشترین شماره نسخه یکی بداند.
بنابراین نسخهها باید از pip، npm یا Registry رسمی دریافت و سپس برای تحلیل به مدل ارسال شوند.
جمعآوری Release Note
تشخیص Major یا Minor کافی نیست. برای Dependencyهای مهم باید Release Note یا Migration Guide رسمی را بررسی کرد.
فایلها را در مسیر زیر ذخیره کنید:
release-notes/
├── python/
│ └── fastapi.md
└── node/
└── vitest.md
بهتر است همراه هر فایل Metadata ذخیره شود:
{
"package": "vitest",
"fromVersion": "3.2.0",
"toVersion": "4.0.0",
"sourceType": "official-release-notes",
"sourceUrl": "OFFICIAL_RELEASE_NOTES_URL",
"retrievedAt": "YYYY-MM-DD"
}
مدل فقط باید براساس Release Note ارائهشده تحلیل کند، نه حافظه عمومی خود.
اتصال به API هوش مصنوعی درواره
در درواره ثبتنام و کلید API دریافت کنید.
فایل .env:
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL=MODEL_ID_DARVAREH
Base URL:
https://api.darvareh.ir/v1
برای انتخاب مدل و مشاهده قیمت بهروز، صفحه مدلهای درواره را ببینید.
تعریف مدل Upgrade Plan
فایل scripts/build_upgrade_plan.py:
from __future__ import annotations
import json
import os
from pathlib import Path
from typing import Literal
from openai import OpenAI
from pydantic import BaseModel, Field
class UpgradeItem(BaseModel):
component: str
ecosystem: Literal["python", "npm"]
package: str
from_version: str
to_version: str
change_type: str
risk: Literal["low", "medium", "high"]
batch: int
reason: str
expected_impact: list[str] = Field(
default_factory=list
)
prechecks: list[str] = Field(
default_factory=list
)
verification_commands: list[str] = Field(
default_factory=list
)
needs_release_notes: bool
needs_human_review: bool
class UpgradePlan(BaseModel):
summary: str
recommended_runtime_changes: list[str] = Field(
default_factory=list
)
batches: list[UpgradeItem] = Field(
default_factory=list
)
blocked_packages: list[str] = Field(
default_factory=list
)
assumptions: list[str] = Field(
default_factory=list
)
def read_json(path: str) -> dict:
return json.loads(
Path(path).read_text(
encoding="utf-8"
)
)
def load_release_notes() -> dict[str, str]:
notes: dict[str, str] = {}
for path in Path(
"release-notes"
).rglob("*.md"):
content = path.read_text(
encoding="utf-8",
errors="replace",
)
notes[str(path)] = content[:15000]
return notes
def strip_code_fence(value: str) -> str:
text = value.strip()
if not text.startswith("```"):
return text
lines = text.splitlines()
content = "\n".join(
lines[1:-1]
).strip()
if content.startswith("json"):
content = content[4:].lstrip()
return content
def main() -> None:
inventory = read_json(
"upgrade-output/dependency-inventory.json"
)
manifest = read_json(
"project-manifest.json"
)
release_notes = load_release_notes()
client = OpenAI(
api_key=os.environ[
"DARVAREH_API_KEY"
],
base_url="https://api.darvareh.ir/v1",
)
model_id = os.environ.get(
"DARVAREH_MODEL",
"MODEL_ID_DARVAREH",
)
response = client.chat.completions.create(
model=model_id,
temperature=0.1,
messages=[
{
"role": "system",
"content": (
"You are a dependency upgrade planner. "
"The inventory is the only source of "
"package names and versions. Never invent "
"package versions, commands, files, APIs, "
"breaking changes, or migration steps. "
"When release notes are unavailable, say "
"that human review is required. Return "
"valid JSON only."
),
},
{
"role": "user",
"content": json.dumps(
{
"task": (
"Create a staged dependency "
"upgrade plan. Group compatible "
"low-risk updates, isolate major "
"updates, and use only verification "
"commands present in the project "
"manifest."
),
"rules": [
(
"Use only package versions "
"from dependency inventory."
),
(
"Do not place more than one "
"major update in a batch."
),
(
"Do not claim a breaking "
"change unless release notes "
"support the claim."
),
(
"Every batch must include "
"verification commands."
),
(
"Set needs_human_review to "
"true for major updates without "
"official release notes."
),
(
"Do not provide commands that "
"modify files or install "
"packages."
),
],
"requiredOutput": {
"summary": "string",
"recommended_runtime_changes": [
"string"
],
"batches": [
{
"component": "string",
"ecosystem": (
"python | npm"
),
"package": "string",
"from_version": "string",
"to_version": "string",
"change_type": "string",
"risk": (
"low | medium | high"
),
"batch": "integer",
"reason": "string",
"expected_impact": [
"string"
],
"prechecks": ["string"],
"verification_commands": [
"string"
],
"needs_release_notes": (
"boolean"
),
"needs_human_review": (
"boolean"
)
}
],
"blocked_packages": ["string"],
"assumptions": ["string"]
},
"manifest": manifest,
"inventory": inventory,
"releaseNotes": release_notes,
},
ensure_ascii=False,
),
},
],
)
content = response.choices[0].message.content
if not content:
raise RuntimeError(
"Model returned an empty response"
)
parsed = json.loads(
strip_code_fence(content)
)
plan = UpgradePlan.model_validate(
parsed
)
output_path = Path(
"upgrade-output/upgrade-plan.json"
)
output_path.write_text(
json.dumps(
plan.model_dump(),
ensure_ascii=False,
indent=2,
),
encoding="utf-8",
)
print(
f"Upgrade plan saved to {output_path}"
)
if __name__ == "__main__":
main()
اجرا:
python scripts/build_upgrade_plan.py
نمونه Upgrade Plan
{
"summary": "Patch updates are grouped first. Major upgrades are isolated and require release-note review.",
"recommended_runtime_changes": [],
"batches": [
{
"component": "backend",
"ecosystem": "python",
"package": "httpx",
"from_version": "0.28.0",
"to_version": "0.28.1",
"change_type": "patch",
"risk": "low",
"batch": 1,
"reason": "Patch-level update based on inventory.",
"expected_impact": [],
"prechecks": [
"Confirm the current test suite passes before upgrading."
],
"verification_commands": [
"python -m pip check",
"ruff check .",
"pytest",
"mypy app"
],
"needs_release_notes": false,
"needs_human_review": false
},
{
"component": "frontend",
"ecosystem": "npm",
"package": "vitest",
"from_version": "3.2.0",
"to_version": "4.0.0",
"change_type": "major",
"risk": "high",
"batch": 4,
"reason": "Major-version upgrade must be isolated.",
"expected_impact": [],
"prechecks": [
"Read the official migration guide.",
"Record current test execution behavior."
],
"verification_commands": [
"npm test",
"npm run typecheck",
"npm run build"
],
"needs_release_notes": true,
"needs_human_review": true
}
],
"blocked_packages": [],
"assumptions": []
}
مدل درباره Breaking Change خاصی ادعا نکرده است؛ چون فقط اختلاف نسخه برای اثبات تغییر API کافی نیست.
اعتبارسنجی Upgrade Plan
مدل نباید Package، Version یا Command جدید ایجاد کند.
فایل scripts/validate_plan.py:
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
def read_json(path: str) -> dict[str, Any]:
return json.loads(
Path(path).read_text(
encoding="utf-8"
)
)
def inventory_keys(
inventory: dict[str, Any],
) -> set[tuple[str, str, str, str]]:
keys = set()
for component in inventory[
"components"
]:
component_name = component[
"component"
]
for package in component["packages"]:
keys.add(
(
component_name,
package["name"].lower(),
package["current"],
package["latest"],
)
)
return keys
def allowed_commands(
manifest: dict[str, Any],
) -> dict[str, set[str]]:
result: dict[str, set[str]] = {}
for component in manifest[
"components"
]:
result[component["name"]] = set(
component
.get("commands", {})
.values()
)
return result
def main() -> None:
inventory = read_json(
"upgrade-output/dependency-inventory.json"
)
manifest = read_json(
"project-manifest.json"
)
plan = read_json(
"upgrade-output/upgrade-plan.json"
)
valid_packages = inventory_keys(
inventory
)
commands = allowed_commands(
manifest
)
errors: list[str] = []
majors_per_batch: dict[int, int] = {}
for item in plan.get("batches", []):
key = (
item["component"],
item["package"].lower(),
item["from_version"],
item["to_version"],
)
if key not in valid_packages:
errors.append(
"Unknown package or version pair: "
f"{key}"
)
component_commands = commands.get(
item["component"],
set(),
)
for command in item.get(
"verification_commands",
[],
):
if command not in component_commands:
errors.append(
"Unknown verification command "
f"for {item['component']}: "
f"{command}"
)
if item["change_type"] == "major":
batch = int(item["batch"])
majors_per_batch[batch] = (
majors_per_batch.get(batch, 0)
+ 1
)
for batch, count in (
majors_per_batch.items()
):
if count > 1:
errors.append(
f"Batch {batch} contains "
f"{count} major updates"
)
if errors:
print(
json.dumps(
{
"valid": False,
"errors": errors,
},
ensure_ascii=False,
indent=2,
)
)
raise SystemExit(1)
print(
json.dumps(
{
"valid": True,
"packageCount": len(
plan.get("batches", [])
),
},
ensure_ascii=False,
indent=2,
)
)
if __name__ == "__main__":
main()
اجرا:
python scripts/validate_plan.py
فرایند عملی ارتقای پکیج Python
پیش از ارتقا، Baseline را ثبت کنید:
cd backend
python -m pip check
ruff check .
pytest
mypy app
اگر تستها از قبل شکست میخورند، نتیجه ارتقا قابلاعتماد نخواهد بود.
برای آزمایش یک نسخه مشخص:
python -m pip install \
"fastapi==TARGET_VERSION"
سپس:
python -m pip check
ruff check .
pytest
mypy app
اگر موفق بود، نسخه را در فایل اصلی Dependency پروژه ثبت و محیط را از ابتدا بازسازی کنید.
برای requirements.txt:
fastapi==TARGET_VERSION
سپس یک محیط تازه بسازید و نصب کامل انجام دهید:
python -m venv .venv-check
source .venv-check/bin/activate
python -m pip install \
-r requirements.txt
python -m pip check
pytest
هدف این است که موفقیت به محیط قبلی و Packageهای باقیمانده وابسته نباشد.
استفاده از pip check
پس از نصب:
python -m pip check
این دستور ناسازگاریهای Dependency نصبشده را بررسی میکند.
خروجی موفق:
No broken requirements found.
موفقیت pip check به معنی درستبودن رفتار برنامه نیست. تستهای پروژه همچنان ضروریاند.
شبیهسازی نصب Python بدون تغییر محیط
نسخههای جدید pip میتوانند گزارش نصب تولید کنند:
python -m pip install \
--dry-run \
--report upgrade-output/pip-report.json \
"PACKAGE_NAME==TARGET_VERSION"
این گزارش نشان میدهد pip قصد دارد چه پکیجهایی را نصب یا تغییر دهد. طبق مستندات Installation Report در pip، ترکیب --report و --dry-run گزارش JSON از عملیات احتمالی تولید میکند.
این گزارش Lockfile نیست؛ اما برای تحلیل اثر ارتقا مفید است.
فرایند عملی ارتقای پکیج Node.js
Baseline:
cd frontend
npm ci
npm test
npm run typecheck
npm run build
مشاهده وضعیت:
npm outdated
آزمایش یک نسخه مشخص:
npm install \
PACKAGE_NAME@TARGET_VERSION \
--save-exact
سپس:
npm test
npm run typecheck
npm run build
تغییرها را بررسی کنید:
git diff -- \
frontend/package.json \
frontend/package-lock.json
در ارتقای کنترلشده باید مشخص باشد:
- چه Rangeای در
package.jsonتغییر کرده است؟ - چند پکیج در Lockfile تغییر کردهاند؟
- آیا Peer Dependency جدیدی ظاهر شده است؟
- آیا Scriptهای Build همچنان کار میکنند؟
- آیا TypeScript Error جدیدی ایجاد شده است؟
حفظ Range یا استفاده از نسخه دقیق؟
در package.json ممکن است نسخه به شکلهای مختلف ثبت شود.
نسخه دقیق:
{
"express": "5.1.0"
}
Caret:
{
"express": "^5.1.0"
}
Tilde:
{
"express": "~5.1.0"
}
معنای دقیق Range باید براساس SemVer و Package Manager بررسی شود. در یک Application، فایل Lock معمولاً نسخه واقعی نصبشده را تثبیت میکند؛ اما package.json همچنان سیاست نسخه قابلقبول را نشان میدهد.
سیاست تیم باید مشخص کند:
- Dependencyهای Runtime دقیق باشند یا Range داشته باشند؟
- Library منتشرشونده چه Rangeای اعلام کند؟
- فایل Lock همیشه Commit شود؟
- ارتقاهای Patch خودکار یا دستی باشند؟
- Major Update چگونه بررسی شود؟
مدل هوش مصنوعی نباید این سیاست را بدون اطلاعات پروژه انتخاب کند.
ارتقای Patch، Minor و Major
Patch Update
معمولاً میتوان چند Patch کمریسک را در یک Batch قرار داد:
Batch 1
- package-a: 2.4.1 → 2.4.3
- package-b: 1.8.0 → 1.8.2
- package-c: 5.1.4 → 5.1.5
بعد از Batch:
Install → Lint → Type Check → Test → Build
Minor Update
تعداد پکیجهای هر Batch را کمتر نگه دارید:
Batch 2
- package-a: 2.4.3 → 2.6.0
- package-b: 1.8.2 → 1.9.0
Major Update
هر Major Update بهتر است مستقل باشد:
Batch 3
- framework: 4.9.0 → 5.0.0
فرایند:
- Release Note رسمی را بخوانید.
- Migration Guide را استخراج کنید.
- APIهای استفادهشده را جستوجو کنید.
- تغییر را اعمال کنید.
- خطاهای Type و Import را برطرف کنید.
- تستها را اجرا کنید.
- Build نهایی را بسازید.
- Smoke Test اجرا کنید.
جستوجوی محل استفاده از API تغییرکرده
فرض کنید Migration Guide اعلام میکند متد oldMethod تغییر کرده است.
جستوجو:
rg "oldMethod" backend frontend
برای Import:
rg "from package_name import" backend
در Node.js:
rg "from ['\"]package-name['\"]" frontend
خروجی این جستوجو را میتوان همراه Release Note برای مدل ارسال کرد تا یک Migration Plan دقیقتر بسازد.
Prompt تحلیل Breaking Change
براساس Release Note رسمی و فهرست محلهای استفاده، اثر ارتقای این پکیج را تحلیل کن.
قوانین:
1. فقط تغییرهایی را بیان کن که در Release Note آمدهاند.
2. فقط فایلهایی را ذکر کن که در Usage Report وجود دارند.
3. کد پروژه را بازنویسی نکن.
4. تغییرها را به required، recommended و unrelated تقسیم کن.
5. برای هر تغییر، شواهد Release Note و محل استفاده را ارائه بده.
6. اگر ارتباط تغییر با پروژه قطعی نیست، confidence را low قرار بده.
7. فقط JSON معتبر برگردان.
خروجی:
{
"required": [
{
"change": "Replace oldMethod with newMethod",
"evidence": "The migration guide removes oldMethod.",
"affectedFiles": [
"backend/app/services/example.py"
],
"confidence": "high"
}
],
"recommended": [],
"unrelated": []
}
تحلیل خطای Dependency Resolution با هوش مصنوعی
pip و npm ممکن است بهدلیل تعارض نسخهها نصب را متوقف کنند.
ورودی مناسب:
{
"ecosystem": "python",
"runtime": "3.12",
"requestedChange": {
"package": "package-a",
"from": "2.0.0",
"to": "3.0.0"
},
"resolverError": "RESOLVER_ERROR_TEXT",
"directDependencies": [
{
"name": "package-a",
"range": "==3.0.0"
},
{
"name": "package-b",
"range": "==4.2.0"
}
]
}
از مدل بخواهید:
- زنجیره تعارض را از متن خطا استخراج کند.
- Packageهای درگیر را مشخص کند.
- از پیشنهاد نسخهای که در ورودی وجود ندارد خودداری کند.
- ابتدا دستورهای بررسی Metadata را پیشنهاد دهد.
- در صورت نبود اطلاعات، درخواست گزارش تکمیلی کند.
مدل نباید تعارض Dependency را صرفاً با حدس نسخه حل کند.
راهنمای رسمی pip توضیح میدهد که Resolver برای یافتن ترکیب سازگار ممکن است Backtracking انجام دهد و در تعارضها باید Constraintها و نسخههای موردنیاز بررسی شوند. راهنمای Dependency Resolution در pip
چه تستهایی بعد از ارتقا لازماند؟
حداقل:
نصب تازه
Can a clean environment install the project?
Dependency Check
Are installed package requirements consistent?
Lint
Did an API or import pattern change?
Type Check
Did type definitions or function signatures change?
Unit Test
Does isolated application behavior still work?
Integration Test
Do components still communicate correctly?
Build
Can the final application or package be built?
Smoke Test
Does the built application start and answer a basic request?
Regression Test
Do previously fixed scenarios still work?
ساخت ماتریس سازگاری Runtime
ممکن است نسخه جدید یک پکیج با همه نسخههای Python یا Node.js پروژه سازگار نباشد.
نمونه Matrix:
| Component | Runtime | Install | Test | Build |
|---|---|---|---|---|
| Backend | Python 3.11 | موفق | موفق | موفق |
| Backend | Python 3.12 | موفق | موفق | موفق |
| Backend | Python 3.13 | موفق | ناموفق | انجام نشد |
| Frontend | Node.js 22 | موفق | موفق | موفق |
این Matrix کمک میکند تفاوت میان مشکل Package و مشکل Runtime مشخص شود.
اجرای هفتگی Inventory در CI
فایل .github/workflows/dependency-inventory.yml:
name: Dependency inventory
on:
schedule:
- cron: "0 5 * * 1"
workflow_dispatch:
permissions:
contents: read
jobs:
inventory:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"
cache: pip
cache-dependency-path: |
backend/requirements.txt
requirements-tools.txt
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: "22"
cache: npm
cache-dependency-path: >-
frontend/package-lock.json
- name: Install Python dependencies
run: |
python -m pip install \
-r requirements-tools.txt
python -m pip install \
-r backend/requirements.txt
- name: Install Node.js dependencies
working-directory: frontend
run: npm ci
- name: Collect dependency inventory
run: |
python scripts/collect_dependencies.py
- name: Upload inventory
uses: actions/upload-artifact@v6
with:
name: dependency-inventory
path: >-
upgrade-output/dependency-inventory.json
retention-days: 14
این Workflow فقط Inventory تولید میکند و هیچ پکیجی را تغییر نمیدهد.
اجرای AI Plan در CI
اگر کلید درواره را در GitHub Actions Secret ذخیره کردهاید:
- name: Build AI upgrade plan
env:
DARVAREH_API_KEY: >-
${{ secrets.DARVAREH_API_KEY }}
DARVAREH_MODEL: MODEL_ID_DARVAREH
run: |
python scripts/build_upgrade_plan.py
python scripts/validate_plan.py
- name: Upload upgrade plan
uses: actions/upload-artifact@v6
with:
name: dependency-upgrade-plan
path: upgrade-output/upgrade-plan.json
retention-days: 14
در این معماری، مدل فقط Plan میسازد. تغییر requirements.txt، package.json یا فایل Lock خودکار نیست.
ثبت نتیجه هر Batch
فایل نتیجه:
{
"batch": 2,
"startedAt": "YYYY-MM-DDTHH:MM:SSZ",
"changes": [
{
"package": "package-a",
"from": "1.2.0",
"to": "1.3.0"
}
],
"checks": [
{
"command": "pytest",
"exitCode": 0,
"durationSeconds": 24.8
},
{
"command": "mypy app",
"exitCode": 1,
"durationSeconds": 5.3,
"summary": "Three incompatible argument type errors"
}
],
"accepted": false
}
این داده را میتوان برای تحلیل به مدل ارسال کرد:
کدام خطاها احتمالاً به ارتقای فعلی مرتبطاند؟
کدام خطاها از قبل وجود داشتهاند؟
چه اطلاعات بیشتری برای تشخیص نیاز است؟
مقایسه Baseline با نتیجه ارتقا
پیش از ارتقا:
{
"tests": {
"passed": 240,
"failed": 0
},
"typeErrors": 0,
"buildSeconds": 42.3
}
پس از ارتقا:
{
"tests": {
"passed": 237,
"failed": 3
},
"typeErrors": 5,
"buildSeconds": 55.1
}
بدون Baseline ممکن است خطاهای قدیمی به نسخه جدید نسبت داده شوند.
استفاده از چند مدل
در یک سیستم بزرگ میتوان نقشها را جدا کرد:
- مدل اقتصادی برای دستهبندی Patch و Minor
- مدل قویتر برای تحلیل Migration Guide
- مدل کدنویسی برای پیشنهاد تغییر محدود
- مدل ارزیاب برای مقایسه Patch با Release Note
اما برای اکثر پروژهها، یک مدل مناسب همراه با Validation قطعی کافی است.
فهرست مدلها و قیمت را در صفحه مدلهای درواره بررسی کنید.
اشتباهات رایج در آپدیت Dependency
ارتقای همه پکیجها در یک Commit
اگر تست شکست بخورد، مشخصکردن عامل مشکل دشوار میشود.
تغییر همزمان Runtime و Framework
ارتقای Python، Node.js و Framework اصلی را در تغییرهای جدا انجام دهید.
اعتماد به Major، Minor و Patch
SemVer راهنمای اولیه است. Release Note و تست همچنان ضروریاند.
حذف Lockfile
Lockfile بخشی از وضعیت Dependency پروژه است. تغییر آن باید بررسی و همراه فایل تعریف Dependency ثبت شود.
اجرا نکردن نصب تازه
ممکن است محیط قدیمی شامل پکیجهایی باشد که دیگر در فایلهای پروژه تعریف نشدهاند.
نادیدهگرفتن Dependencyهای غیرمستقیم
گاهی تغییر Lockfile تعداد زیادی Dependency غیرمستقیم را تغییر میدهد. Diff آن باید بررسی شود.
دریافت نسخه از مدل زبانی
نسخه Current و Latest را فقط از ابزار رسمی Package Manager یا Registry معتبر بگیرید.
استفاده از Latest در محیط واقعی
بهجای نسخه مبهم:
package@latest
نسخه بررسیشده و مشخص را ثبت کنید.
اجرای خودکار Command پیشنهادی مدل
مدل باید Plan و پیشنهاد ارائه دهد. فرمانهای تغییردهنده فقط پس از Validation و تأیید اجرا شوند.
افزودن continue-on-error به تست
هدف ارتقا سبزکردن ظاهری Pipeline نیست. خطای تست باید بررسی شود.
چکلیست ارتقای Dependency
- Baseline تستها ثبت شده است.
- نسخه Runtime مشخص است.
- Dependencyهای مستقیم شناسایی شدهاند.
- فایل Lock وجود دارد.
- Current، Wanted و Latest از Package Manager گرفته شدهاند.
- Major Updateها جدا شدهاند.
- Release Note رسمی بررسی شده است.
- Migration Guide ذخیره شده است.
- محل استفاده از APIهای تغییرکرده جستوجو شده است.
- نصب تازه آزمایش شده است.
- Dependency Check موفق است.
- Lint موفق است.
- Type Check موفق است.
- Unit Test موفق است.
- Integration Test موفق است.
- Build موفق است.
- Smoke Test موفق است.
- Diff فایل Lock بررسی شده است.
- نتیجه هر Batch ثبت شده است.
- نسخه جدید در CI آزمایش شده است.
- مدل نسخه یا Command خیالی وارد نکرده است.
انتخاب مدل مناسب برای Dependency Upgrade
مدل مناسب باید:
- Release Note فنی را درک کند.
- Python و Node.js را بشناسد.
- JSON معتبر تولید کند.
- میان اطلاعات قطعی و فرضیه تفاوت بگذارد.
- تغییر Major را بدون شواهد توضیح ندهد.
- Logهای نصب، Type Check و Test را تحلیل کند.
- از ساخت نسخه پکیج خودداری کند.
- بتواند Upgrade Plan مرحلهای تولید کند.
برای Inventory ساده، مدل سریع کافی است. برای Migration Guide طولانی، Monorepo یا تغییر Framework اصلی، مدل قویتر با Context Window بیشتر مناسب خواهد بود.
چرا API درواره برای این پروژه مناسب است؟
مدیریت Dependency باید از Script، Backend یا CI اجرا شود. با API درواره میتوانید:
- Inventory پکیجها را تحلیل کنید.
- Upgrade Plan ساختاریافته تولید کنید.
- Migration Guide را خلاصه کنید.
- خطاهای Resolver را بررسی کنید.
- خطاهای تست بعد از ارتقا را دستهبندی کنید.
- مدل مناسب هر مرحله را انتخاب کنید.
- خروجی را در Artifact یا داشبورد داخلی ذخیره کنید.
- بدون وابستگی معماری به یک مدل، مدل را تغییر دهید.
برای شروع، در درواره ثبتنام کنید، کلید API بگیرید و مدل مناسب را از صفحه مدلها انتخاب کنید.
پرسشهای متداول
چگونه پکیجهای قدیمی Python را پیدا کنیم؟
از دستور زیر استفاده کنید:
python -m pip list --outdated
برای خروجی JSON:
python -m pip list \
--outdated \
--format=json
چگونه پکیجهای قدیمی Node.js را ببینیم؟
npm outdated
برای خروجی JSON:
npm outdated --json
آیا باید همه پکیجها را به Latest ارتقا دهیم؟
خیر. نسخه جدید باید با Runtime، Dependencyهای دیگر و کد پروژه سازگار باشد. هر ارتقا باید تست شود.
آیا هوش مصنوعی میتواند Breaking Change را تشخیص دهد؟
اگر Release Note، Migration Guide و محل استفاده API در اختیار مدل باشد، میتواند تغییرهای مرتبط را تحلیل کند. اختلاف شماره نسخه بهتنهایی کافی نیست.
تفاوت Wanted و Latest در npm چیست؟
Wanted جدیدترین نسخه سازگار با Range فعلی package.json است. Latest نسخه مرتبط با Tag مربوط در Registry است و ممکن است خارج از Range پروژه باشد.
آیا Patch Update همیشه بدون مشکل است؟
خیر. Patch معمولاً کمریسکتر است، اما همچنان باید نصب، تست و Build شود.
آیا فایل Lock را باید Commit کنیم؟
برای Applicationها معمولاً فایل Lock بخشی از وضعیت تکرارپذیر Dependencyها است. سیاست دقیق به نوع پروژه و Package Manager بستگی دارد.
چگونه Dependency Conflict را حل کنیم؟
متن کامل Resolver، Dependencyهای مستقیم، Range نسخهها و Runtime را بررسی کنید. مدل میتواند زنجیره تعارض را استخراج کند، اما نسخه جایگزین باید از Registry و Metadata واقعی تأیید شود.
آیا درواره خودش پکیجها را آپدیت میکند؟
درواره دسترسی API به مدلهای هوش مصنوعی را فراهم میکند. جمعآوری نسخهها، اعمال تغییر، اجرای تست و انتشار برعهده ابزارها و Pipeline پروژه شما است.
Model ID درواره را از کجا بگیریم؟
Model ID، مشخصات و قیمت بهروز مدلها را در صفحه مدلهای درواره ببینید.
جمعبندی
آپدیت Dependency با هوش مصنوعی نباید به معنی اجرای خودکار دستور Upgrade برای همه پکیجها باشد.
یک سیستم قابلاعتماد باید:
- نسخهها را از Package Manager دریافت کند.
- Dependencyهای مستقیم را مشخص کند.
- Major، Minor و Patch را دستهبندی کند.
- Release Note رسمی را وارد تحلیل کند.
- Upgrade Plan را بهصورت JSON دریافت کند.
- Packageها و Versionها را با Inventory تطبیق دهد.
- Commandهای مدل را با Manifest مقایسه کند.
- تغییرها را در Batchهای کوچک اجرا کند.
- بعد از هر Batch تست و Build کامل انجام دهد.
- نتیجه را با Baseline مقایسه کند.
این روش باعث میشود Dependencyهای پروژه بهجای یک تغییر بزرگ و پرابهام، بهصورت مرحلهای و قابلاندازهگیری ارتقا پیدا کنند.
برای ساخت Dependency Upgrade Assistant، در درواره ثبتنام کنید، کلید API بگیرید و مدل مناسب تحلیل کد و Release Note را از صفحه مدلها انتخاب کنید.
مقالات مرتبط
- تولید تست نرمافزار با هوش مصنوعی
- دیباگ کد و رفع خطا با هوش مصنوعی
- تحلیل Log با هوش مصنوعی
- بازنویسی و Refactor کدهای قدیمی با هوش مصنوعی
- بازبینی کد و Pull Request با هوش مصنوعی
- تولید مستندات کد و API با هوش مصنوعی
- ساخت API هوش مصنوعی آماده Production
- ارزیابی مدلهای هوش مصنوعی و Evals
- اتصال API هوش مصنوعی به اپلیکیشن
- راهنمای انتخاب بهترین API هوش مصنوعی
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.