machineconfig 5.25__py3-none-any.whl → 5.27__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of machineconfig might be problematic. Click here for more details.

Files changed (50) hide show
  1. machineconfig/jobs/installer/custom/hx.py +1 -1
  2. machineconfig/jobs/installer/custom_dev/alacritty.py +4 -4
  3. machineconfig/jobs/installer/installer_data.json +17 -0
  4. machineconfig/jobs/installer/linux_scripts/wezterm.sh +1 -1
  5. machineconfig/jobs/installer/package_groups.py +70 -111
  6. machineconfig/scripts/python/agents.py +4 -4
  7. machineconfig/scripts/python/choose_wezterm_theme.py +2 -2
  8. machineconfig/scripts/python/devops.py +6 -6
  9. machineconfig/scripts/python/{devops_update_repos.py → devops_helpers/devops_update_repos.py} +1 -1
  10. machineconfig/scripts/python/fire_jobs.py +3 -3
  11. machineconfig/scripts/python/helpers/repo_sync_helpers.py +14 -1
  12. machineconfig/scripts/python/helpers_fire/__init__.py +0 -0
  13. machineconfig/scripts/python/{fire_agents_help_launch.py → helpers_fire/fire_agents_help_launch.py} +1 -1
  14. machineconfig/scripts/python/helpers_fire_command/__init__.py +0 -0
  15. machineconfig/scripts/python/helpers_fire_command/fire_jobs_streamlit_helper.py +0 -0
  16. machineconfig/scripts/python/helpers_repos/grource.py +341 -0
  17. machineconfig/scripts/python/interactive.py +1 -1
  18. machineconfig/scripts/python/repos.py +75 -73
  19. machineconfig/scripts/python/{count_lines_frontend.py → repos_helpers/count_lines_frontend.py} +1 -1
  20. machineconfig/scripts/python/{repos_helper.py → repos_helpers/repos_helper.py} +4 -12
  21. machineconfig/scripts/python/{repos_helper_action.py → repos_helpers/repos_helper_action.py} +1 -1
  22. machineconfig/scripts/python/sessions_multiprocess.py +1 -1
  23. machineconfig/utils/files/ouch/__init__.py +0 -0
  24. machineconfig/utils/files/ouch/decompress.py +45 -0
  25. machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
  26. machineconfig/utils/source_of_truth.py +0 -1
  27. machineconfig/utils/ssh.py +33 -19
  28. {machineconfig-5.25.dist-info → machineconfig-5.27.dist-info}/METADATA +3 -1
  29. {machineconfig-5.25.dist-info → machineconfig-5.27.dist-info}/RECORD +48 -44
  30. machineconfig/scripts/python/get_zellij_cmd.py +0 -15
  31. machineconfig/scripts/python/t4.py +0 -17
  32. /machineconfig/jobs/{python → installer}/check_installations.py +0 -0
  33. /machineconfig/scripts/python/{fire_jobs_streamlit_helper.py → devops_helpers/__init__.py} +0 -0
  34. /machineconfig/scripts/python/{devops_add_identity.py → devops_helpers/devops_add_identity.py} +0 -0
  35. /machineconfig/scripts/python/{devops_add_ssh_key.py → devops_helpers/devops_add_ssh_key.py} +0 -0
  36. /machineconfig/scripts/python/{devops_backup_retrieve.py → devops_helpers/devops_backup_retrieve.py} +0 -0
  37. /machineconfig/scripts/python/{devops_status.py → devops_helpers/devops_status.py} +0 -0
  38. /machineconfig/scripts/python/{fire_agents_help_search.py → helpers_fire/fire_agents_help_search.py} +0 -0
  39. /machineconfig/scripts/python/{fire_agents_helper_types.py → helpers_fire/fire_agents_helper_types.py} +0 -0
  40. /machineconfig/scripts/python/{fire_agents_load_balancer.py → helpers_fire/fire_agents_load_balancer.py} +0 -0
  41. /machineconfig/scripts/python/{cloud_manager.py → helpers_fire_command/cloud_manager.py} +0 -0
  42. /machineconfig/scripts/python/{fire_jobs_args_helper.py → helpers_fire_command/fire_jobs_args_helper.py} +0 -0
  43. /machineconfig/scripts/python/{fire_jobs_route_helper.py → helpers_fire_command/fire_jobs_route_helper.py} +0 -0
  44. /machineconfig/scripts/python/{count_lines.py → repos_helpers/count_lines.py} +0 -0
  45. /machineconfig/scripts/python/{repos_helper_clone.py → repos_helpers/repos_helper_clone.py} +0 -0
  46. /machineconfig/scripts/python/{repos_helper_record.py → repos_helpers/repos_helper_record.py} +0 -0
  47. /machineconfig/scripts/python/{repos_helper_update.py → repos_helpers/repos_helper_update.py} +0 -0
  48. {machineconfig-5.25.dist-info → machineconfig-5.27.dist-info}/WHEEL +0 -0
  49. {machineconfig-5.25.dist-info → machineconfig-5.27.dist-info}/entry_points.txt +0 -0
  50. {machineconfig-5.25.dist-info → machineconfig-5.27.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,341 @@
1
+ """Gource visualization tool for git repositories."""
2
+
3
+ from pathlib import Path
4
+ from typing import Annotated, Optional
5
+ import subprocess
6
+ import platform
7
+ import zipfile
8
+ import typer
9
+
10
+
11
+ def get_gource_install_dir() -> Path:
12
+ """Get the installation directory for portable Gource."""
13
+ if platform.system() == "Windows":
14
+ appdata = Path.home() / "AppData" / "Local"
15
+ return appdata / "gource"
16
+ else:
17
+ return Path.home() / ".local" / "bin" / "gource"
18
+
19
+
20
+ def get_gource_executable() -> Path:
21
+ """Get the path to the gource executable (inside the extracted directory with DLLs)."""
22
+ install_dir = get_gource_install_dir()
23
+ if platform.system() == "Windows":
24
+ possible_paths = [
25
+ install_dir / "gource.exe",
26
+ install_dir / f"gource-{get_default_version()}.win64" / "gource.exe",
27
+ ]
28
+ for path in possible_paths:
29
+ if path.exists():
30
+ return path
31
+ return install_dir / f"gource-{get_default_version()}.win64" / "gource.exe"
32
+ else:
33
+ return install_dir / "gource"
34
+
35
+
36
+ def get_default_version() -> str:
37
+ """Get the default gource version."""
38
+ return "0.53"
39
+
40
+
41
+ def install_gource_windows(version: Optional[str] = None) -> None:
42
+ """Install portable Gource on Windows by downloading and extracting the zip archive."""
43
+ if platform.system() != "Windows":
44
+ raise OSError(f"This installer is for Windows only. Current OS: {platform.system()}")
45
+
46
+ from machineconfig.utils.path_extended import PathExtended
47
+ from machineconfig.utils.source_of_truth import INSTALL_TMP_DIR
48
+
49
+ print("\n" + "=" * 80)
50
+ print("🚀 GOURCE PORTABLE INSTALLATION 🚀")
51
+ print("=" * 80 + "\n")
52
+
53
+ version_str = version or get_default_version()
54
+ portable_url = f"https://github.com/acaudwell/Gource/releases/download/gource-{version_str}/gource-{version_str}.win64.zip"
55
+ install_dir = get_gource_install_dir()
56
+
57
+ print(f"📥 Downloading portable Gource from: {portable_url}")
58
+ downloaded_zip = PathExtended(portable_url).download(folder=INSTALL_TMP_DIR)
59
+ print(f"✅ Downloaded to: {downloaded_zip}")
60
+
61
+ print(f"\n� Extracting to: {install_dir}")
62
+ install_dir.mkdir(parents=True, exist_ok=True)
63
+
64
+ try:
65
+ with zipfile.ZipFile(downloaded_zip, 'r') as zip_ref:
66
+ zip_ref.extractall(install_dir)
67
+ print(f"✅ Extracted successfully to: {install_dir}")
68
+ print(f" (The zip contains gource-{version_str}.win64/ directory with exe and DLL dependencies)")
69
+ except Exception as e:
70
+ print(f"❌ Extraction failed with error: {e}")
71
+ raise
72
+
73
+ print("\n🗑️ Cleaning up zip file...")
74
+ try:
75
+ downloaded_zip.unlink()
76
+ print(f"✅ Removed zip file: {downloaded_zip}")
77
+ except Exception as e:
78
+ print(f"⚠️ Warning: Could not remove zip file: {e}")
79
+
80
+ gource_exe = get_gource_executable()
81
+ if gource_exe.exists():
82
+ print(f"\n✅ Gource executable found at: {gource_exe}")
83
+ dll_dir = gource_exe.parent
84
+ dll_count = len(list(dll_dir.glob("*.dll")))
85
+ print(f" Found {dll_count} DLL dependencies in: {dll_dir}")
86
+ else:
87
+ print(f"\n⚠️ Warning: Expected executable not found at: {gource_exe}")
88
+ print(f" Contents of {install_dir}:")
89
+ for item in install_dir.rglob("*"):
90
+ if item.is_file():
91
+ print(f" - {item.relative_to(install_dir)}")
92
+
93
+ print("\n" + "=" * 80)
94
+ print("✅ GOURCE PORTABLE INSTALLATION COMPLETED")
95
+ print("=" * 80)
96
+ print(f"\n📌 Gource installed to: {install_dir}")
97
+ print(f" Executable: {gource_exe}")
98
+ print(" All DLL dependencies are kept together in the same directory.")
99
+ print(" This script will automatically use the portable version.")
100
+
101
+
102
+ def visualize(
103
+ repo: Annotated[str, typer.Option("--repo", "-r", help="Path to git repository to visualize")] = Path.cwd().__str__(),
104
+ output_file: Annotated[Optional[Path], typer.Option("--output", "-o", help="Output video file (e.g., output.mp4). If specified, gource will render to video.")] = None,
105
+ resolution: Annotated[str, typer.Option("--resolution", "-res", help="Video resolution (e.g., 1920x1080, 1280x720)")] = "1920x1080",
106
+ seconds_per_day: Annotated[float, typer.Option("--seconds-per-day", "-spd", help="Speed of simulation (lower = faster)")] = 0.1,
107
+ auto_skip_seconds: Annotated[float, typer.Option("--auto-skip-seconds", "-as", help="Skip to next entry if nothing happens for X seconds")] = 1.0,
108
+ title: Annotated[Optional[str], typer.Option("--title", "-t", help="Title for the visualization")] = None,
109
+ hide_items: Annotated[list[str], typer.Option("--hide", "-h", help="Items to hide: bloom, date, dirnames, files, filenames, mouse, progress, root, tree, users, usernames")] = [],
110
+ key_items: Annotated[bool, typer.Option("--key", "-k", help="Show file extension key")] = False,
111
+ fullscreen: Annotated[bool, typer.Option("--fullscreen", "-f", help="Run in fullscreen mode")] = False,
112
+ viewport: Annotated[Optional[str], typer.Option("--viewport", "-v", help="Camera viewport (e.g., '1000x1000')")] = None,
113
+ start_date: Annotated[Optional[str], typer.Option("--start-date", help="Start date (YYYY-MM-DD)")] = None,
114
+ stop_date: Annotated[Optional[str], typer.Option("--stop-date", help="Stop date (YYYY-MM-DD)")] = None,
115
+ user_image_dir: Annotated[Optional[Path], typer.Option("--user-image-dir", help="Directory with user avatar images")] = None,
116
+ max_files: Annotated[int, typer.Option("--max-files", help="Maximum number of files to show (0 = no limit)")] = 0,
117
+ max_file_lag: Annotated[float, typer.Option("--max-file-lag", help="Max time files remain on screen after last change")] = 5.0,
118
+ file_idle_time: Annotated[int, typer.Option("--file-idle-time", help="Time in seconds files remain idle before being removed")] = 0,
119
+ framerate: Annotated[int, typer.Option("--framerate", help="Frames per second for video output")] = 60,
120
+ background_color: Annotated[str, typer.Option("--background-color", help="Background color in hex (e.g., 000000 for black)")] = "000000",
121
+ font_size: Annotated[int, typer.Option("--font-size", help="Font size")] = 22,
122
+ camera_mode: Annotated[str, typer.Option("--camera-mode", help="Camera mode: overview or track")] = "overview",
123
+ ) -> None:
124
+ """
125
+ Visualize git repository history using Gource with reasonable defaults.
126
+
127
+ Examples:
128
+ # Basic visualization of current directory
129
+ python grource.py visualize
130
+
131
+ # Visualize specific repository
132
+ python grource.py visualize --repo-path /path/to/repo
133
+
134
+ # Create video output
135
+ python grource.py visualize --output output.mp4
136
+
137
+ # Fast visualization with custom title
138
+ python grource.py visualize --seconds-per-day 0.01 --title "My Project"
139
+
140
+ # Hide specific elements
141
+ python grource.py visualize --hide filenames --hide date
142
+
143
+ # Custom resolution and viewport
144
+ python grource.py visualize --resolution 2560x1440 --viewport 1200x1200
145
+ """
146
+ print("\n" + "=" * 80)
147
+ print("🎬 GOURCE VISUALIZATION 🎬")
148
+ print("=" * 80 + "\n")
149
+ repo_path: Path = Path(repo).expanduser().resolve()
150
+ if not repo_path.exists():
151
+ print(f"❌ Error: Repository path does not exist: {repo_path}")
152
+ raise typer.Exit(1)
153
+
154
+ if not repo_path.joinpath(".git").exists():
155
+ print(f"❌ Error: Not a git repository: {repo_path}")
156
+ raise typer.Exit(1)
157
+
158
+ print(f"📁 Repository: {repo_path}")
159
+ print("⚙️ Configuration:")
160
+ print(f" - Resolution: {resolution}")
161
+ print(f" - Speed: {seconds_per_day} seconds per day")
162
+ print(f" - Auto-skip: {auto_skip_seconds} seconds")
163
+ if output_file:
164
+ print(f" - Output: {output_file}")
165
+ print()
166
+
167
+ gource_exe: Path = get_gource_executable()
168
+ if not gource_exe.exists():
169
+ if platform.system() == "Windows":
170
+ print(f"⚠️ Portable gource not found at {gource_exe}, installing...")
171
+ install_gource_windows()
172
+ # Check again after installation
173
+ if gource_exe.exists():
174
+ print(f"✅ Gource installed successfully at: {gource_exe}")
175
+ gource_cmd: str = str(gource_exe)
176
+ else:
177
+ print("❌ Installation failed, falling back to system gource")
178
+ gource_cmd = "gource"
179
+ else:
180
+ gource_cmd = "gource"
181
+ print(f"⚠️ Portable gource not found at {gource_exe}, using system gource")
182
+ else:
183
+ gource_cmd = str(gource_exe)
184
+
185
+ cmd: list[str] = [gource_cmd, str(repo_path)]
186
+
187
+ cmd.extend(["--seconds-per-day", str(seconds_per_day)])
188
+ cmd.extend(["--auto-skip-seconds", str(auto_skip_seconds)])
189
+
190
+ if resolution:
191
+ width, height = resolution.split("x")
192
+ cmd.extend(["-{}x{}".format(width, height)])
193
+
194
+ if title:
195
+ cmd.extend(["--title", title])
196
+ elif not title and not output_file:
197
+ cmd.extend(["--title", repo_path.name])
198
+
199
+ for hide_item in hide_items:
200
+ cmd.extend(["--hide", hide_item])
201
+
202
+ if key_items:
203
+ cmd.append("--key")
204
+
205
+ if fullscreen and not output_file:
206
+ cmd.append("--fullscreen")
207
+
208
+ if viewport:
209
+ cmd.extend(["--viewport", viewport])
210
+
211
+ if start_date:
212
+ cmd.extend(["--start-date", start_date])
213
+
214
+ if stop_date:
215
+ cmd.extend(["--stop-date", stop_date])
216
+
217
+ if user_image_dir and user_image_dir.exists():
218
+ cmd.extend(["--user-image-dir", str(user_image_dir)])
219
+
220
+ if max_files > 0:
221
+ cmd.extend(["--max-files", str(max_files)])
222
+
223
+ cmd.extend(["--max-file-lag", str(max_file_lag)])
224
+
225
+ if file_idle_time > 0:
226
+ cmd.extend(["--file-idle-time", str(file_idle_time)])
227
+
228
+ cmd.extend(["--background-colour", background_color])
229
+ cmd.extend(["--font-size", str(font_size)])
230
+ cmd.extend(["--camera-mode", camera_mode])
231
+
232
+ if output_file:
233
+ cmd.extend(["-r", str(framerate)])
234
+ if platform.system() == "Windows":
235
+ cmd.extend(["-o", "-"])
236
+ ffmpeg_cmd: list[str] = [
237
+ "ffmpeg",
238
+ "-y",
239
+ "-r", str(framerate),
240
+ "-f", "image2pipe",
241
+ "-vcodec", "ppm",
242
+ "-i", "-",
243
+ "-vcodec", "libx264",
244
+ "-preset", "medium",
245
+ "-pix_fmt", "yuv420p",
246
+ "-crf", "23",
247
+ str(output_file),
248
+ ]
249
+ print("🎥 Rendering video...")
250
+ print(f" Command: {' '.join(cmd)} | {' '.join(ffmpeg_cmd)}")
251
+ print()
252
+ try:
253
+ gource_proc: subprocess.Popen[bytes] = subprocess.Popen(cmd, stdout=subprocess.PIPE)
254
+ ffmpeg_proc: subprocess.Popen[bytes] = subprocess.Popen(ffmpeg_cmd, stdin=gource_proc.stdout)
255
+ if gource_proc.stdout:
256
+ gource_proc.stdout.close()
257
+ ffmpeg_proc.communicate()
258
+ print(f"\n✅ Video saved to: {output_file}")
259
+ except subprocess.CalledProcessError as e:
260
+ print(f"❌ Error during video rendering: {e}")
261
+ raise typer.Exit(1)
262
+ except FileNotFoundError:
263
+ print("❌ Error: ffmpeg not found. Please install ffmpeg to create video output.")
264
+ print(" Download from: https://ffmpeg.org/download.html")
265
+ raise typer.Exit(1)
266
+ else:
267
+ cmd.extend(["-o", "-"])
268
+ ffmpeg_cmd = [
269
+ "ffmpeg",
270
+ "-y",
271
+ "-r", str(framerate),
272
+ "-f", "image2pipe",
273
+ "-vcodec", "ppm",
274
+ "-i", "-",
275
+ "-vcodec", "libx264",
276
+ "-preset", "medium",
277
+ "-pix_fmt", "yuv420p",
278
+ "-crf", "23",
279
+ str(output_file),
280
+ ]
281
+ print("🎥 Rendering video...")
282
+ print(f" Command: {' '.join(cmd)} | {' '.join(ffmpeg_cmd)}")
283
+ print()
284
+ try:
285
+ gource_proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
286
+ ffmpeg_proc = subprocess.Popen(ffmpeg_cmd, stdin=gource_proc.stdout)
287
+ if gource_proc.stdout:
288
+ gource_proc.stdout.close()
289
+ ffmpeg_proc.communicate()
290
+ print(f"\n✅ Video saved to: {output_file}")
291
+ except subprocess.CalledProcessError as e:
292
+ print(f"❌ Error during video rendering: {e}")
293
+ raise typer.Exit(1)
294
+ except FileNotFoundError:
295
+ print("❌ Error: ffmpeg not found. Please install ffmpeg to create video output.")
296
+ raise typer.Exit(1)
297
+ else:
298
+ print("🎬 Launching interactive visualization...")
299
+ print(f" Command: {' '.join(cmd)}")
300
+ print()
301
+ try:
302
+ subprocess.run(cmd, check=True)
303
+ except subprocess.CalledProcessError as e:
304
+ print(f"❌ Error running gource: {e}")
305
+ raise typer.Exit(1)
306
+ except FileNotFoundError:
307
+ print("❌ Error: gource not found. Please install gource first.")
308
+ if platform.system() == "Windows":
309
+ print(" Run: uv run python src/machineconfig/scripts/python/grource.py install")
310
+ else:
311
+ print(" For Linux/Mac, use your package manager:")
312
+ print(" - Ubuntu/Debian: sudo apt install gource")
313
+ print(" - macOS: brew install gource")
314
+ print(" - Fedora: sudo dnf install gource")
315
+ raise typer.Exit(1)
316
+
317
+ print("\n" + "=" * 80)
318
+ print("✅ VISUALIZATION COMPLETED")
319
+ print("=" * 80)
320
+
321
+
322
+ def install(
323
+ version: Optional[str] = typer.Option("0.53", "--version", "-v", help="Gource version to install"),
324
+ ) -> None:
325
+ """Install portable Gource on Windows (no admin privileges required)."""
326
+ if platform.system() == "Windows":
327
+ install_gource_windows(version=version)
328
+ else:
329
+ print(f"❌ Portable installer currently supports Windows only. Current OS: {platform.system()}")
330
+ print("For Linux/Mac, please use your package manager:")
331
+ print(" - Ubuntu/Debian: sudo apt install gource")
332
+ print(" - macOS: brew install gource")
333
+ print(" - Fedora: sudo dnf install gource")
334
+ raise typer.Exit(1)
335
+
336
+
337
+ if __name__ == "__main__":
338
+ app = typer.Typer(help="Gource visualization tool for git repositories")
339
+ app.command()(install)
340
+ app.command()(visualize)
341
+ app()
@@ -182,7 +182,7 @@ Set-Service -Name sshd -StartupType 'Automatic'"""
182
182
  console.print(Panel("💾 [bold bright_cyan]DATA RETRIEVAL[/bold bright_cyan]\n[italic]Backup restoration[/italic]", border_style="bright_cyan"))
183
183
  console.print("🔧 Retrieving backup data", style="bold cyan")
184
184
  try:
185
- from machineconfig.scripts.python.devops_backup_retrieve import main_backup_retrieve
185
+ from machineconfig.scripts.python.devops_helpers.devops_backup_retrieve import main_backup_retrieve
186
186
  main_backup_retrieve(direction="RETRIEVE")
187
187
  console.print("✅ Backup data retrieved successfully", style="bold green")
188
188
  except Exception as e:
@@ -5,128 +5,130 @@ in the event that username@github.com is not mentioned in the remote url.
5
5
 
6
6
  """
7
7
 
8
-
8
+ from pathlib import Path
9
9
  from typing import Annotated, Optional
10
10
  import typer
11
11
 
12
12
 
13
-
14
13
  app = typer.Typer(help="� Manage development repositories", no_args_is_help=True)
15
14
  sync_app = typer.Typer(help="� Manage repository specifications and syncing", no_args_is_help=True)
16
15
  app.add_typer(sync_app, name="sync", help="� Sync repositories using saved specs")
17
16
 
18
17
 
19
- DirectoryArgument = Annotated[
20
- Optional[str],
21
- typer.Argument(help="📁 Folder containing repos or the specs JSON file to use."),
22
- ]
23
- RecursiveOption = Annotated[
24
- bool,
25
- typer.Option("--recursive", "-r", help="🔍 Recurse into nested repositories."),
26
- ]
27
- NoSyncOption = Annotated[
28
- bool,
29
- typer.Option("--no-sync", help="🚫 Disable automatic uv sync after pulls."),
30
- ]
31
- CloudOption = Annotated[
32
- Optional[str],
33
- typer.Option("--cloud", "-c", help="☁️ Upload to or download from this cloud remote."),
34
- ]
35
-
18
+ DirectoryArgument = Annotated[Optional[str], typer.Argument(help="📁 Folder containing repos or the specs JSON file to use.")]
19
+ RecursiveOption = Annotated[bool, typer.Option("--recursive", "-r", help="🔍 Recurse into nested repositories.")]
20
+ NoSyncOption = Annotated[bool, typer.Option("--no-sync", help="🚫 Disable automatic uv sync after pulls.")]
21
+ CloudOption = Annotated[Optional[str], typer.Option("--cloud", "-c", help="☁️ Upload to or download from this cloud remote.")]
36
22
 
37
23
 
38
24
  @app.command(no_args_is_help=True)
39
- def push(directory: DirectoryArgument = None,
40
- recursive: RecursiveOption = False,
41
- no_sync: NoSyncOption = False,
42
- ) -> None:
25
+ def push(directory: DirectoryArgument = None, recursive: RecursiveOption = False, no_sync: NoSyncOption = False) -> None:
43
26
  """🚀 Push changes across repositories."""
44
- from machineconfig.scripts.python.repos_helper import git_operations
27
+ from machineconfig.scripts.python.repos_helpers.repos_helper import git_operations
28
+
45
29
  git_operations(directory, pull=False, commit=False, push=True, recursive=recursive, no_sync=no_sync)
30
+
31
+
46
32
  @app.command(no_args_is_help=True)
47
- def pull(
48
- directory: DirectoryArgument = None,
49
- recursive: RecursiveOption = False,
50
- no_sync: NoSyncOption = False,
51
- ) -> None:
33
+ def pull(directory: DirectoryArgument = None, recursive: RecursiveOption = False, no_sync: NoSyncOption = False) -> None:
52
34
  """⬇️ Pull changes across repositories."""
53
- from machineconfig.scripts.python.repos_helper import git_operations
35
+ from machineconfig.scripts.python.repos_helpers.repos_helper import git_operations
36
+
54
37
  git_operations(directory, pull=True, commit=False, push=False, recursive=recursive, no_sync=no_sync)
38
+
39
+
55
40
  @app.command(no_args_is_help=True)
56
- def commit(
57
- directory: DirectoryArgument = None,
58
- recursive: RecursiveOption = False,
59
- no_sync: NoSyncOption = False,
60
- ) -> None:
41
+ def commit(directory: DirectoryArgument = None, recursive: RecursiveOption = False, no_sync: NoSyncOption = False) -> None:
61
42
  """💾 Commit changes across repositories."""
62
- from machineconfig.scripts.python.repos_helper import git_operations
43
+ from machineconfig.scripts.python.repos_helpers.repos_helper import git_operations
44
+
63
45
  git_operations(directory, pull=False, commit=True, push=False, recursive=recursive, no_sync=no_sync)
46
+
47
+
64
48
  @app.command(no_args_is_help=True)
65
- def cleanup(
66
- directory: DirectoryArgument = None,
67
- recursive: RecursiveOption = False,
68
- no_sync: NoSyncOption = False,
69
- ) -> None:
49
+ def cleanup(directory: DirectoryArgument = None, recursive: RecursiveOption = False, no_sync: NoSyncOption = False) -> None:
70
50
  """🔄 Pull, commit, and push changes across repositories."""
71
- from machineconfig.scripts.python.repos_helper import git_operations
51
+ from machineconfig.scripts.python.repos_helpers.repos_helper import git_operations
52
+
72
53
  git_operations(directory, pull=True, commit=True, push=True, recursive=recursive, no_sync=no_sync)
73
54
 
74
55
 
75
56
  @sync_app.command(no_args_is_help=True)
76
- def capture(
77
- directory: DirectoryArgument = None,
78
- cloud: CloudOption = None,
79
- ) -> None:
57
+ def capture(directory: DirectoryArgument = None, cloud: CloudOption = None) -> None:
80
58
  """📝 Record repositories into a repos.json specification."""
81
- from machineconfig.scripts.python.repos_helper import print_banner, resolve_directory
82
- print_banner()
59
+ from machineconfig.scripts.python.repos_helpers.repos_helper import resolve_directory
83
60
  repos_root = resolve_directory(directory)
84
- from machineconfig.scripts.python.repos_helper_record import main as record_repos
61
+ from machineconfig.scripts.python.repos_helpers.repos_helper_record import main as record_repos
62
+
85
63
  save_path = record_repos(repos_root=repos_root)
86
64
  from machineconfig.utils.path_extended import PathExtended
65
+
87
66
  if cloud is not None:
88
67
  PathExtended(save_path).to_cloud(rel2home=True, cloud=cloud)
68
+
69
+
89
70
  @sync_app.command(no_args_is_help=True)
90
- def clone(
91
- directory: DirectoryArgument = None,
92
- cloud: CloudOption = None,
93
- ) -> None:
71
+ def clone(directory: DirectoryArgument = None, cloud: CloudOption = None) -> None:
94
72
  """📥 Clone repositories described by a repos.json specification."""
95
- from machineconfig.scripts.python.repos_helper import print_banner, clone_from_specs
96
- print_banner()
73
+ from machineconfig.scripts.python.repos_helpers.repos_helper import clone_from_specs
74
+
75
+
97
76
  clone_from_specs(directory, cloud, checkout_branch_flag=False, checkout_commit_flag=False)
98
77
 
99
78
 
100
79
  @sync_app.command(name="checkout-to-commit", no_args_is_help=True)
101
- def checkout_command(
102
- directory: DirectoryArgument = None,
103
- cloud: CloudOption = None,
104
- ) -> None:
80
+ def checkout_command(directory: DirectoryArgument = None, cloud: CloudOption = None) -> None:
105
81
  """🔀 Check out specific commits listed in the specification."""
106
- from machineconfig.scripts.python.repos_helper import print_banner, clone_from_specs
107
- print_banner()
82
+ from machineconfig.scripts.python.repos_helpers.repos_helper import clone_from_specs
83
+
84
+
108
85
  clone_from_specs(directory, cloud, checkout_branch_flag=False, checkout_commit_flag=True)
109
86
 
110
87
 
111
88
  @sync_app.command(name="checkout-to-branch", no_args_is_help=True)
112
- def checkout_to_branch_command(
113
- directory: DirectoryArgument = None,
114
- cloud: CloudOption = None,
115
- ) -> None:
89
+ def checkout_to_branch_command(directory: DirectoryArgument = None, cloud: CloudOption = None) -> None:
116
90
  """🔀 Check out to the main branch defined in the specification."""
117
- from machineconfig.scripts.python.repos_helper import print_banner, clone_from_specs
118
- print_banner()
91
+ from machineconfig.scripts.python.repos_helpers.repos_helper import clone_from_specs
119
92
  clone_from_specs(directory, cloud, checkout_branch_flag=True, checkout_commit_flag=False)
120
93
 
121
94
 
122
95
  @app.command(no_args_is_help=True)
123
- def analyze(
124
- directory: DirectoryArgument = None,
125
- ) -> None:
96
+ def analyze(directory: DirectoryArgument = None) -> None:
126
97
  """📊 Analyze repository development over time."""
127
- from machineconfig.scripts.python.repos_helper import print_banner
128
- print_banner()
129
98
  repo_path = directory if directory is not None else "."
130
- from machineconfig.scripts.python.count_lines_frontend import analyze_repo_development
99
+ from machineconfig.scripts.python.repos_helpers.count_lines_frontend import analyze_repo_development
100
+
131
101
  analyze_repo_development(repo_path=repo_path)
132
102
 
103
+
104
+ @app.command(no_args_is_help=True)
105
+ def viz(
106
+ repo: str = typer.Option(Path.cwd().__str__(), "--repo", "-r", help="Path to git repository to visualize"),
107
+ output_file: Optional[Path] = typer.Option(None, "--output", "-o", help="Output video file (e.g., output.mp4). If specified, gource will render to video."),
108
+ resolution: str = typer.Option("1920x1080", "--resolution", "-res", help="Video resolution (e.g., 1920x1080, 1280x720)"),
109
+ seconds_per_day: float = typer.Option(0.1, "--seconds-per-day", "-spd", help="Speed of simulation (lower = faster)"),
110
+ auto_skip_seconds: float = typer.Option(1.0, "--auto-skip-seconds", "-as", help="Skip to next entry if nothing happens for X seconds"),
111
+ title: Optional[str] = typer.Option(None, "--title", "-t", help="Title for the visualization"),
112
+ hide_items: list[str] = typer.Option([], "--hide", "-h", help="Items to hide: bloom, date, dirnames, files, filenames, mouse, progress, root, tree, users, usernames"),
113
+ key_items: bool = typer.Option(False, "--key", "-k", help="Show file extension key"),
114
+ fullscreen: bool = typer.Option(False, "--fullscreen", "-f", help="Run in fullscreen mode"),
115
+ viewport: Optional[str] = typer.Option(None, "--viewport", "-v", help="Camera viewport (e.g., '1000x1000')"),
116
+ start_date: Optional[str] = typer.Option(None, "--start-date", help="Start date (YYYY-MM-DD)"),
117
+ stop_date: Optional[str] = typer.Option(None, "--stop-date", help="Stop date (YYYY-MM-DD)"),
118
+ user_image_dir: Optional[Path] = typer.Option(None, "--user-image-dir", help="Directory with user avatar images"),
119
+ max_files: int = typer.Option(0, "--max-files", help="Maximum number of files to show (0 = no limit)"),
120
+ max_file_lag: float = typer.Option(5.0, "--max-file-lag", help="Max time files remain on screen after last change"),
121
+ file_idle_time: int = typer.Option(0, "--file-idle-time", help="Time in seconds files remain idle before being removed"),
122
+ framerate: int = typer.Option(60, "--framerate", help="Frames per second for video output"),
123
+ background_color: str = typer.Option("000000", "--background-color", help="Background color in hex (e.g., 000000 for black)"),
124
+ font_size: int = typer.Option(22, "--font-size", help="Font size"),
125
+ camera_mode: str = typer.Option("overview", "--camera-mode", help="Camera mode: overview or track"),
126
+ ) -> None:
127
+ """🎬 Visualize repository activity using Gource."""
128
+ from machineconfig.scripts.python.helpers_repos.grource import visualize
129
+ visualize(repo=repo, output_file=output_file, resolution=resolution, seconds_per_day=seconds_per_day,
130
+ auto_skip_seconds=auto_skip_seconds, title=title, hide_items=hide_items, key_items=key_items,
131
+ fullscreen=fullscreen, viewport=viewport, start_date=start_date, stop_date=stop_date,
132
+ user_image_dir=user_image_dir, max_files=max_files, max_file_lag=max_file_lag,
133
+ file_idle_time=file_idle_time, framerate=framerate, background_color=background_color,
134
+ font_size=font_size, camera_mode=camera_mode)
@@ -3,7 +3,7 @@ import typer
3
3
 
4
4
 
5
5
  def analyze_repo_development(repo_path: str = typer.Argument(..., help="Path to the git repository")):
6
- from machineconfig.scripts.python import count_lines
6
+ from machineconfig.scripts.python.repos_helpers import count_lines
7
7
  from pathlib import Path
8
8
  count_lines_path = Path(count_lines.__file__)
9
9
  # --project $HOME/code/machineconfig --group plot
@@ -8,14 +8,6 @@ from machineconfig.utils.source_of_truth import CONFIG_PATH, DEFAULTS_PATH
8
8
  import typer
9
9
 
10
10
 
11
-
12
- def print_banner() -> None:
13
- typer.echo("\n" + "=" * 50)
14
- typer.echo("📂 Welcome to the Repository Manager")
15
- typer.echo("=" * 50 + "\n")
16
-
17
-
18
-
19
11
  def resolve_directory(directory: Optional[str]) -> Path:
20
12
  if directory is None:
21
13
  directory = Path.cwd().as_posix()
@@ -30,10 +22,10 @@ def git_operations(
30
22
  recursive: bool,
31
23
  no_sync: bool,
32
24
  ) -> None:
33
- print_banner()
25
+
34
26
  repos_root = resolve_directory(directory)
35
27
  auto_sync = not no_sync
36
- from machineconfig.scripts.python.repos_helper_action import perform_git_operations
28
+ from machineconfig.scripts.python.repos_helpers.repos_helper_action import perform_git_operations
37
29
  from machineconfig.utils.path_extended import PathExtended
38
30
  perform_git_operations(
39
31
  repos_root=PathExtended(repos_root),
@@ -73,10 +65,10 @@ def clone_from_specs(
73
65
  checkout_branch_flag: bool,
74
66
  checkout_commit_flag: bool,
75
67
  ) -> None:
76
- print_banner()
68
+
77
69
  typer.echo("\n📥 Cloning or checking out repositories...")
78
70
  spec_path = resolve_spec_path(directory, cloud)
79
- from machineconfig.scripts.python.repos_helper_clone import clone_repos
71
+ from machineconfig.scripts.python.repos_helpers.repos_helper_clone import clone_repos
80
72
  clone_repos(
81
73
  spec_path=spec_path,
82
74
  preferred_remote=None,
@@ -1,6 +1,6 @@
1
1
  from machineconfig.utils.path_extended import PathExtended
2
2
  from machineconfig.utils.accessories import randstr
3
- from machineconfig.scripts.python.repos_helper_update import update_repository
3
+ from machineconfig.scripts.python.repos_helpers.repos_helper_update import update_repository
4
4
 
5
5
  from typing import Optional
6
6
  from dataclasses import dataclass
@@ -41,7 +41,7 @@ def create_from_function(
41
41
 
42
42
  # ========================= choosing function to run
43
43
  if function is None or function.strip() == "":
44
- from machineconfig.scripts.python.fire_jobs_route_helper import choose_function_or_lines
44
+ from machineconfig.scripts.python.helpers_fire_command.fire_jobs_route_helper import choose_function_or_lines
45
45
  choice_function, choice_file, _kwargs_dict = choose_function_or_lines(choice_file, kwargs_dict={})
46
46
  else:
47
47
  choice_function = function
File without changes