/ bootstrap.py
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()