- Create new dss/ Python package at project root - Move MCP core from tools/dss_mcp/ to dss/mcp/ - Move storage layer from tools/storage/ to dss/storage/ - Move domain logic from dss-mvp1/dss/ to dss/ - Move services from tools/api/services/ to dss/services/ - Move API server to apps/api/ - Move CLI to apps/cli/ - Move Storybook assets to storybook/ - Create unified dss/__init__.py with comprehensive exports - Merge configuration into dss/settings.py (Pydantic-based) - Create pyproject.toml for proper package management - Update startup scripts for new paths - Remove old tools/ and dss-mvp1/ directories Architecture changes: - DSS is now MCP-first with 40+ tools for Claude Code - Clean imports: from dss import Projects, Components, FigmaToolSuite - No more sys.path.insert() hacking - apps/ contains thin application wrappers (API, CLI) - Single unified Python package for all DSS logic 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
149 lines
4.6 KiB
Python
149 lines
4.6 KiB
Python
"""Schema migration system for .dss archive compatibility"""
|
|
|
|
from typing import Dict, Any, List, Callable
|
|
import json
|
|
|
|
|
|
class SchemaMigration:
|
|
"""Base class for schema migrations"""
|
|
|
|
source_version: str = "1.0.0"
|
|
target_version: str = "1.0.1"
|
|
|
|
def up(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Migrate data from source to target version"""
|
|
raise NotImplementedError
|
|
|
|
def down(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Rollback migration"""
|
|
raise NotImplementedError
|
|
|
|
|
|
class MigrationV1_0_0_to_V1_0_1(SchemaMigration):
|
|
"""Initial migration: add UUID support to all entities"""
|
|
|
|
source_version = "1.0.0"
|
|
target_version = "1.0.1"
|
|
|
|
def up(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Add UUID fields if missing"""
|
|
from uuid import uuid4
|
|
|
|
# Ensure all entities have UUIDs (backward compat)
|
|
if 'project' in data:
|
|
if 'uuid' not in data['project']:
|
|
data['project']['uuid'] = str(uuid4())
|
|
|
|
if 'tokens' in data:
|
|
for token_name, token in data['tokens'].items():
|
|
if isinstance(token, dict) and 'uuid' not in token:
|
|
token['uuid'] = str(uuid4())
|
|
|
|
if 'components' in data:
|
|
for comp in data['components']:
|
|
if 'uuid' not in comp:
|
|
comp['uuid'] = str(uuid4())
|
|
if 'variants' in comp:
|
|
for variant in comp['variants']:
|
|
if 'uuid' not in variant:
|
|
variant['uuid'] = str(uuid4())
|
|
|
|
if 'themes' in data:
|
|
for theme in data['themes']:
|
|
if 'uuid' not in theme:
|
|
theme['uuid'] = str(uuid4())
|
|
|
|
return data
|
|
|
|
def down(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Remove UUID fields (rollback)"""
|
|
if 'project' in data:
|
|
data['project'].pop('uuid', None)
|
|
|
|
if 'tokens' in data:
|
|
for token in data['tokens'].values():
|
|
if isinstance(token, dict):
|
|
token.pop('uuid', None)
|
|
|
|
if 'components' in data:
|
|
for comp in data['components']:
|
|
comp.pop('uuid', None)
|
|
if 'variants' in comp:
|
|
for variant in comp['variants']:
|
|
variant.pop('uuid', None)
|
|
|
|
if 'themes' in data:
|
|
for theme in data['themes']:
|
|
theme.pop('uuid', None)
|
|
|
|
return data
|
|
|
|
|
|
class MigrationManager:
|
|
"""Manages schema migrations for .dss archives"""
|
|
|
|
# Map of version pairs to migration classes
|
|
MIGRATIONS: Dict[tuple, type] = {
|
|
("1.0.0", "1.0.1"): MigrationV1_0_0_to_V1_0_1,
|
|
}
|
|
|
|
# Ordered list of all schema versions
|
|
VERSIONS = ["1.0.0", "1.0.1"]
|
|
|
|
@classmethod
|
|
def migrate(cls, data: Dict[str, Any], from_version: str, to_version: str) -> Dict[str, Any]:
|
|
"""
|
|
Apply migrations from source version to target version.
|
|
|
|
Args:
|
|
data: Archive data to migrate
|
|
from_version: Current schema version in archive
|
|
to_version: Target schema version
|
|
|
|
Returns:
|
|
Migrated data
|
|
|
|
Raises:
|
|
ValueError: If migration path doesn't exist or versions are invalid
|
|
"""
|
|
if from_version == to_version:
|
|
return data
|
|
|
|
# Validate versions
|
|
if from_version not in cls.VERSIONS:
|
|
raise ValueError(f"Unknown source schema version: {from_version}")
|
|
if to_version not in cls.VERSIONS:
|
|
raise ValueError(f"Unknown target schema version: {to_version}")
|
|
|
|
from_idx = cls.VERSIONS.index(from_version)
|
|
to_idx = cls.VERSIONS.index(to_version)
|
|
|
|
if from_idx > to_idx:
|
|
raise ValueError(f"Cannot downgrade from {from_version} to {to_version}")
|
|
|
|
# Apply migrations sequentially
|
|
current_version = from_version
|
|
while current_version != to_version:
|
|
current_idx = cls.VERSIONS.index(current_version)
|
|
next_version = cls.VERSIONS[current_idx + 1]
|
|
|
|
migration_key = (current_version, next_version)
|
|
if migration_key not in cls.MIGRATIONS:
|
|
raise ValueError(f"No migration found for {current_version} -> {next_version}")
|
|
|
|
migration_class = cls.MIGRATIONS[migration_key]
|
|
migration = migration_class()
|
|
data = migration.up(data)
|
|
current_version = next_version
|
|
|
|
return data
|
|
|
|
@classmethod
|
|
def get_latest_version(cls) -> str:
|
|
"""Get latest schema version"""
|
|
return cls.VERSIONS[-1]
|
|
|
|
|
|
# Export
|
|
__all__ = ['MigrationManager', 'SchemaMigration']
|