machineconfig 5.24__py3-none-any.whl → 5.26__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.
- machineconfig/jobs/installer/custom/hx.py +1 -1
- machineconfig/jobs/installer/custom_dev/alacritty.py +4 -4
- machineconfig/jobs/installer/linux_scripts/wezterm.sh +1 -1
- machineconfig/scripts/python/agents.py +4 -4
- machineconfig/scripts/python/choose_wezterm_theme.py +2 -2
- machineconfig/scripts/python/devops.py +6 -6
- machineconfig/scripts/python/{devops_update_repos.py → devops_helpers/devops_update_repos.py} +1 -1
- machineconfig/scripts/python/fire_jobs.py +3 -3
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +14 -1
- machineconfig/scripts/python/helpers_fire/__init__.py +0 -0
- machineconfig/scripts/python/{fire_agents_help_launch.py → helpers_fire/fire_agents_help_launch.py} +1 -1
- machineconfig/scripts/python/helpers_fire_command/__init__.py +0 -0
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_streamlit_helper.py +0 -0
- machineconfig/scripts/python/helpers_repos/grource.py +341 -0
- machineconfig/scripts/python/interactive.py +11 -19
- machineconfig/scripts/python/repos.py +75 -73
- machineconfig/scripts/python/{count_lines_frontend.py → repos_helpers/count_lines_frontend.py} +1 -1
- machineconfig/scripts/python/{repos_helper.py → repos_helpers/repos_helper.py} +4 -12
- machineconfig/scripts/python/{repos_helper_action.py → repos_helpers/repos_helper_action.py} +1 -1
- machineconfig/scripts/python/sessions_multiprocess.py +1 -1
- machineconfig/utils/files/ouch/__init__.py +0 -0
- machineconfig/utils/files/ouch/decompress.py +45 -0
- machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
- machineconfig/utils/source_of_truth.py +0 -1
- machineconfig/utils/ssh.py +33 -19
- {machineconfig-5.24.dist-info → machineconfig-5.26.dist-info}/METADATA +3 -5
- {machineconfig-5.24.dist-info → machineconfig-5.26.dist-info}/RECORD +46 -42
- machineconfig/scripts/python/get_zellij_cmd.py +0 -15
- machineconfig/scripts/python/t4.py +0 -17
- /machineconfig/jobs/{python → installer}/check_installations.py +0 -0
- /machineconfig/scripts/python/{fire_jobs_streamlit_helper.py → devops_helpers/__init__.py} +0 -0
- /machineconfig/scripts/python/{devops_add_identity.py → devops_helpers/devops_add_identity.py} +0 -0
- /machineconfig/scripts/python/{devops_add_ssh_key.py → devops_helpers/devops_add_ssh_key.py} +0 -0
- /machineconfig/scripts/python/{devops_backup_retrieve.py → devops_helpers/devops_backup_retrieve.py} +0 -0
- /machineconfig/scripts/python/{devops_status.py → devops_helpers/devops_status.py} +0 -0
- /machineconfig/scripts/python/{fire_agents_help_search.py → helpers_fire/fire_agents_help_search.py} +0 -0
- /machineconfig/scripts/python/{fire_agents_helper_types.py → helpers_fire/fire_agents_helper_types.py} +0 -0
- /machineconfig/scripts/python/{fire_agents_load_balancer.py → helpers_fire/fire_agents_load_balancer.py} +0 -0
- /machineconfig/scripts/python/{cloud_manager.py → helpers_fire_command/cloud_manager.py} +0 -0
- /machineconfig/scripts/python/{fire_jobs_args_helper.py → helpers_fire_command/fire_jobs_args_helper.py} +0 -0
- /machineconfig/scripts/python/{fire_jobs_route_helper.py → helpers_fire_command/fire_jobs_route_helper.py} +0 -0
- /machineconfig/scripts/python/{count_lines.py → repos_helpers/count_lines.py} +0 -0
- /machineconfig/scripts/python/{repos_helper_clone.py → repos_helpers/repos_helper_clone.py} +0 -0
- /machineconfig/scripts/python/{repos_helper_record.py → repos_helpers/repos_helper_record.py} +0 -0
- /machineconfig/scripts/python/{repos_helper_update.py → repos_helpers/repos_helper_update.py} +0 -0
- {machineconfig-5.24.dist-info → machineconfig-5.26.dist-info}/WHEEL +0 -0
- {machineconfig-5.24.dist-info → machineconfig-5.26.dist-info}/entry_points.txt +0 -0
- {machineconfig-5.24.dist-info → machineconfig-5.26.dist-info}/top_level.txt +0 -0
|
@@ -77,7 +77,7 @@ def main(version: Optional[str], install_lib: bool = False):
|
|
|
77
77
|
print(f" ✨ Cleaned '{runtime_path}' and '{contrib_path}'.")
|
|
78
78
|
|
|
79
79
|
print("\n📦 [Step 4/5] Installing Helix components...")
|
|
80
|
-
target_config_dir = PathExtended("
|
|
80
|
+
target_config_dir = PathExtended.home().joinpath(".config/helix").expanduser()
|
|
81
81
|
target_config_dir.mkdir(parents=True, exist_ok=True)
|
|
82
82
|
|
|
83
83
|
if platform.system() in ["Linux", "Darwin"]:
|
|
@@ -30,8 +30,8 @@ def main(installer_data: InstallerData, version: Optional[str]) -> None:
|
|
|
30
30
|
program = """
|
|
31
31
|
|
|
32
32
|
cargo install alacritty
|
|
33
|
-
mkdir -p
|
|
34
|
-
git clone https://github.com/alacritty/alacritty-theme
|
|
33
|
+
mkdir -p $HOME/.config/alacritty/themes
|
|
34
|
+
git clone https://github.com/alacritty/alacritty-theme $HOME/.config/alacritty/themes
|
|
35
35
|
|
|
36
36
|
"""
|
|
37
37
|
elif platform.system() in ["Linux", "Darwin"]:
|
|
@@ -41,8 +41,8 @@ git clone https://github.com/alacritty/alacritty-theme ~/.config/alacritty/theme
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
cargo install alacritty
|
|
44
|
-
mkdir -p
|
|
45
|
-
git clone https://github.com/alacritty/alacritty-theme
|
|
44
|
+
mkdir -p $HOME/.config/alacritty/themes
|
|
45
|
+
git clone https://github.com/alacritty/alacritty-theme $HOME/.config/alacritty/themes
|
|
46
46
|
|
|
47
47
|
"""
|
|
48
48
|
else:
|
|
@@ -36,5 +36,5 @@ echo """#=======================================================================
|
|
|
36
36
|
#=======================================================================
|
|
37
37
|
"""
|
|
38
38
|
echo "🚀 You can now launch WezTerm from your applications menu or by typing 'wezterm' in terminal"
|
|
39
|
-
echo "💡 Configure WezTerm by editing
|
|
39
|
+
echo "💡 Configure WezTerm by editing $HOME/.config/wezterm/wezterm.lua"
|
|
40
40
|
echo "🔗 For more information and configuration options, visit: https://wezfurlong.org/wezterm/"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import cast, Iterable, Optional, get_args
|
|
7
7
|
import typer
|
|
8
|
-
from machineconfig.scripts.python.fire_agents_helper_types import AGENTS
|
|
8
|
+
from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import AGENTS
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def _write_list_file(target: Path, files: Iterable[Path]) -> None:
|
|
@@ -28,9 +28,9 @@ def create(
|
|
|
28
28
|
agents_dir: Optional[Path] = typer.Option(None, help="Directory to store agent files. If not provided, will be constructed automatically."),
|
|
29
29
|
):
|
|
30
30
|
|
|
31
|
-
from machineconfig.scripts.python.fire_agents_help_launch import prep_agent_launch, get_agents_launch_layout
|
|
32
|
-
from machineconfig.scripts.python.fire_agents_help_search import search_files_by_pattern, search_python_files
|
|
33
|
-
from machineconfig.scripts.python.fire_agents_load_balancer import chunk_prompts
|
|
31
|
+
from machineconfig.scripts.python.helpers_fire.fire_agents_help_launch import prep_agent_launch, get_agents_launch_layout
|
|
32
|
+
from machineconfig.scripts.python.helpers_fire.fire_agents_help_search import search_files_by_pattern, search_python_files
|
|
33
|
+
from machineconfig.scripts.python.helpers_fire.fire_agents_load_balancer import chunk_prompts
|
|
34
34
|
from machineconfig.utils.accessories import get_repo_root, randstr
|
|
35
35
|
import json
|
|
36
36
|
|
|
@@ -52,14 +52,14 @@ def main2():
|
|
|
52
52
|
|
|
53
53
|
def set_theme(theme: str):
|
|
54
54
|
print(f"🔄 Setting WezTerm theme to: {theme}")
|
|
55
|
-
txt_lines = PathExtended("
|
|
55
|
+
txt_lines = PathExtended.home().joinpath(".config/wezterm/wezterm.lua").expanduser().read_text(encoding="utf-8").splitlines()
|
|
56
56
|
res_lines = []
|
|
57
57
|
for line in txt_lines:
|
|
58
58
|
if "config.color_scheme = " in line:
|
|
59
59
|
res_lines.append(f"config.color_scheme = '{theme}'")
|
|
60
60
|
else:
|
|
61
61
|
res_lines.append(line)
|
|
62
|
-
PathExtended("
|
|
62
|
+
PathExtended.home().joinpath(".config/wezterm/wezterm.lua").expanduser().write_text("\n".join(res_lines), encoding="utf-8")
|
|
63
63
|
time.sleep(0.1)
|
|
64
64
|
print("💾 Configuration saved")
|
|
65
65
|
|
|
@@ -34,7 +34,7 @@ app.add_typer(self_app, name="self")
|
|
|
34
34
|
@self_app.command()
|
|
35
35
|
def update():
|
|
36
36
|
"""🔄 UPDATE essential repos"""
|
|
37
|
-
import machineconfig.scripts.python.devops_update_repos as helper
|
|
37
|
+
import machineconfig.scripts.python.devops_helpers.devops_update_repos as helper
|
|
38
38
|
helper.main()
|
|
39
39
|
@self_app.command()
|
|
40
40
|
def interactive():
|
|
@@ -44,7 +44,7 @@ def interactive():
|
|
|
44
44
|
@self_app.command()
|
|
45
45
|
def status():
|
|
46
46
|
"""📊 STATUS of machine, shell profile, apps, symlinks, dotfiles, etc."""
|
|
47
|
-
import machineconfig.scripts.python.devops_status as helper
|
|
47
|
+
import machineconfig.scripts.python.devops_helpers.devops_status as helper
|
|
48
48
|
helper.main()
|
|
49
49
|
@self_app.command()
|
|
50
50
|
def clone():
|
|
@@ -100,12 +100,12 @@ def shell(method: Annotated[Literal["copy", "reference"], typer.Argument(help="C
|
|
|
100
100
|
@nw_apps.command()
|
|
101
101
|
def add_key():
|
|
102
102
|
"""🔑 SSH add pub key to this machine"""
|
|
103
|
-
import machineconfig.scripts.python.devops_add_ssh_key as helper
|
|
103
|
+
import machineconfig.scripts.python.devops_helpers.devops_add_ssh_key as helper
|
|
104
104
|
helper.main()
|
|
105
105
|
@nw_apps.command()
|
|
106
106
|
def add_identity():
|
|
107
107
|
"""🗝️ SSH add identity (private key) to this machine"""
|
|
108
|
-
import machineconfig.scripts.python.devops_add_identity as helper
|
|
108
|
+
import machineconfig.scripts.python.devops_helpers.devops_add_identity as helper
|
|
109
109
|
helper.main()
|
|
110
110
|
@nw_apps.command()
|
|
111
111
|
def connect():
|
|
@@ -131,14 +131,14 @@ def setup():
|
|
|
131
131
|
@app_data.command()
|
|
132
132
|
def backup():
|
|
133
133
|
"""💾 BACKUP"""
|
|
134
|
-
from machineconfig.scripts.python.devops_backup_retrieve import main_backup_retrieve
|
|
134
|
+
from machineconfig.scripts.python.devops_helpers.devops_backup_retrieve import main_backup_retrieve
|
|
135
135
|
main_backup_retrieve(direction="BACKUP")
|
|
136
136
|
|
|
137
137
|
|
|
138
138
|
@app_data.command()
|
|
139
139
|
def retrieve():
|
|
140
140
|
"""📥 RETRIEVE"""
|
|
141
|
-
from machineconfig.scripts.python.devops_backup_retrieve import main_backup_retrieve
|
|
141
|
+
from machineconfig.scripts.python.devops_helpers.devops_backup_retrieve import main_backup_retrieve
|
|
142
142
|
main_backup_retrieve(direction="RETRIEVE")
|
|
143
143
|
|
|
144
144
|
|
machineconfig/scripts/python/{devops_update_repos.py → devops_helpers/devops_update_repos.py}
RENAMED
|
@@ -8,7 +8,7 @@ from rich.panel import Panel
|
|
|
8
8
|
from rich.table import Table
|
|
9
9
|
from rich.text import Text
|
|
10
10
|
|
|
11
|
-
from machineconfig.scripts.python.repos_helper_update import RepositoryUpdateResult, run_uv_sync, update_repository
|
|
11
|
+
from machineconfig.scripts.python.repos_helpers.repos_helper_update import RepositoryUpdateResult, run_uv_sync, update_repository
|
|
12
12
|
from machineconfig.utils.io import read_ini
|
|
13
13
|
from machineconfig.utils.source_of_truth import DEFAULTS_PATH
|
|
14
14
|
|
|
@@ -12,7 +12,7 @@ from machineconfig.utils.options import choose_from_options
|
|
|
12
12
|
from machineconfig.utils.path_helper import match_file_name, sanitize_path
|
|
13
13
|
from machineconfig.utils.path_extended import PathExtended
|
|
14
14
|
from machineconfig.utils.accessories import get_repo_root, randstr
|
|
15
|
-
from machineconfig.scripts.python.fire_jobs_args_helper import FireJobArgs, extract_kwargs, parse_fire_args_from_context
|
|
15
|
+
from machineconfig.scripts.python.helpers_fire_command.fire_jobs_args_helper import FireJobArgs, extract_kwargs, parse_fire_args_from_context
|
|
16
16
|
|
|
17
17
|
import platform
|
|
18
18
|
from typing import Optional, Annotated
|
|
@@ -55,13 +55,13 @@ def route(args: FireJobArgs, fire_args: str = "") -> None:
|
|
|
55
55
|
# ========================= choosing function to run
|
|
56
56
|
choice_function: Optional[str] = None # Initialize to avoid unbound variable
|
|
57
57
|
if args.choose_function:
|
|
58
|
-
from machineconfig.scripts.python.fire_jobs_route_helper import choose_function_or_lines
|
|
58
|
+
from machineconfig.scripts.python.helpers_fire_command.fire_jobs_route_helper import choose_function_or_lines
|
|
59
59
|
choice_function, choice_file, kwargs_dict = choose_function_or_lines(choice_file, kwargs_dict)
|
|
60
60
|
else:
|
|
61
61
|
choice_function = args.function
|
|
62
62
|
|
|
63
63
|
if choice_file.suffix == ".py":
|
|
64
|
-
from machineconfig.scripts.python.fire_jobs_route_helper import get_command_streamlit
|
|
64
|
+
from machineconfig.scripts.python.helpers_fire_command.fire_jobs_route_helper import get_command_streamlit
|
|
65
65
|
if args.streamlit: exe = get_command_streamlit(choice_file, args.environment, repo_root)
|
|
66
66
|
elif args.interactive is False: exe = "python"
|
|
67
67
|
elif args.jupyter: exe = "jupyter-lab"
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
from machineconfig.utils.path_extended import PathExtended
|
|
2
|
-
from machineconfig.scripts.python.get_zellij_cmd import get_zellij_cmd
|
|
3
2
|
from machineconfig.utils.code import write_shell_script_to_file
|
|
4
3
|
import platform
|
|
4
|
+
from pathlib import Path
|
|
5
5
|
from rich.console import Console
|
|
6
6
|
from rich.panel import Panel
|
|
7
7
|
|
|
8
8
|
console = Console()
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
def get_zellij_cmd(wd1: Path, wd2: Path) -> str:
|
|
12
|
+
_ = wd1, wd2
|
|
13
|
+
lines = [
|
|
14
|
+
""" zellij action new-tab --name gitdiff""",
|
|
15
|
+
"""zellij action new-pane --direction down --name local --cwd ./data """,
|
|
16
|
+
"""zellij action write-chars "cd '{wd1}'; git status" """,
|
|
17
|
+
"""zellij action move-focus up; zellij action close-pane """,
|
|
18
|
+
"""zellij action new-pane --direction down --name remote --cwd code """,
|
|
19
|
+
"""zellij action write-chars "cd '{wd2}' """,
|
|
20
|
+
"""git status" """,
|
|
21
|
+
]
|
|
22
|
+
return "; ".join(lines)
|
|
23
|
+
|
|
11
24
|
def delete_remote_repo_copy_and_push_local(remote_repo: str, local_repo: str, cloud: str):
|
|
12
25
|
console.print(Panel("🗑️ Deleting remote repo copy and pushing local copy", title="[bold blue]Repo Sync[/bold blue]", border_style="blue"))
|
|
13
26
|
repo_sync_root = PathExtended(remote_repo).expanduser().absolute()
|
|
File without changes
|
machineconfig/scripts/python/{fire_agents_help_launch.py → helpers_fire/fire_agents_help_launch.py}
RENAMED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import random
|
|
3
3
|
import shlex
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from machineconfig.scripts.python.fire_agents_helper_types import AGENTS, AGENT_NAME_FORMATTER
|
|
5
|
+
from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import AGENTS, AGENT_NAME_FORMATTER
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def get_gemini_api_keys() -> list[str]:
|
|
File without changes
|
|
File without changes
|
|
@@ -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()
|
|
@@ -36,7 +36,6 @@ console = Console()
|
|
|
36
36
|
def display_header() -> None:
|
|
37
37
|
from machineconfig.utils.installer import get_machineconfig_version
|
|
38
38
|
from rich.align import Align
|
|
39
|
-
from rich.padding import Padding
|
|
40
39
|
|
|
41
40
|
# Fancy ASCII art header
|
|
42
41
|
ascii_art = """
|
|
@@ -57,23 +56,16 @@ def display_header() -> None:
|
|
|
57
56
|
subtitle = "🎯 Your digital life manager. Dotfiles, data, code and more."
|
|
58
57
|
bug_report = "🐛 Please report bugs to Alex Al-Saffar @ https://github.com/thisismygitrepo/machineconfig"
|
|
59
58
|
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
console.print(Align.center(title_text))
|
|
71
|
-
console.print(Align.center(subtitle_text))
|
|
72
|
-
console.print()
|
|
73
|
-
console.print(Align.center(bug_text))
|
|
74
|
-
|
|
75
|
-
panel_content = capture.get()
|
|
76
|
-
console.print(Panel(Padding(panel_content, (1, 2)), border_style="bright_cyan", padding=(1, 2)))
|
|
59
|
+
# Print ASCII art
|
|
60
|
+
console.print(Text(ascii_art, style="bold cyan"))
|
|
61
|
+
console.print()
|
|
62
|
+
|
|
63
|
+
# Print centered text elements
|
|
64
|
+
console.print(Align.center(Text(title, style="bold bright_magenta")))
|
|
65
|
+
console.print(Align.center(Text(subtitle, style="italic bright_blue")))
|
|
66
|
+
console.print()
|
|
67
|
+
console.print(Align.center(Text(bug_report, style="dim white")))
|
|
68
|
+
console.print()
|
|
77
69
|
def display_completion_message() -> None:
|
|
78
70
|
completion_text = Text("INSTALLATION COMPLETE", style="bold green")
|
|
79
71
|
subtitle_text = Text("System setup finished successfully", style="italic green")
|
|
@@ -190,7 +182,7 @@ Set-Service -Name sshd -StartupType 'Automatic'"""
|
|
|
190
182
|
console.print(Panel("💾 [bold bright_cyan]DATA RETRIEVAL[/bold bright_cyan]\n[italic]Backup restoration[/italic]", border_style="bright_cyan"))
|
|
191
183
|
console.print("🔧 Retrieving backup data", style="bold cyan")
|
|
192
184
|
try:
|
|
193
|
-
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
|
|
194
186
|
main_backup_retrieve(direction="RETRIEVE")
|
|
195
187
|
console.print("✅ Backup data retrieved successfully", style="bold green")
|
|
196
188
|
except Exception as e:
|