/ archive / python / kamaji / update.py
update.py
  1  """
  2  Self-update functionality for Kamaji.
  3  """
  4  
  5  import os
  6  import sys
  7  import subprocess
  8  from pathlib import Path
  9  
 10  
 11  def get_source_path() -> Path:
 12      """
 13      Get the source directory path where Kamaji is installed from.
 14  
 15      Returns:
 16          Path to the source directory
 17      """
 18      # The source path is stored during installation
 19      # It's the directory containing setup.py
 20      import kamaji
 21      kamaji_module_path = Path(kamaji.__file__).parent
 22      source_path = kamaji_module_path.parent
 23      return source_path
 24  
 25  
 26  def check_git_repo(source_path: Path) -> bool:
 27      """
 28      Check if the source path is a git repository.
 29  
 30      Args:
 31          source_path: Path to check
 32  
 33      Returns:
 34          True if it's a git repo
 35      """
 36      git_dir = source_path / ".git"
 37      return git_dir.exists() and git_dir.is_dir()
 38  
 39  
 40  def run_update(verbose: bool = False) -> int:
 41      """
 42      Update Kamaji by pulling latest changes and reinstalling.
 43  
 44      Args:
 45          verbose: Show detailed output
 46  
 47      Returns:
 48          Exit code (0 for success)
 49      """
 50      try:
 51          source_path = get_source_path()
 52  
 53          print(f"šŸ” Kamaji source location: {source_path}")
 54  
 55          # Check if it's a git repository
 56          if not check_git_repo(source_path):
 57              print("āŒ Error: Source directory is not a git repository")
 58              print(f"   Location: {source_path}")
 59              print("\nTo enable updates, install Kamaji from a git clone:")
 60              print("  git clone git@github.com:TransformerOS/Kamaji.git")
 61              print("  cd Kamaji")
 62              print("  make install")
 63              return 1
 64  
 65          print("šŸ“” Checking for updates...")
 66  
 67          # Check current branch
 68          result = subprocess.run(
 69              ["git", "rev-parse", "--abbrev-ref", "HEAD"],
 70              cwd=source_path,
 71              capture_output=True,
 72              text=True
 73          )
 74          branch = result.stdout.strip()
 75          print(f"šŸ“ Current branch: {branch}")
 76  
 77          # Check current commit
 78          result = subprocess.run(
 79              ["git", "rev-parse", "--short", "HEAD"],
 80              cwd=source_path,
 81              capture_output=True,
 82              text=True
 83          )
 84          current_commit = result.stdout.strip()
 85          print(f"šŸ“Œ Current commit: {current_commit}")
 86  
 87          # Fetch updates
 88          print("\nšŸ”„ Fetching updates from remote...")
 89          result = subprocess.run(
 90              ["git", "fetch", "origin"],
 91              cwd=source_path,
 92              capture_output=not verbose,
 93              text=True
 94          )
 95  
 96          if result.returncode != 0:
 97              print("āŒ Error: Failed to fetch updates")
 98              if not verbose:
 99                  print(result.stderr)
100              return 1
101  
102          # Check if updates are available
103          result = subprocess.run(
104              ["git", "rev-list", f"HEAD..origin/{branch}", "--count"],
105              cwd=source_path,
106              capture_output=True,
107              text=True
108          )
109  
110          commits_behind = int(result.stdout.strip()) if result.returncode == 0 else 0
111  
112          if commits_behind == 0:
113              print("āœ… Already up to date!")
114              return 0
115  
116          print(f"šŸ“¦ {commits_behind} update(s) available")
117  
118          # Show what will be updated
119          print("\nšŸ“‹ Changes:")
120          result = subprocess.run(
121              ["git", "log", f"HEAD..origin/{branch}", "--oneline", "--no-decorate"],
122              cwd=source_path,
123              capture_output=True,
124              text=True
125          )
126          if result.returncode == 0:
127              for line in result.stdout.strip().split('\n')[:5]:
128                  print(f"  • {line}")
129              if commits_behind > 5:
130                  print(f"  ... and {commits_behind - 5} more")
131  
132          # Ask for confirmation
133          print("\nā“ Update Kamaji?")
134          response = input("   Type 'yes' to continue: ").strip().lower()
135  
136          if response != 'yes':
137              print("āŒ Update cancelled")
138              return 0
139  
140          # Pull updates
141          print("\nā¬‡ļø  Pulling updates...")
142          result = subprocess.run(
143              ["git", "pull", "origin", branch],
144              cwd=source_path,
145              capture_output=not verbose,
146              text=True
147          )
148  
149          if result.returncode != 0:
150              print("āŒ Error: Failed to pull updates")
151              if not verbose:
152                  print(result.stderr)
153              return 1
154  
155          # Get new commit
156          result = subprocess.run(
157              ["git", "rev-parse", "--short", "HEAD"],
158              cwd=source_path,
159              capture_output=True,
160              text=True
161          )
162          new_commit = result.stdout.strip()
163          print(f"āœ… Updated to commit: {new_commit}")
164  
165          # Check if Makefile exists
166          makefile = source_path / "Makefile"
167  
168          if makefile.exists():
169              print("\nšŸ”Ø Rebuilding and reinstalling...")
170              print("   Running: make install")
171              result = subprocess.run(
172                  ["make", "install"],
173                  cwd=source_path,
174                  capture_output=False,  # Always show output
175                  text=True
176              )
177  
178              if result.returncode != 0:
179                  print("āŒ Error: Failed to reinstall")
180                  return 1
181  
182              print("āœ… Installation complete")
183          else:
184              # Fallback to pip install
185              print("\nšŸ”Ø Rebuilding and reinstalling...")
186              venv_pip = source_path / "venv" / "bin" / "pip"
187  
188              if venv_pip.exists():
189                  print(f"   Running: {venv_pip} install -e .")
190                  result = subprocess.run(
191                      [str(venv_pip), "install", "-e", "."],
192                      cwd=source_path,
193                      capture_output=False,  # Always show output
194                      text=True
195                  )
196  
197                  if result.returncode != 0:
198                      print("āŒ Error: Failed to reinstall")
199                      return 1
200  
201                  print("āœ… Installation complete")
202              else:
203                  print("āš ļø  Warning: Could not find pip to reinstall")
204                  print("   Please run 'make install' manually in:")
205                  print(f"   {source_path}")
206                  return 1
207  
208          print("\nšŸŽ‰ Kamaji successfully updated!")
209          print(f"   {current_commit} → {new_commit}")
210          print("\nšŸ’” Restart any running Kamaji sessions to use the new version")
211  
212          return 0
213  
214      except KeyboardInterrupt:
215          print("\n\nāŒ Update cancelled by user")
216          return 130
217      except Exception as e:
218          print(f"\nāŒ Error: {e}")
219          if verbose:
220              import traceback
221              traceback.print_exc()
222          return 1