Completed Phase 4 of the schema-of-schemas implementation with successful migration of all legacy schemas to the new markdown format following the naming convention. Migration Script (scripts/migrate_schemas.py - 240 lines): - Automated schema migration from JSON to markdown format - Updates version and $id fields to follow conventions - Generates proper frontmatter metadata - Dry-run mode for safe testing - Database cleanup functionality - Comprehensive progress reporting Schemas Migrated (2): - terminology-schema.json → terminology-schema-v1.0.md - Fixed missing version field - Updated $id from /terminology-v1.json to /terminology/v1.0 - Validates successfully against metaschema - api-documentation → api-documentation-schema-v1.0.md - Added version: 1.0.0 - Updated $id to follow /api-documentation/v1.0 format - Validates successfully against metaschema Schemas Deleted (3): - markdown-manpage (duplicate of manpage-schema-v1.0.md) - markdown-manpage-schema.json (duplicate of manpage-schema-v1.0.md) - enhanced-manpage (replaced by manpage-schema-v1.0.md) CLI Enhancement (markitect/cli.py): - Updated schema-ingest to support markdown (.md) files - Auto-detects file type and uses MarkdownSchemaLoader for .md files - Extracts JSON schema from markdown for database storage - Maintains backward compatibility with JSON files Final Schema Registry (4 schemas): ✅ terminology-schema-v1.0.md - Terminology validation ✅ api-documentation-schema-v1.0.md - API documentation structure ✅ manpage-schema-v1.0.md - Unix manual pages ✅ schema-schema-v1.0.md - Metaschema for validating schemas All schemas: - Follow naming convention: {domain}-schema-v{major}.{minor}.md - Include proper frontmatter with schema-id, version, status - Validate successfully against schema-schema-v1.0.md metaschema - Stored in database and ready for use Progress Tracking: - Updated TODO.md with Phase 4 completion - Updated CHANGELOG.md with migration details - Next: Phase 5 - CLI & Documentation Updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
209 lines
6.2 KiB
Python
Executable File
209 lines
6.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Migrate schemas to markdown format with versioning.
|
|
|
|
This script converts existing JSON schemas in the database to the new
|
|
markdown format following the naming convention: {domain}-schema-v{major}.{minor}.md
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Add parent directory to path for imports
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from markitect.database import DatabaseManager
|
|
from markitect.schema_loader import MarkdownSchemaLoader
|
|
|
|
|
|
def migrate_schema(
|
|
db_manager: DatabaseManager,
|
|
old_name: str,
|
|
new_filename: str,
|
|
version: str,
|
|
domain: str,
|
|
description: str,
|
|
dry_run: bool = False
|
|
):
|
|
"""
|
|
Migrate a single schema to new markdown format.
|
|
|
|
Args:
|
|
db_manager: Database manager instance
|
|
old_name: Name of old schema in database
|
|
new_filename: New filename following naming convention
|
|
version: SemVer version (major.minor.patch)
|
|
domain: Schema domain name
|
|
description: Brief schema description
|
|
dry_run: If True, don't save files
|
|
"""
|
|
print(f"\n{'[DRY RUN] ' if dry_run else ''}Migrating: {old_name} → {new_filename}")
|
|
|
|
# Get old schema from database
|
|
old_schema_data = db_manager.get_schema_file(old_name)
|
|
if not old_schema_data:
|
|
print(f" ❌ Schema not found in database: {old_name}")
|
|
return None
|
|
|
|
# Parse schema JSON
|
|
try:
|
|
schema_json = json.loads(old_schema_data['schema_content'])
|
|
except json.JSONDecodeError as e:
|
|
print(f" ❌ Invalid JSON: {e}")
|
|
return None
|
|
|
|
# Update schema metadata
|
|
major, minor = version.split('.')[:2]
|
|
schema_json['version'] = version
|
|
schema_json['$id'] = f"https://markitect.dev/schemas/{domain}/v{major}.{minor}"
|
|
|
|
# Ensure required fields
|
|
if 'description' not in schema_json or not schema_json['description']:
|
|
schema_json['description'] = description
|
|
|
|
# Create frontmatter
|
|
frontmatter = {
|
|
'schema-id': schema_json['$id'],
|
|
'version': version,
|
|
'status': 'stable',
|
|
'domain': domain,
|
|
'description': description
|
|
}
|
|
|
|
if dry_run:
|
|
print(f" ✓ Would create: {new_filename}")
|
|
print(f" Version: {version}")
|
|
print(f" $id: {schema_json['$id']}")
|
|
return None
|
|
|
|
# Save as markdown
|
|
loader = MarkdownSchemaLoader()
|
|
md_path = Path(__file__).parent.parent / 'markitect' / 'schemas' / new_filename
|
|
|
|
loader.save_schema(
|
|
schema=schema_json,
|
|
md_path=md_path,
|
|
frontmatter=frontmatter
|
|
)
|
|
|
|
print(f" ✅ Created: {md_path}")
|
|
print(f" Version: {version}")
|
|
print(f" $id: {schema_json['$id']}")
|
|
|
|
return md_path
|
|
|
|
|
|
def cleanup_old_schema(db_manager: DatabaseManager, schema_name: str, dry_run: bool = False):
|
|
"""
|
|
Remove old schema from database.
|
|
|
|
Args:
|
|
db_manager: Database manager instance
|
|
schema_name: Name of schema to remove
|
|
dry_run: If True, don't actually delete
|
|
"""
|
|
if dry_run:
|
|
print(f" [DRY RUN] Would delete from database: {schema_name}")
|
|
return
|
|
|
|
success = db_manager.delete_schema_file(schema_name)
|
|
if success:
|
|
print(f" 🗑️ Deleted from database: {schema_name}")
|
|
else:
|
|
print(f" ⚠️ Failed to delete: {schema_name}")
|
|
|
|
|
|
def main():
|
|
"""Execute schema migration."""
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description='Migrate schemas to markdown format')
|
|
parser.add_argument('--dry-run', action='store_true', help='Show what would be done without making changes')
|
|
parser.add_argument('--db', default='markitect.db', help='Database path')
|
|
args = parser.parse_args()
|
|
|
|
db_manager = DatabaseManager(args.db)
|
|
|
|
print("=" * 60)
|
|
print("Schema Migration - Phase 4")
|
|
print("=" * 60)
|
|
|
|
if args.dry_run:
|
|
print("\n🔍 DRY RUN MODE - No changes will be made\n")
|
|
|
|
# Define migrations
|
|
migrations = [
|
|
{
|
|
'old_name': 'terminology-schema.json',
|
|
'new_filename': 'terminology-schema-v1.0.md',
|
|
'version': '1.0.0',
|
|
'domain': 'terminology',
|
|
'description': 'Schema for validating terminology and glossary documents with consistent structure'
|
|
},
|
|
{
|
|
'old_name': 'api-documentation',
|
|
'new_filename': 'api-documentation-schema-v1.0.md',
|
|
'version': '1.0.0',
|
|
'domain': 'api-documentation',
|
|
'description': 'Schema for API documentation structure and content validation'
|
|
},
|
|
]
|
|
|
|
# Schemas to delete (duplicates and replaced)
|
|
to_delete = [
|
|
'markdown-manpage', # Duplicate
|
|
'markdown-manpage-schema.json', # Duplicate
|
|
'enhanced-manpage', # Replaced by manpage-schema-v1.0.md
|
|
]
|
|
|
|
# Execute migrations
|
|
print("\n📝 MIGRATING SCHEMAS")
|
|
print("-" * 60)
|
|
|
|
migrated_files = []
|
|
for migration in migrations:
|
|
result = migrate_schema(
|
|
db_manager=db_manager,
|
|
dry_run=args.dry_run,
|
|
**migration
|
|
)
|
|
if result:
|
|
migrated_files.append(result)
|
|
|
|
# Clean up old schemas
|
|
print("\n\n🗑️ CLEANING UP OLD SCHEMAS")
|
|
print("-" * 60)
|
|
|
|
for schema_name in to_delete:
|
|
cleanup_old_schema(db_manager, schema_name, dry_run=args.dry_run)
|
|
|
|
# Summary
|
|
print("\n\n" + "=" * 60)
|
|
print("MIGRATION SUMMARY")
|
|
print("=" * 60)
|
|
|
|
if args.dry_run:
|
|
print("\n✓ Dry run completed successfully")
|
|
print(f" Would migrate {len(migrations)} schemas to markdown format")
|
|
print(f" Would delete {len(to_delete)} old schemas from database")
|
|
else:
|
|
print(f"\n✓ Migrated {len(migrated_files)} schemas to markdown format")
|
|
print(f"✓ Cleaned up {len(to_delete)} old schemas")
|
|
|
|
if migrated_files:
|
|
print("\n📄 New schema files created:")
|
|
for f in migrated_files:
|
|
print(f" - {f.name}")
|
|
|
|
print("\n🔍 Next steps:")
|
|
print(" 1. Validate new schemas: markitect schema-validate <schema-file>")
|
|
print(" 2. Ingest new schemas: markitect schema-ingest <schema-file>")
|
|
print(" 3. Test with documents")
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|