bootstrap.py
1 #!/usr/bin/env python3 2 """ 3 Workspace discovery and namespace mapping. 4 Scans the workspace root for repositories and generates workspace_map.json. 5 """ 6 import os 7 import json 8 import sys 9 10 from config import ( 11 WORKSPACE_ROOT, WORKSPACE_MAP_PATH, RUN_PY_PATH, PROXY_SYMLINK_PATH, 12 get_namespace 13 ) 14 15 16 def ensure_proxy_symlink(): 17 """Ensure the 'r' symlink to run.py exists.""" 18 if os.path.islink(PROXY_SYMLINK_PATH): 19 current_target = os.readlink(PROXY_SYMLINK_PATH) 20 if current_target == RUN_PY_PATH: 21 print(f"Proxy symlink OK: {PROXY_SYMLINK_PATH} -> {RUN_PY_PATH}") 22 return True 23 print(f"Symlink exists but points to {current_target}, updating...") 24 25 # Ensure run.py is executable 26 if not os.access(RUN_PY_PATH, os.X_OK): 27 try: 28 os.chmod(RUN_PY_PATH, 0o755) 29 except OSError as e: 30 sys.stderr.write(f"!ERR:CHMOD Cannot make run.py executable: {e}\n") 31 return False 32 33 # Create/update symlink (may require sudo) 34 try: 35 if os.path.exists(PROXY_SYMLINK_PATH) or os.path.islink(PROXY_SYMLINK_PATH): 36 os.remove(PROXY_SYMLINK_PATH) 37 os.symlink(RUN_PY_PATH, PROXY_SYMLINK_PATH) 38 print(f"Created symlink: {PROXY_SYMLINK_PATH} -> {RUN_PY_PATH}") 39 return True 40 except PermissionError: 41 sys.stderr.write(f"!ERR:PERM Need sudo to create {PROXY_SYMLINK_PATH}\n") 42 sys.stderr.write(f" Run: sudo ln -sf {RUN_PY_PATH} {PROXY_SYMLINK_PATH}\n") 43 return False 44 except OSError as e: 45 sys.stderr.write(f"!ERR:SYMLINK Cannot create symlink: {e}\n") 46 return False 47 48 49 def scan_repos(root_dir): 50 """Scan directory for repositories (identified by .git or .forgejo).""" 51 repos = {} 52 53 try: 54 entries = os.listdir(root_dir) 55 except OSError as e: 56 sys.stderr.write(f"!ERR:SCAN Cannot read {root_dir}: {e}\n") 57 return repos 58 59 for entry in entries: 60 full_path = os.path.join(root_dir, entry) 61 if not os.path.isdir(full_path): 62 continue 63 64 # Check for repo indicators 65 if os.path.exists(os.path.join(full_path, '.git')) or \ 66 os.path.exists(os.path.join(full_path, '.forgejo')): 67 namespace_key = get_namespace(entry) 68 repos[namespace_key] = full_path 69 70 return repos 71 72 73 def main(): 74 print(f"Workspace root: {WORKSPACE_ROOT}") 75 76 # Ensure proxy wrapper symlink exists 77 ensure_proxy_symlink() 78 79 print("Scanning for repositories...") 80 81 repo_map = scan_repos(WORKSPACE_ROOT) 82 83 if not repo_map: 84 sys.stderr.write("!ERR:EMPTY No repositories found.\n") 85 sys.exit(1) 86 87 print(f"Generating workspace map at {WORKSPACE_MAP_PATH}...") 88 89 try: 90 with open(WORKSPACE_MAP_PATH, 'w') as f: 91 json.dump(repo_map, f, indent=2) 92 except OSError as e: 93 sys.stderr.write(f"!ERR:WRITE Cannot write map: {e}\n") 94 sys.exit(1) 95 96 print(f"[S:PASS] Mapped {len(repo_map)} repositories.") 97 98 99 if __name__ == "__main__": 100 main()