147 lines
4.8 KiB
Python
147 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|||
|
|
"""
|
||
|
|
Validate a GFA manifest (v1) - check schema + file existence.
|
||
|
|
|
||
|
|
Usage:
|
||
|
|
python validate_manifest.py <manifest.json>
|
||
|
|
python validate_manifest.py --help
|
||
|
|
"""
|
||
|
|
# 验证 Identify manifest
|
||
|
|
#python apps/cli/validate_manifest.py workspace/20260128_072702_3148145_fb521a/final/Ft.SPL.identify.manifest.json
|
||
|
|
|
||
|
|
# 如果想指定其他 schema 文件
|
||
|
|
#python apps/cli/validate_manifest.py workspace/.../some.manifest.json --schema gfa_core/schemas/core/manifest_v2.schema.json
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
try:
|
||
|
|
import jsonschema
|
||
|
|
HAS_JSONSCHEMA = True
|
||
|
|
except ImportError:
|
||
|
|
HAS_JSONSCHEMA = False
|
||
|
|
print("⚠️ jsonschema not installed, schema validation will be skipped.")
|
||
|
|
print(" Install with: pip install jsonschema")
|
||
|
|
|
||
|
|
|
||
|
|
def validate_schema(manifest_path: Path, schema_path: Path):
|
||
|
|
"""Validate manifest against JSON Schema."""
|
||
|
|
if not HAS_JSONSCHEMA:
|
||
|
|
print("❌ Schema validation unavailable: jsonschema is not installed")
|
||
|
|
return False
|
||
|
|
|
||
|
|
try:
|
||
|
|
with open(manifest_path, 'r', encoding='utf-8') as f:
|
||
|
|
instance = json.load(f)
|
||
|
|
with open(schema_path, 'r', encoding='utf-8') as f:
|
||
|
|
schema = json.load(f)
|
||
|
|
|
||
|
|
jsonschema.validate(instance=instance, schema=schema)
|
||
|
|
print(f"✅ Schema validation passed")
|
||
|
|
return True
|
||
|
|
except json.JSONDecodeError as e:
|
||
|
|
print(f"❌ Manifest is not valid JSON: {e}")
|
||
|
|
return False
|
||
|
|
except jsonschema.exceptions.ValidationError as e:
|
||
|
|
print(f"❌ Schema validation failed:")
|
||
|
|
print(f" Path: {e.json_path}")
|
||
|
|
print(f" Error: {e.message}")
|
||
|
|
return False
|
||
|
|
except Exception as e:
|
||
|
|
print(f"❌ Unexpected error during schema validation: {e}")
|
||
|
|
return False
|
||
|
|
|
||
|
|
|
||
|
|
def validate_file_existence(manifest_path: Path):
|
||
|
|
"""Check that all files referenced in manifest.artifacts exist."""
|
||
|
|
with open(manifest_path, 'r', encoding='utf-8') as f:
|
||
|
|
manifest = json.load(f)
|
||
|
|
|
||
|
|
artifacts = manifest.get('artifacts', {})
|
||
|
|
if not artifacts:
|
||
|
|
print("⚠️ No artifacts found in manifest")
|
||
|
|
return True
|
||
|
|
|
||
|
|
workspace_root = manifest.get('workspace_root')
|
||
|
|
if not workspace_root:
|
||
|
|
print("⚠️ workspace_root not found in manifest, cannot resolve relative paths")
|
||
|
|
# We'll still try using full_path if available
|
||
|
|
|
||
|
|
all_exist = True
|
||
|
|
missing_files = []
|
||
|
|
|
||
|
|
for artifact_name, artifact_info in artifacts.items():
|
||
|
|
# Try full_path first, then path/relpath_or_hint
|
||
|
|
full_path = artifact_info.get('full_path')
|
||
|
|
if not full_path:
|
||
|
|
# Try to construct from workspace_root + path/relpath
|
||
|
|
rel_path = artifact_info.get('path') or artifact_info.get('relpath_or_hint')
|
||
|
|
if rel_path and workspace_root:
|
||
|
|
full_path = os.path.join(workspace_root, rel_path)
|
||
|
|
else:
|
||
|
|
print(f"⚠️ Artifact '{artifact_name}' has no full_path or resolvable path")
|
||
|
|
continue
|
||
|
|
|
||
|
|
file_path = Path(full_path)
|
||
|
|
if file_path.exists():
|
||
|
|
print(f"✅ [{artifact_name}] file exists: {file_path}")
|
||
|
|
else:
|
||
|
|
print(f"❌ [{artifact_name}] file MISSING: {file_path}")
|
||
|
|
missing_files.append(f"{artifact_name}: {file_path}")
|
||
|
|
all_exist = False
|
||
|
|
|
||
|
|
if missing_files:
|
||
|
|
print("\n❌ Missing files summary:")
|
||
|
|
for m in missing_files:
|
||
|
|
print(f" - {m}")
|
||
|
|
|
||
|
|
return all_exist
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
parser = argparse.ArgumentParser(description="Validate GFA manifest")
|
||
|
|
parser.add_argument("manifest", help="Path to manifest.json")
|
||
|
|
parser.add_argument("--schema",
|
||
|
|
default="gfa_core/schemas/core/manifest_v1.schema.json",
|
||
|
|
help="Path to JSON schema file (default: gfa_core/schemas/core/manifest_v1.schema.json)")
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
manifest_path = Path(args.manifest)
|
||
|
|
if not manifest_path.exists():
|
||
|
|
print(f"❌ Manifest file not found: {manifest_path}")
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
schema_path = Path(args.schema)
|
||
|
|
if not schema_path.exists():
|
||
|
|
print(f"❌ Schema file not found: {schema_path}")
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
print(f"🔍 Validating manifest: {manifest_path}")
|
||
|
|
print(f" Using schema: {schema_path}")
|
||
|
|
|
||
|
|
# Step 1: Schema validation
|
||
|
|
schema_ok = validate_schema(manifest_path, schema_path)
|
||
|
|
|
||
|
|
# Step 2: File existence validation
|
||
|
|
files_ok = validate_file_existence(manifest_path)
|
||
|
|
|
||
|
|
# Summary
|
||
|
|
print("\n" + "="*60)
|
||
|
|
if schema_ok and files_ok:
|
||
|
|
print("✅ Manifest validation PASSED")
|
||
|
|
sys.exit(0)
|
||
|
|
else:
|
||
|
|
print("❌ Manifest validation FAILED")
|
||
|
|
if not schema_ok:
|
||
|
|
print(" - Schema validation failed")
|
||
|
|
if not files_ok:
|
||
|
|
print(" - File existence check failed")
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|