gabriel / musehub public
seed_production.py python
136 lines 4.5 KB
00d5d2e3 Sync dev → main: test fixes, MCP refactor, hero centering, deploy pipel… Gabriel Cardona <cgcardona@gmail.com> 3d ago
1 """
2 Production seed for musehub.ai
3
4 Creates gabriel's account and profile only.
5 The muse repo is pushed from the actual codebase via `muse push`.
6
7 Idempotent: safe to re-run (skips existing records).
8
9 Run inside the container:
10 docker compose exec musehub python3 /app/scripts/seed_production.py
11 """
12 from __future__ import annotations
13
14 import asyncio
15 import hashlib
16 import uuid
17 from datetime import datetime, timedelta, timezone
18
19 from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
20 from sqlalchemy.orm import sessionmaker
21 from sqlalchemy import select, delete
22
23 from musehub.config import settings
24 from musehub.db.models import User
25 from musehub.db.musehub_models import (
26 MusehubBranch,
27 MusehubCommit,
28 MusehubIssue,
29 MusehubProfile,
30 MusehubRelease,
31 MusehubRepo,
32 )
33
34 UTC = timezone.utc
35
36
37 def _now(days: int = 0) -> datetime:
38 return datetime.now(tz=UTC) - timedelta(days=days)
39
40
41 def _uid(seed: str) -> str:
42 return str(uuid.UUID(bytes=hashlib.md5(seed.encode()).digest()))
43
44
45 GABRIEL_ID = _uid("prod-gabriel-cgcardona")
46
47 # Slugs seeded in the past that should be removed if still present.
48 REMOVED_SLUGS = [
49 "muse",
50 "musehub",
51 "agentception",
52 "maestro",
53 "stori",
54 "well-tempered-clavier",
55 "moonlight-sonata",
56 "neobaroque-sketches",
57 "modal-sessions",
58 "chopin-nocturnes",
59 ]
60
61
62 async def seed() -> None:
63 db_url = settings.database_url or "sqlite+aiosqlite:///./muse.db"
64 engine = create_async_engine(db_url)
65 Session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
66
67 async with Session() as session:
68
69 # ── 1. Remove any previously-seeded repos ─────────────────────────────
70 for slug in REMOVED_SLUGS:
71 result = await session.execute(
72 select(MusehubRepo).where(
73 MusehubRepo.owner == "gabriel",
74 MusehubRepo.slug == slug,
75 )
76 )
77 repo = result.scalar_one_or_none()
78 if repo:
79 rid = repo.repo_id
80 await session.execute(delete(MusehubCommit).where(MusehubCommit.repo_id == rid))
81 await session.execute(delete(MusehubBranch).where(MusehubBranch.repo_id == rid))
82 await session.execute(delete(MusehubIssue).where(MusehubIssue.repo_id == rid))
83 await session.execute(delete(MusehubRelease).where(MusehubRelease.repo_id == rid))
84 await session.delete(repo)
85 print(f"[-] Removed gabriel/{slug}")
86
87 await session.flush()
88
89 # ── 2. Gabriel's core user record ─────────────────────────────────────
90 existing_user = await session.get(User, GABRIEL_ID)
91 if existing_user is None:
92 session.add(User(id=GABRIEL_ID, created_at=_now(365)))
93 print("[+] Created User: gabriel")
94 else:
95 print("[=] User gabriel already exists")
96
97 # ── 3. Gabriel's public profile ───────────────────────────────────────
98 existing_profile = await session.get(MusehubProfile, GABRIEL_ID)
99 if existing_profile is None:
100 session.add(MusehubProfile(
101 user_id=GABRIEL_ID,
102 username="gabriel",
103 display_name="Gabriel Cardona",
104 bio=(
105 "Building Muse — a domain-agnostic VCS for multidimensional state. "
106 "Code, music, narrative, genomics. If it changes over time, Muse can version it."
107 ),
108 location="San Francisco, CA",
109 website_url="https://musehub.ai",
110 twitter_handle="cgcardona",
111 is_verified=True,
112 pinned_repo_ids=[],
113 created_at=_now(365),
114 ))
115 print("[+] Created Profile: gabriel")
116 else:
117 print("[=] Profile gabriel already exists")
118
119 await session.commit()
120
121 print()
122 print("=" * 60)
123 print("SEED COMPLETE — push repos via `muse push`")
124 print("=" * 60)
125 print()
126 print("Mint your admin JWT:")
127 print()
128 print(" docker compose exec musehub python3 -c \"")
129 print(" from musehub.auth.tokens import generate_access_code")
130 print(f" print(generate_access_code(user_id='{GABRIEL_ID}', duration_days=365, is_admin=True))")
131 print(" \"")
132 print()
133
134
135 if __name__ == "__main__":
136 asyncio.run(seed())