Cmc Inspect
No description available
Ask AI about Cmc Inspect
Powered by Claude · Grounded in docs
I know everything about Cmc Inspect. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
CMC蜯件å åæå·¥å ·
çšäº CI/CD 莚ééšçŠçèªåšå蜯件å åæä»£ç
ð ç®åœ
- é¡¹ç®æŠè¿°
- æ žå¿åèœ
- é¡¹ç®æ¶æ
- å¿«éåŒå§
- å®è£ æå
- äœ¿çšæ¹æ³
- APIææ¡£
- é 眮诎æ
- åŒåæå
- 瀺äŸä»£ç
- æ éæé€
ð¯ é¡¹ç®æŠè¿°
CMC蜯件å åæå·¥å ·æ¯äžäžªçšäºCI/CD莚ééšçŠçèªåšå代ççšåºïŒäžéšè®Ÿè®¡çšäºåšBçæ¬è¢«æå䞺Cçæ¬ä¹åè¿è¡èŽšéæ£æ¥ãè¯¥å·¥å ·èœå€èªåšåæèœ¯ä»¶å çç®åœç»æå蜯件å 倧å°ååïŒåæ©åç°æå éè¯¯å¹¶é»æ¢æé®é¢çæå»ºç»§ç»è¿è¡ã
æ žå¿ä»·åŒ
- ð èªåšåèŽšéæ£æ¥: æ é人工干é¢ç蜯件å 莚éåæ
- ðš æ©æé®é¢åç°: åšCI/CDæµæ°Žçº¿æ©æåç°æå é®é¢
- ð ç»æåæ¥å: èŸåºæ åJSONæ ŒåŒæ¥åïŒæäºéæ
- ð§ çµæŽ»é 眮: æ¯æèªå®ä¹éåŒåæ£æ¥è§å
- ð å€ç§æ¥å£: CLIå·¥å ·ãMCPæå¡åšãPython API
åºçšåºæ¯
- CI/CD莚ééšçŠ: äœäžºèªåšåèŽšéæ£æ¥æ¥éª€
- çæ¬ååžéªè¯: ç¡®ä¿ååžå 笊åèŽšéæ å
- æå éè¯¯æ£æµ: åæ©åç°æä»¶çŒºå€±æåäœ
- åè§æ§æ£æ¥: ç¡®ä¿ååžå äžå 嫿ææä»¶
ð æ žå¿åèœ
1. 蜯件å 倧å°åæ
- â 计ç®èœ¯ä»¶å æ»å€§å°
- â äžåäžçæ¬æ¯èŸå€§å°åå
- â æ¯æå¯é 眮éåŒæ£æ¥ïŒé»è®€Â±15%ïŒ
- â æäŸè¯Šç»ç倧å°ååæè¿°
2. ç®åœç»æåæ
- â 对æ¯åœåçæ¬äžåäžçæ¬çæä»¶/ç®åœæ
- â è¯å«æ°å¢çæä»¶åç®åœ
- â è¯å«å é€çæä»¶åç®åœ
- â æ£æµçŠæ¢æä»¶æš¡åŒïŒ*.log, *.tmp, .idea/çïŒ
3. å€ç§èŸåºæ ŒåŒ
- â ç»æåJSONæ¥åïŒç¬ŠåCI/CDéæèŠæ±ïŒ
- â 人类å¯è¯»çææ¬æèŠ
- â 诊ç»çåææ¥å¿
4. å€ç§æ¥å£æ¯æ
- ð¥ïž åœä»€è¡å·¥å ·(CLI): çŽæ¥åœä»€è¡äœ¿çš
- ð MCPæå¡åš: 䞺AI代çæäŸåæèœå
- ð§ Python API: çŒçšæ¥å£æ¯æ
ðïž é¡¹ç®æ¶æ
æŽäœæ¶æåŸ
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â CMC蜯件å
åæå·¥å
· â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â çšæ·æ¥å£å± â
â âââââââââââââââ âââââââââââââââ âââââââââââââââââââââââ â
â â CLIå·¥å
· â â MCPæå¡åš â â Python API â â
â â â â â â â â
â â cmc-inspect â âFastMCP-basedâ â çŒçšæ¥å£ â â
â âââââââââââââââ âââââââââââââââ âââââââââââââââââââââââ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â äžå¡é»èŸå± â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â åæåšæš¡å â â
â â âââââââââââââââ âââââââââââââââ âââââââââââââââââ â â
â â â SizeAnalyzerâ âStructAnalyz.â â BaseAnalyzer â â â
â â â 倧å°åæ â â ç»æåæ â â åæåšåºç±» â â â
â â âââââââââââââââ âââââââââââââââ âââââââââââââââââ â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â æ°æ®æš¡åå± â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â Pydanticæš¡å â â
â â âââââââââââââââ âââââââââââââââ âââââââââââââââââ â â
â â âPackageInfo â âAnalysisResultâ â é
眮暡å â â â
â â â å
ä¿¡æ¯ â â åæç»æ â â â â â
â â âââââââââââââââ âââââââââââââââ âââââââââââââââââ â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â å·¥å
·æ¯æå± â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â å·¥å
·æš¡å â â
â â âââââââââââââââ âââââââââââââââ âââââââââââââââââ â â
â â âPackageExtr. â â FileUtils â â FormatUtils â â â
â â â å
è§£å â â æä»¶å·¥å
· â â æ ŒåŒåå·¥å
· â â â
â â âââââââââââââââ âââââââââââââââ âââââââââââââââââ â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ç®åœç»æ
cmc-inspect/
âââ src/cmc_inspect/ # äž»èŠæºç ç®åœ
â âââ __init__.py # å
åå§åïŒå¯Œåºæ žå¿ç»ä»¶
â âââ config.py # é
çœ®ç®¡çæš¡å
â âââ cli.py # åœä»€è¡æ¥å£
â âââ server.py # MCPæå¡åšå
¥å£
â â
â âââ models/ # æ°æ®æš¡å
â â âââ __init__.py
â â âââ package_info.py # 蜯件å
ä¿¡æ¯æš¡å
â â âââ analysis_result.py # åæç»ææš¡å
â â
â âââ analyzers/ # åæåšæš¡å
â â âââ __init__.py
â â âââ base.py # åæåšåºç±»
â â âââ size.py # 倧å°åæåš
â â âââ structure.py # ç»æåæåš
â â
â âââ tools/ # å·¥å
·æš¡å
â â âââ __init__.py
â â âââ package_extractor.py # 蜯件å
è§£åå·¥å
·
â â âââ file_utils.py # æä»¶æäœå·¥å
·
â â âââ format_utils.py # æ ŒåŒåå·¥å
·
â â
â âââ mcp_tools/ # MCPå·¥å
·å®ä¹
â âââ __init__.py
â âââ analyze_package.py # åæå·¥å
·
â âââ compare_packages.py # æ¯èŸå·¥å
·
â
âââ tests/ # æµè¯ç®åœ
â âââ __init__.py
â âââ test_basic.py # åºç¡åèœæµè¯
â âââ test_analyzers/ # åæåšæµè¯
â âââ test_tools/ # å·¥å
·æµè¯
â âââ test_mcp_tools/ # MCPå·¥å
·æµè¯
â
âââ examples/ # 瀺äŸåææ¡£
â âââ config.json # 瀺äŸé
眮æä»¶
â âââ usage_examples.md # 䜿çšç€ºäŸææ¡£
â âââ demo.py # åèœæŒç€ºèæ¬
â âââ sample_packages/ # 瀺äŸèœ¯ä»¶å
â âââ create_samples.py # å建瀺äŸå
èæ¬
â âââ example-package-v1.0.zip
â âââ example-package-v2.0.zip
â âââ example-package-v2.1.zip
â
âââ pyproject.toml # 项ç®é
眮
âââ README.md # é¡¹ç®ææ¡£
âââ PROJECT_SUMMARY.md # é¡¹ç®æ»ç»
âââ .gitignore # Gitå¿œç¥æä»¶
ð ïž æš¡ååèœè¯Šè§£
1. æ°æ®æš¡åå± (models/)
PackageInfo - 蜯件å
ä¿¡æ¯æš¡å
class PackageInfo(BaseModel):
name: str # 蜯件å
åç§°
version: str # çæ¬å·
file_path: Path # æä»¶è·¯åŸ
size_bytes: int # æä»¶å€§å°ïŒåèïŒ
extracted_path: Optional[Path] # è§£åè·¯åŸ
created_at: datetime # å建æ¶éŽ
AnalysisResult - åæç»ææš¡å
class AnalysisResult(BaseModel):
analysis_info: AnalysisInfo # åæå
ä¿¡æ¯
overall_status: StatusType # æ»äœç¶æ
size_check: SizeCheckResult # 倧尿£æ¥ç»æ
structure_check: StructureCheckResult # ç»ææ£æ¥ç»æ
summary: str # 人类å¯è¯»æèŠ
ç¶æç±»åå®ä¹
StatusType = Literal["PASSED", "WARNING", "FAILED"]
2. åæåšå± (analyzers/)
BaseAnalyzer - åæåšåºç±»
- æäŸéçšçåæåšæ¥å£
- å®ä¹æœè±¡æ¹æ³ïŒ
analyze()åcompare() - å å«é 眮管çåéªè¯åèœ
SizeAnalyzer - 倧å°åæåš
æ žå¿åèœ:
- 计ç®èœ¯ä»¶å 倧å°
- æ¯èŸçæ¬éŽå€§å°åå
- æ ¹æ®éåŒç¡®å®ç¶æ
- æ ŒåŒå倧尿Ÿç€º
å ³é®æ¹æ³:
async def analyze(package_info: PackageInfo) -> Dict[str, Any]
async def compare(current: PackageInfo, previous: PackageInfo) -> Dict[str, Any]
StructureAnalyzer - ç»æåæåš
æ žå¿åèœ:
- åæç®åœç»æ
- æ¯èŸæä»¶åå
- æ£æµçŠæ¢æä»¶
- çæå忥å
å ³é®æ¹æ³:
async def analyze(package_info: PackageInfo) -> DirectoryStructure
async def compare(current: PackageInfo, previous: PackageInfo) -> Dict[str, Any]
3. å·¥å
·æ¯æå± (tools/)
PackageExtractor - 蜯件å
è§£åå·¥å
·
æ¯ææ ŒåŒ:
.zip- ZIPå猩å.tar,.tar.gz,.tgz- TARå猩å.tar.bz2,.tar.xz- å ¶ä»TARåç§.7z- 7-Zipå猩å ïŒéèŠpy7zrïŒ
æ žå¿åèœ:
- å®å šè§£åïŒé²æ¢è·¯åŸéåæ»å»ïŒ
- åŒæ¥æäœæ¯æ
- é误å€çåæž ç
- æ ŒåŒèªå𿣿µ
file_utils.py - æä»¶æäœå·¥å
·
æäŸåèœ:
- ç®åœå€§å°è®¡ç®
- æä»¶å衚è·å
- æš¡åŒå¹é æ¥æŸ
- å®å šç®åœæäœ
format_utils.py - æ ŒåŒåå·¥å
·
æäŸåèœ:
- æä»¶å€§å°æ ŒåŒåïŒB, KB, MB, GB, TBïŒ
- çŸåæ¯ååæ ŒåŒå
- æ¶éŽæ³æ ŒåŒå
- ç¶æä¿¡æ¯æ ŒåŒå
4. MCPå·¥å
·å± (mcp_tools/)
åæå·¥å
· (analyze_package.py)
analyze_package- 宿Žèœ¯ä»¶å åæextract_package- 蜯件å è§£åget_package_info- è·åå åºæ¬ä¿¡æ¯
æ¯èŸå·¥å
· (compare_packages.py)
compare_packages- äž»èŠæ¯èŸå·¥å ·ïŒèŸåºæ åJSONïŒcompare_packages_detailed- è¯Šç»æ¯èŸåæquick_size_compare- å¿«é倧尿¯èŸ
5. æ¥å£å±
CLIæ¥å£ (cli.py)
ååœä»€:
analyze- åæå䞪蜯件åcompare- æ¯èŸäž€äžªèœ¯ä»¶å çæ¬server- å¯åšMCPæå¡åš
MCPæå¡åš (server.py)
- FastMCPæå¡åšå®ç°
- å·¥å ·æ³šåå管ç
- å¥åº·æ£æ¥åèœ
- é 眮管ç
ð å¿«éåŒå§
åææ¡ä»¶
- Python 3.13+
- uvå 管çåš
1. å é项ç®
git clone <your-repo-url>
cd cmc-inspect
2. å®è£ äŸèµ
# å®è£
åºç¡äŸèµ
uv sync
# å®è£
åŒåäŸèµ
uv sync --extra dev
3. è¿è¡æµè¯
uv run pytest tests/ -v
4. å建瀺äŸå
python examples/sample_packages/create_samples.py
5. å¿«éäœéª
# åæå䞪蜯件å
uv run cmc-inspect analyze examples/sample_packages/example-package-v1.0.zip
# æ¯èŸäž€äžªçæ¬
uv run cmc-inspect compare \
examples/sample_packages/example-package-v2.0.zip \
examples/sample_packages/example-package-v1.0.zip \
--current-version "B-103" \
--previous-version "B-102"
ðŠ å®è£ æå
æ¹åŒ1: 䜿çšuvïŒæšèïŒ
# åŒåæš¡åŒå®è£
uv pip install -e .
# ç产暡åŒå®è£
uv pip install .
æ¹åŒ2: 䜿çšpip
# 仿ºç å®è£
pip install -e .
# æè
çŽæ¥å®è£
pip install .
äŸèµè¯Žæ
æ žå¿äŸèµ:
fastmcp>=2.11.1- MCPæå¡å𿡿¶pydantic>=2.11.7- æ°æ®éªè¯ååºååtyping-extensions>=4.14.1- ç±»åæ¯æpathlib2>=2.3.7- è·¯åŸæäœå¢åŒº
åŒåäŸèµ:
pytest>=7.4.0- æµè¯æ¡æ¶pytest-asyncio>=0.21.0- åŒæ¥æµè¯æ¯æblack>=23.0.0- ä»£ç æ ŒåŒåruff>=0.1.0- ä»£ç æ£æ¥mypy>=1.7.0- ç±»åæ£æ¥
ð¥ïž äœ¿çšæ¹æ³
CLIåœä»€è¡å·¥å ·
åæå䞪蜯件å
# åºæ¬çšæ³
cmc-inspect analyze package.zip
# æå®çæ¬ååæ°
cmc-inspect analyze package.zip \
--version "B-103" \
--package-name "my-app" \
--temp-dir "./temp" \
--size-threshold 20.0
æ¯èŸäž€äžªèœ¯ä»¶å
# åºæ¬æ¯èŸ
cmc-inspect compare current.zip previous.zip
# 宿Žåæ°
cmc-inspect compare current.zip previous.zip \
--current-version "B-103" \
--previous-version "B-102" \
--size-threshold 15.0 \
--temp-dir "./temp"
å¯åšMCPæå¡åš
# 䜿çšé»è®€é
眮
cmc-inspect-server
# 䜿çšèªå®ä¹é
眮
cmc-inspect-server --config config.json --verbose
MCPæå¡åšäœ¿çš
1. å¯åšæå¡åš
uv run cmc-inspect-server
2. è¿æ¥å°æå¡åš
æå¡åšå°æäŸä»¥äžMCPå·¥å ·ïŒ
analyze_package- åæèœ¯ä»¶åcompare_packages- æ¯èŸèœ¯ä»¶å ïŒäž»èŠå·¥å ·ïŒextract_package- è§£å蜯件åget_package_info- è·åå ä¿¡æ¯compare_packages_detailed- è¯Šç»æ¯èŸquick_size_compare- å¿«é倧尿¯èŸ
Python API䜿çš
åºæ¬åæç€ºäŸ
import asyncio
from pathlib import Path
from cmc_inspect import SizeAnalyzer, StructureAnalyzer, PackageInfo
from cmc_inspect.tools import PackageExtractor
async def analyze_package_example():
# å建蜯件å
ä¿¡æ¯
package_file = Path("example.zip")
package_info = PackageInfo(
name="example",
version="1.0.0",
file_path=package_file,
size_bytes=package_file.stat().st_size
)
# è§£å蜯件å
extractor = PackageExtractor()
extracted_path = await extractor.extract(str(package_file))
package_info.extracted_path = Path(extracted_path)
# æ§è¡åæ
size_analyzer = SizeAnalyzer()
structure_analyzer = StructureAnalyzer()
size_result = await size_analyzer.analyze(package_info)
structure_result = await structure_analyzer.analyze(package_info)
print(f"蜯件å
倧å°: {size_result['formatted_size']}")
print(f"æä»¶æ°é: {structure_result.total_files}")
# è¿è¡ç€ºäŸ
asyncio.run(analyze_package_example())
æ¯èŸåæç€ºäŸ
import asyncio
from cmc_inspect import SizeAnalyzer, StructureAnalyzer
from cmc_inspect.models import AnalysisResult, AnalysisInfo, SizeCheckResult, StructureCheckResult
async def compare_packages_example():
# åå€å
ä¿¡æ¯ïŒçç¥å建代ç ïŒ
current_info = create_package_info("current.zip", "2.0.0")
previous_info = create_package_info("previous.zip", "1.0.0")
# æ§è¡æ¯èŸ
size_analyzer = SizeAnalyzer()
structure_analyzer = StructureAnalyzer()
size_comparison = await size_analyzer.compare(current_info, previous_info)
structure_comparison = await structure_analyzer.compare(current_info, previous_info)
# æå»ºå®æŽç»æ
result = AnalysisResult(
analysis_info=AnalysisInfo(
current_version="2.0.0",
previous_version="1.0.0"
),
overall_status="PASSED", # æ ¹æ®æ£æ¥ç»æç¡®å®
size_check=SizeCheckResult(**size_comparison),
structure_check=StructureCheckResult(**structure_comparison),
summary="" # èªåšçæ
)
# èŸåºJSONæ¥å
print(result.to_json_output())
asyncio.run(compare_packages_example())
ð APIææ¡£
æ žå¿ç±»åæ¹æ³
PackageInfo
class PackageInfo(BaseModel):
"""蜯件å
ä¿¡æ¯æš¡å"""
name: str # 蜯件å
åç§°
version: str # çæ¬å·
file_path: Path # 蜯件å
æä»¶è·¯åŸ
size_bytes: int # æä»¶å€§å°ïŒåèïŒ
extracted_path: Optional[Path] # è§£ååç®åœè·¯åŸ
created_at: datetime # å建æ¶éŽ
AnalysisResult
class AnalysisResult(BaseModel):
"""åæç»ææš¡å"""
analysis_info: AnalysisInfo # åæä¿¡æ¯
overall_status: StatusType # æ»äœç¶æ
size_check: SizeCheckResult # 倧尿£æ¥ç»æ
structure_check: StructureCheckResult # ç»ææ£æ¥ç»æ
summary: str # 人类å¯è¯»æèŠ
def to_json_output(self) -> str:
"""蜬æ¢äžºJSONèŸåºæ ŒåŒ"""
@classmethod
def create_failed_result(cls, error_message: str) -> "AnalysisResult":
"""åå»ºå€±èŽ¥ç»æ"""
SizeAnalyzer
class SizeAnalyzer(BaseAnalyzer):
"""蜯件å
倧å°åæåš"""
async def analyze(self, package_info: PackageInfo) -> Dict[str, Any]:
"""åæå䞪蜯件å
ç倧å°"""
async def compare(self, current: PackageInfo, previous: PackageInfo) -> Dict[str, Any]:
"""æ¯èŸäž€äžªçæ¬ç蜯件å
倧å°"""
StructureAnalyzer
class StructureAnalyzer(BaseAnalyzer):
"""ç®åœç»æåæåš"""
async def analyze(self, package_info: PackageInfo) -> DirectoryStructure:
"""åæèœ¯ä»¶å
çç®åœç»æ"""
async def compare(self, current: PackageInfo, previous: PackageInfo) -> Dict[str, Any]:
"""æ¯èŸäž€äžªçæ¬çç®åœç»æ"""
PackageExtractor
class PackageExtractor:
"""蜯件å
è§£ååš"""
async def extract(self, package_path: str, extract_dir: Optional[str] = None) -> str:
"""è§£å蜯件å
å°æå®ç®åœ"""
@staticmethod
def get_supported_formats() -> List[str]:
"""è·åæ¯æçæä»¶æ ŒåŒå衚"""
@staticmethod
def is_supported_format(file_path: str) -> bool:
"""æ£æ¥æä»¶æ ŒåŒæ¯åŠåæ¯æ"""
JSONèŸåºæ ŒåŒ
æ ååææ¥å
{
"analysis_info": {
"current_version": "B-103",
"previous_version": "B-102",
"timestamp_utc": "2025-01-27T12:00:00Z"
},
"overall_status": "FAILED",
"size_check": {
"status": "FAILED",
"current_size_bytes": 15000000,
"previous_size_bytes": 10000000,
"change_percentage": 50.0,
"threshold_percentage": 15.0
},
"structure_check": {
"status": "WARNING",
"added_files": ["/config/new_feature.xml", "/lib/extra_lib.jar"],
"deleted_files": ["/config/old_setting.properties"],
"forbidden_files": []
},
"summary": "æ£æ¥å€±èŽ¥ã蜯件å
倧å°å¢å 50%ïŒè¶
åº15%éåŒïŒãç®åœç»æåæŽïŒæ°å¢2䞪æä»¶ïŒå é€1䞪æä»¶ã"
}
ç¶æè¯Žæ
PASSED: æææ£æ¥éè¿WARNING: æååäœåšå¯æ¥åèåŽåFAILED: æ£æ¥å€±èŽ¥ïŒäžå»ºè®®ç»§ç»
âïž é 眮诎æ
é 眮æä»¶æ ŒåŒ (JSON)
{
"analysis": {
"size_threshold_percentage": 15.0,
"size_warning_threshold_percentage": 12.0,
"forbidden_patterns": [
"*.log", "*.tmp", "*.temp",
".idea/", ".vscode/", ".git/",
"__pycache__/", "*.pyc", "*.pyo",
".DS_Store", "Thumbs.db",
"*.swp", "*.swo", "*~"
],
"temp_dir": "./temp/cmc_extract",
"cleanup_temp_files": true,
"verbose": false,
"max_listed_files": 50
},
"mcp": {
"server_name": "CMC蜯件å
åæä»£ç",
"server_version": "1.0.0",
"enable_analyze_tool": true,
"enable_compare_tool": true,
"enable_extract_tool": true
}
}
é 眮项诎æ
åæé
眮 (analysis)
size_threshold_percentage: 倧å°åå倱莥éåŒïŒé»è®€15%ïŒsize_warning_threshold_percentage: 倧å°ååèŠåéåŒïŒé»è®€12%ïŒforbidden_patterns: çŠæ¢çæä»¶æš¡åŒå衚temp_dir: 䞎æ¶è§£åç®åœcleanup_temp_files: æ¯åŠæž çäžŽæ¶æä»¶verbose: æ¯åŠè¯Šç»èŸåºmax_listed_files: æå€ååºçæä»¶æ°é
MCPé
眮 (mcp)
server_name: MCPæå¡åšåç§°server_version: æå¡åšçæ¬enable_analyze_tool: æ¯åŠå¯çšåæå·¥å ·enable_compare_tool: æ¯åŠå¯çšæ¯èŸå·¥å ·enable_extract_tool: æ¯åŠå¯çšè§£åå·¥å ·
ç¯å¢å鿝æ
# 讟眮䞎æ¶ç®åœ
export CMC_TEMP_DIR="/custom/temp/dir"
# 讟眮倧å°éåŒ
export CMC_SIZE_THRESHOLD="20.0"
# å¯çšè¯Šç»èŸåº
export CMC_VERBOSE="true"
ð§ åŒåæå
åŒåç¯å¢è®Ÿçœ®
1. å éåå®è£
git clone <repo-url>
cd cmc-inspect
uv sync --extra dev
2. åŒåå·¥å ·é 眮
# ä»£ç æ ŒåŒå
uv run black src/ tests/
# ä»£ç æ£æ¥
uv run ruff check src/ tests/
# ç±»åæ£æ¥
uv run mypy src/
3. è¿è¡æµè¯
# è¿è¡æææµè¯
uv run pytest tests/ -v
# è¿è¡ç¹å®æµè¯
uv run pytest tests/test_basic.py -v
# æµè¯èŠçç
uv run pytest tests/ --cov=cmc_inspect
æ·»å æ°çåæåš
1. å建åæåšç±»
# src/cmc_inspect/analyzers/my_analyzer.py
from .base import BaseAnalyzer
from ..models.package_info import PackageInfo
class MyAnalyzer(BaseAnalyzer):
"""èªå®ä¹åæåš"""
async def analyze(self, package_info: PackageInfo) -> Dict[str, Any]:
"""å®ç°åæé»èŸ"""
# äœ çåæä»£ç
return {"status": "PASSED", "details": "..."}
async def compare(self, current: PackageInfo, previous: PackageInfo) -> Dict[str, Any]:
"""å®ç°æ¯èŸé»èŸ"""
# äœ çæ¯èŸä»£ç
return {"status": "PASSED", "changes": "..."}
2. 泚ååæåš
# src/cmc_inspect/analyzers/__init__.py
from .my_analyzer import MyAnalyzer
__all__ = [..., "MyAnalyzer"]
3. æ·»å MCPå·¥å ·
# src/cmc_inspect/mcp_tools/my_tools.py
from fastmcp import FastMCP
from ..analyzers.my_analyzer import MyAnalyzer
def setup_my_tools(mcp: FastMCP, config: AnalysisConfig):
@mcp.tool()
async def my_analysis_tool(package_path: str) -> Dict[str, Any]:
"""èªå®ä¹åæå·¥å
·"""
analyzer = MyAnalyzer(config)
# å®ç°å·¥å
·é»èŸ
return result
æ·»å æ°çæ°æ®æš¡å
1. å®ä¹æš¡å
# src/cmc_inspect/models/my_model.py
from pydantic import BaseModel, Field
from typing import List, Optional
class MyModel(BaseModel):
"""èªå®ä¹æ°æ®æš¡å"""
name: str = Field(..., description="åç§°")
items: List[str] = Field(default_factory=list, description="项ç®å衚")
optional_field: Optional[str] = Field(None, description="å¯éåæ®µ")
def custom_method(self) -> str:
"""èªå®ä¹æ¹æ³"""
return f"Model: {self.name}"
2. å¯Œåºæš¡å
# src/cmc_inspect/models/__init__.py
from .my_model import MyModel
__all__ = [..., "MyModel"]
æµè¯çŒå
1. åå æµè¯
# tests/test_my_analyzer.py
import pytest
from cmc_inspect.analyzers.my_analyzer import MyAnalyzer
from cmc_inspect.models.package_info import PackageInfo
class TestMyAnalyzer:
def test_analyzer_creation(self):
"""æµè¯åæåšå建"""
analyzer = MyAnalyzer()
assert analyzer is not None
@pytest.mark.asyncio
async def test_analyze_function(self):
"""æµè¯åæåèœ"""
analyzer = MyAnalyzer()
# å建æµè¯æ°æ®
package_info = create_test_package_info()
result = await analyzer.analyze(package_info)
assert result["status"] in ["PASSED", "WARNING", "FAILED"]
2. éææµè¯
# tests/test_integration.py
import pytest
from cmc_inspect import create_mcp_server
@pytest.mark.asyncio
async def test_mcp_server_integration():
"""æµè¯MCPæå¡åšéæ"""
server = create_mcp_server()
tools = server.list_tools()
assert "analyze_package" in tools
assert "compare_packages" in tools
æ§èœäŒå
1. åŒæ¥æäœ
# 䜿çšåŒæ¥æäœå€çI/Oå¯éä»»å¡
async def process_large_package(package_path: str):
async with aiofiles.open(package_path, 'rb') as f:
content = await f.read()
# å€çå
容
2. å å管ç
# ååå€ç倧æä»¶
def process_large_file_in_chunks(file_path: Path, chunk_size: int = 8192):
with open(file_path, 'rb') as f:
while chunk := f.read(chunk_size):
yield chunk
3. çŒåäŒå
from functools import lru_cache
@lru_cache(maxsize=128)
def expensive_calculation(package_hash: str) -> Dict[str, Any]:
"""çŒåæè޵ç计ç®ç»æ"""
# æ§è¡è®¡ç®
return result
ð¡ 瀺äŸä»£ç
宿Žåææµçšç€ºäŸ
#!/usr/bin/env python3
"""宿Žç蜯件å
åææµçšç€ºäŸ"""
import asyncio
import json
from pathlib import Path
from cmc_inspect import (
PackageInfo, AnalysisResult, AnalysisInfo,
SizeAnalyzer, StructureAnalyzer, PackageExtractor,
SizeCheckResult, StructureCheckResult
)
async def full_analysis_example():
"""宿Žåææµçšç€ºäŸ"""
# 1. åå€èœ¯ä»¶å
ä¿¡æ¯
current_path = Path("current-package.zip")
previous_path = Path("previous-package.zip")
current_info = PackageInfo(
name="my-app",
version="B-103",
file_path=current_path,
size_bytes=current_path.stat().st_size
)
previous_info = PackageInfo(
name="my-app",
version="B-102",
file_path=previous_path,
size_bytes=previous_path.stat().st_size
)
# 2. è§£å蜯件å
extractor = PackageExtractor()
current_extracted = await extractor.extract(str(current_path))
previous_extracted = await extractor.extract(str(previous_path))
current_info.extracted_path = Path(current_extracted)
previous_info.extracted_path = Path(previous_extracted)
# 3. æ§è¡åæ
size_analyzer = SizeAnalyzer()
structure_analyzer = StructureAnalyzer()
# 倧尿¯èŸ
size_comparison = await size_analyzer.compare(current_info, previous_info)
print("倧尿¯èŸç»æ:", size_comparison)
# ç»ææ¯èŸ
structure_comparison = await structure_analyzer.compare(current_info, previous_info)
print("ç»ææ¯èŸç»æ:", structure_comparison)
# 4. æå»ºå®æŽç»æ
from datetime import datetime
result = AnalysisResult(
analysis_info=AnalysisInfo(
current_version=current_info.version,
previous_version=previous_info.version,
timestamp_utc=datetime.utcnow()
),
overall_status="PASSED", # 䞎æ¶åŒ
size_check=SizeCheckResult(**size_comparison),
structure_check=StructureCheckResult(**structure_comparison),
summary="" # 䞎æ¶åŒ
)
# èªåšç¡®å®ç¶æåæèŠ
result.overall_status = result.determine_overall_status()
result.summary = result.generate_summary()
# 5. èŸåºç»æ
print("\n=== 宿Žåææ¥å ===")
print(result.to_json_output())
# 6. æž
çäžŽæ¶æä»¶
PackageExtractor.cleanup_directory(current_extracted)
PackageExtractor.cleanup_directory(previous_extracted)
return result
# è¿è¡ç€ºäŸ
if __name__ == "__main__":
asyncio.run(full_analysis_example())
MCP客æ·ç«¯äœ¿çšç€ºäŸ
#!/usr/bin/env python3
"""MCP客æ·ç«¯äœ¿çšç€ºäŸ"""
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def mcp_client_example():
"""MCP客æ·ç«¯äœ¿çšç€ºäŸ"""
# å¯åšMCPæå¡åš
server_params = StdioServerParameters(
command="uv",
args=["run", "cmc-inspect-server"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# åå§å
await session.initialize()
# è·åå¯çšå·¥å
·
tools = await session.list_tools()
print("å¯çšå·¥å
·:", [tool.name for tool in tools.tools])
# è°çšåæå·¥å
·
result = await session.call_tool(
"analyze_package",
arguments={
"package_path": "examples/sample_packages/example-package-v1.0.zip",
"package_version": "B-102"
}
)
print("åæç»æ:", result.content)
# è°çšæ¯èŸå·¥å
·
comparison = await session.call_tool(
"compare_packages",
arguments={
"current_package_path": "examples/sample_packages/example-package-v2.0.zip",
"previous_package_path": "examples/sample_packages/example-package-v1.0.zip",
"current_version": "B-103",
"previous_version": "B-102"
}
)
print("æ¯èŸç»æ:", comparison.content)
# è¿è¡ç€ºäŸ
if __name__ == "__main__":
asyncio.run(mcp_client_example())
èªå®ä¹é 眮瀺äŸ
#!/usr/bin/env python3
"""èªå®ä¹é
眮䜿çšç€ºäŸ"""
from cmc_inspect import AnalysisConfig, MCPServerConfig, SizeAnalyzer
# å建èªå®ä¹åæé
眮
custom_config = AnalysisConfig(
size_threshold_percentage=20.0, # æŽå®œæŸç倧å°éåŒ
size_warning_threshold_percentage=15.0,
forbidden_patterns=[
"*.log", "*.tmp", "*.cache",
"node_modules/", ".git/",
"*.secret", "*.key"
],
temp_dir="./custom_temp",
cleanup_temp_files=True,
verbose=True,
max_listed_files=100
)
# å建èªå®ä¹MCPé
眮
mcp_config = MCPServerConfig(
server_name="èªå®ä¹CMCåæåš",
server_version="2.0.0",
enable_analyze_tool=True,
enable_compare_tool=True,
enable_extract_tool=False # çŠçšè§£åå·¥å
·
)
# 䜿çšèªå®ä¹é
眮
async def custom_config_example():
"""èªå®ä¹é
眮䜿çšç€ºäŸ"""
# 䜿çšèªå®ä¹é
眮å建åæåš
analyzer = SizeAnalyzer(custom_config)
# å建èªå®ä¹MCPæå¡åš
from cmc_inspect import create_mcp_server
server = create_mcp_server(custom_config, mcp_config)
print("èªå®ä¹æå¡åšå·²å建")
print(f"æå¡åšåç§°: {mcp_config.server_name}")
print(f"é
眮ç倧å°éåŒ: {custom_config.size_threshold_percentage}%")
# 仿件å 蜜é
眮
def load_config_from_file(config_path: str):
"""仿件å 蜜é
眮"""
import json
from pathlib import Path
config_file = Path(config_path)
if not config_file.exists():
raise FileNotFoundError(f"é
眮æä»¶äžååš: {config_path}")
with open(config_file, 'r', encoding='utf-8') as f:
config_data = json.load(f)
analysis_config = AnalysisConfig(**config_data.get("analysis", {}))
mcp_config = MCPServerConfig(**config_data.get("mcp", {}))
return analysis_config, mcp_config
# 䜿çšç€ºäŸ
if __name__ == "__main__":
# 䜿çšèªå®ä¹é
眮
asyncio.run(custom_config_example())
# 仿件å 蜜é
眮
try:
analysis_cfg, mcp_cfg = load_config_from_file("examples/config.json")
print(f"仿件å 蜜çé
眮: {analysis_cfg.size_threshold_percentage}%")
except FileNotFoundError as e:
print(f"é
眮æä»¶å 蜜倱莥: {e}")
ð æ éæé€
åžžè§é®é¢
1. å¯Œå ¥é误
é®é¢: ModuleNotFoundError: No module named 'cmc_inspect'
è§£å³æ¹æ¡:
# ç¡®ä¿æ£ç¡®å®è£
项ç®
uv pip install -e .
# æè
䜿çšuvè¿è¡
uv run python your_script.py
2. è§£å倱莥
é®é¢: ValueError: äžæ¯æçæä»¶æ ŒåŒ
è§£å³æ¹æ¡:
# æ£æ¥æ¯æçæ ŒåŒ
from cmc_inspect.tools import PackageExtractor
supported = PackageExtractor.get_supported_formats()
print("æ¯æçæ ŒåŒ:", supported)
# 对äº7zæä»¶ïŒå®è£
é¢å€äŸèµ
# pip install py7zr
3. æéé误
é®é¢: PermissionError: [WinError 5] æç»è®¿é®
è§£å³æ¹æ¡:
# ç¡®ä¿äžŽæ¶ç®åœæåæé
import os
temp_dir = "./temp"
os.makedirs(temp_dir, exist_ok=True)
os.access(temp_dir, os.W_OK) # æ£æ¥åæé
# æè
䜿çšäžåç䞎æ¶ç®åœ
config = AnalysisConfig(temp_dir="/tmp/cmc_extract")
4. 倧æä»¶å€ç
é®é¢: å€ç倧æä»¶æ¶å åäžè¶³ è§£å³æ¹æ¡:
# å¢å å
åéå¶
import resource
resource.setrlimit(resource.RLIMIT_AS, (2**30, 2**30)) # 1GBéå¶
# æè
ååå€ç
def process_large_archive(archive_path, chunk_size=1024*1024):
# ååå€çé»èŸ
pass
è°è¯æå·§
1. å¯çšè¯Šç»æ¥å¿
import logging
logging.basicConfig(level=logging.DEBUG)
# æè
䜿çšé
眮
config = AnalysisConfig(verbose=True)
2. ä¿çäžŽæ¶æä»¶
config = AnalysisConfig(cleanup_temp_files=False)
# åæå®æåæ£æ¥äžŽæ¶ç®åœå
容
3. 䜿çšè°è¯æš¡åŒ
# 䜿çšè°è¯æš¡åŒè¿è¡
uv run python -m pdb your_script.py
# æè
åšä»£ç äžæ·»å æç¹
import pdb; pdb.set_trace()
æ§èœäŒå建议
1. 倧å蜯件å å€ç
- 䜿çšåŒæ¥æäœ
- åå读å倧æä»¶
- 讟眮åççå åéå¶
2. çœç»ç¯å¢
- é 眮代ç讟眮
- äœ¿çšæ¬å°çŒå
- å¹¶è¡å€çå€äžªå
3. CI/CDéæ
- 䜿çšDocker容åšé犻
- é 眮éåœçè¶ æ¶æ¶éŽ
- å®ç°éè¯æºå¶
è·ååž®å©
åŠæéå°å ¶ä»é®é¢ïŒå¯ä»¥ïŒ
- æ¥çæ¥å¿: å¯çšè¯Šç»æš¡åŒè·åæŽå€ä¿¡æ¯
- æ£æ¥é 眮: 确讀é 眮æä»¶æ ŒåŒæ£ç¡®
- è¿è¡æµè¯: éªè¯åºæ¬åèœæ¯åŠæ£åžž
- æ¥ç瀺äŸ: åèexamplesç®åœäžç瀺äŸä»£ç
- æäº€é®é¢: åšé¡¹ç®ä»åºäžæäº€Issue
ð 讞å¯è¯
MIT License - è¯Šè§ LICENSE æä»¶
ð€ 莡ç®
æ¬¢è¿æäº€Pull RequeståIssueïŒ
ð èç³»æ¹åŒ
- äœè : SillyDog5254
- é®ç®±: ruyileng26@gmail.com
ç¥æšäœ¿çšæå¿«ïŒð
