Files
orchard/backend/app/seed.py
Mondo Diaz d4026978bc Add development mode with automatic test data seeding
- Add ORCHARD_ENV setting (defaults to 'development')
- Add seed.py with test projects, packages, artifacts, and tags
- Automatically seed database on startup in development mode
- Skip seeding if database already contains data
- Skip seeding in production mode (ORCHARD_ENV=production)

Test data includes:
- 4 projects (3 public, 1 private)
- 8 packages across projects
- 5 artifacts with various content types
- 10 tags including version tags and 'latest'
2025-12-11 14:24:34 -06:00

223 lines
6.8 KiB
Python

"""
Test data seeding for development environment.
"""
import hashlib
import logging
from sqlalchemy.orm import Session
from .models import Project, Package, Artifact, Tag, Upload
from .storage import get_storage
logger = logging.getLogger(__name__)
# Test data definitions
TEST_PROJECTS = [
{
"name": "frontend-libs",
"description": "Shared frontend libraries and components",
"is_public": True,
"packages": [
{
"name": "ui-components",
"description": "Reusable UI component library",
},
{
"name": "design-tokens",
"description": "Design system tokens and variables",
},
],
},
{
"name": "backend-services",
"description": "Backend microservices and shared utilities",
"is_public": True,
"packages": [
{
"name": "auth-lib",
"description": "Authentication and authorization library",
},
{
"name": "common-utils",
"description": "Common utility functions",
},
{
"name": "api-client",
"description": "Generated API client library",
},
],
},
{
"name": "mobile-apps",
"description": "Mobile application builds and assets",
"is_public": True,
"packages": [
{
"name": "ios-release",
"description": "iOS release builds",
},
{
"name": "android-release",
"description": "Android release builds",
},
],
},
{
"name": "internal-tools",
"description": "Internal development tools (private)",
"is_public": False,
"packages": [
{
"name": "dev-scripts",
"description": "Development automation scripts",
},
],
},
]
# Sample artifacts to create (content, tags)
TEST_ARTIFACTS = [
{
"project": "frontend-libs",
"package": "ui-components",
"content": b"/* UI Components v1.0.0 */\nexport const Button = () => {};\nexport const Input = () => {};\n",
"filename": "ui-components-1.0.0.js",
"content_type": "application/javascript",
"tags": ["v1.0.0", "latest"],
},
{
"project": "frontend-libs",
"package": "ui-components",
"content": b"/* UI Components v1.1.0 */\nexport const Button = () => {};\nexport const Input = () => {};\nexport const Modal = () => {};\n",
"filename": "ui-components-1.1.0.js",
"content_type": "application/javascript",
"tags": ["v1.1.0"],
},
{
"project": "frontend-libs",
"package": "design-tokens",
"content": b'{"colors": {"primary": "#007bff", "secondary": "#6c757d"}, "spacing": {"sm": "8px", "md": "16px"}}',
"filename": "tokens.json",
"content_type": "application/json",
"tags": ["v1.0.0", "latest"],
},
{
"project": "backend-services",
"package": "common-utils",
"content": b"# Common Utils\n\ndef format_date(dt):\n return dt.isoformat()\n\ndef slugify(text):\n return text.lower().replace(' ', '-')\n",
"filename": "utils-2.0.0.py",
"content_type": "text/x-python",
"tags": ["v2.0.0", "stable", "latest"],
},
{
"project": "backend-services",
"package": "auth-lib",
"content": b"package auth\n\nfunc ValidateToken(token string) bool {\n return len(token) > 0\n}\n",
"filename": "auth-lib-1.0.0.go",
"content_type": "text/x-go",
"tags": ["v1.0.0", "latest"],
},
]
def is_database_empty(db: Session) -> bool:
"""Check if the database has any projects."""
return db.query(Project).first() is None
def seed_database(db: Session) -> None:
"""Seed the database with test data."""
if not is_database_empty(db):
logger.info("Database already has data, skipping seed")
return
logger.info("Seeding database with test data...")
storage = get_storage()
# Create projects and packages
project_map = {}
package_map = {}
for project_data in TEST_PROJECTS:
project = Project(
name=project_data["name"],
description=project_data["description"],
is_public=project_data["is_public"],
created_by="seed-user",
)
db.add(project)
db.flush() # Get the ID
project_map[project_data["name"]] = project
for package_data in project_data["packages"]:
package = Package(
project_id=project.id,
name=package_data["name"],
description=package_data["description"],
)
db.add(package)
db.flush()
package_map[(project_data["name"], package_data["name"])] = package
logger.info(f"Created {len(project_map)} projects and {len(package_map)} packages")
# Create artifacts and tags
artifact_count = 0
tag_count = 0
for artifact_data in TEST_ARTIFACTS:
project = project_map[artifact_data["project"]]
package = package_map[(artifact_data["project"], artifact_data["package"])]
content = artifact_data["content"]
sha256_hash = hashlib.sha256(content).hexdigest()
size = len(content)
s3_key = f"fruits/{sha256_hash[:2]}/{sha256_hash[2:4]}/{sha256_hash}"
# Store in S3
try:
storage.client.put_object(
Bucket=storage.bucket,
Key=s3_key,
Body=content,
)
except Exception as e:
logger.warning(f"Failed to store artifact in S3: {e}")
continue
# Create artifact record
artifact = Artifact(
id=sha256_hash,
size=size,
content_type=artifact_data["content_type"],
original_name=artifact_data["filename"],
created_by="seed-user",
s3_key=s3_key,
ref_count=len(artifact_data["tags"]),
)
db.add(artifact)
# Create upload record
upload = Upload(
artifact_id=sha256_hash,
package_id=package.id,
original_name=artifact_data["filename"],
uploaded_by="seed-user",
)
db.add(upload)
artifact_count += 1
# Create tags
for tag_name in artifact_data["tags"]:
tag = Tag(
package_id=package.id,
name=tag_name,
artifact_id=sha256_hash,
created_by="seed-user",
)
db.add(tag)
tag_count += 1
db.commit()
logger.info(f"Created {artifact_count} artifacts and {tag_count} tags")
logger.info("Database seeding complete")