machineconfig 4.8__py3-none-any.whl → 4.9__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.

@@ -0,0 +1,154 @@
1
+ from typing import Literal, TypeAlias, Union
2
+
3
+ PACKAGES_NAMES_DEV = Literal[
4
+ "ngrok",
5
+ "Visual Studio Code",
6
+ "aider",
7
+ "github-copilot-cli",
8
+ "gemini",
9
+ "crush",
10
+ "opencode-ai",
11
+ "warp",
12
+ "q",
13
+ "cursor-cli",
14
+ "droid",
15
+ "Alacritty",
16
+ "Brave",
17
+ "bypass-paywalls-chrome",
18
+ "Cursor",
19
+ "espanso",
20
+ "Gorilla",
21
+ "lvim",
22
+ "nerdfont",
23
+ "Redis",
24
+ "Wezterm",
25
+ "winget",
26
+ "ytui-music",
27
+ "youtube-tui",
28
+ "termusic",
29
+ "kronos",
30
+ "transmission",
31
+ "nnn",
32
+ "exa",
33
+ "bytehound",
34
+ "atuin",
35
+ "browsh",
36
+ "carbonyl",
37
+ "patat",
38
+ "SqliteBrowser",
39
+ "DBeaver",
40
+ "rainfrog",
41
+ "duckdb",
42
+ "cpz",
43
+ "rmz",
44
+ "mermaid-cli",
45
+ "html2markdown",
46
+ "xcrawl3r",
47
+ "obsidian",
48
+ "marp",
49
+ "presenterm",
50
+ "pandoc",
51
+ "devcontainer",
52
+ "bitwarden",
53
+ "OBS Background removal",
54
+ "rustdesk",
55
+ "evcxr",
56
+ "forward-cli",
57
+ "bandwhich",
58
+ "ipinfo",
59
+ "dust",
60
+ "ots",
61
+ "ffsend",
62
+ "portal",
63
+ "qrcp",
64
+ "qrscan",
65
+ "termscp",
66
+ "qr",
67
+ "filebrowser",
68
+ "cloudreve",
69
+ "restic",
70
+ "syncthing",
71
+ "istio",
72
+ "openpomodoro-cli",
73
+ "rust-analyzer",
74
+ "kondo",
75
+ "tokei",
76
+ "lazygit",
77
+ "lazydocker",
78
+ "onefetch",
79
+ "gitcs",
80
+ "just",
81
+ "navi",
82
+ "tealdeer",
83
+ "sniffnet",
84
+ "hyperfine",
85
+ "ollama",
86
+ "cointop",
87
+ "vtm",
88
+ "edex-ui",
89
+ "extraterm",
90
+ "nushell",
91
+ "geckodriver",
92
+ "lolcatjs",
93
+ "figlet-cli",
94
+ "sharewifi",
95
+ "share-wifi",
96
+ ]
97
+
98
+ PACKAGES_NAMES_ESSENTIAL = Literal[
99
+ "gh",
100
+ "hx",
101
+ "speedtest",
102
+ "pistol",
103
+ "diskonaut",
104
+ "xplr",
105
+ "btop",
106
+ "gotty",
107
+ "joshuto",
108
+ "zellij",
109
+ "boxes",
110
+ "ugrep",
111
+ "zoomit",
112
+ "ntop",
113
+ "devtunnel",
114
+ "bat",
115
+ "broot",
116
+ "btm",
117
+ "chatgpt",
118
+ "cloudflared",
119
+ "cpufetch",
120
+ "delta",
121
+ "dua",
122
+ "fastfetch",
123
+ "fd",
124
+ "fzf",
125
+ "gitui",
126
+ "glow",
127
+ "gum",
128
+ "lf",
129
+ "lsd",
130
+ "m365",
131
+ "mcfly",
132
+ "mods",
133
+ "mprocs",
134
+ "ouch",
135
+ "procs",
136
+ "rclone",
137
+ "rg",
138
+ "rga",
139
+ "starship",
140
+ "tere",
141
+ "topgrade",
142
+ "ttyd",
143
+ "viu",
144
+ "watchexec",
145
+ "yazi",
146
+ "zoxide",
147
+ ]
148
+
149
+ PACKAGE_GROUPS: TypeAlias = Literal["ESSENTIAL", "DEV"]
150
+ PACKAGE_GROUP2NAMES: dict[PACKAGE_GROUPS, list[str]] = {
151
+ "ESSENTIAL": list(PACKAGES_NAMES_ESSENTIAL.__args__),
152
+ "DEV": list(PACKAGES_NAMES_DEV.__args__),
153
+ }
154
+ _ = Union
@@ -2,10 +2,15 @@
2
2
 
3
3
  import typer
4
4
  from rich.progress import Progress, SpinnerColumn, TextColumn
5
+ from rich.console import Console
6
+ from rich.panel import Panel
7
+ from rich.table import Table
5
8
  from platform import system
6
9
  from typing import Optional, Literal, TypeAlias, cast, get_args, Annotated
7
10
 
8
- WHICH_CAT: TypeAlias = Literal["essentials", "essentialsDev", "systymPackages", "precheckedPackages", "ia"]
11
+ console = Console()
12
+
13
+ WHICH_CAT: TypeAlias = Literal["essentials", "essentialsDev", "systemPackages", "precheckedPackages", "ia"]
9
14
 
10
15
 
11
16
  def _handle_installer_not_found(search_term: str, all_installers: list["InstallerData"]) -> None: # type: ignore
@@ -21,27 +26,37 @@ def _handle_installer_not_found(search_term: str, all_installers: list["Installe
21
26
  # Find close matches using fuzzy matching
22
27
  close_matches = get_close_matches(search_term, all_names, n=5, cutoff=0.4)
23
28
 
24
- print(f"\n❌ '{search_term}' was not found.")
29
+ console.print(f"\n❌ '[red]{search_term}[/red]' was not found.", style="bold")
25
30
 
26
31
  if close_matches:
27
- print("🤔 Did you mean one of these?")
32
+ console.print("🤔 Did you mean one of these?", style="yellow")
33
+ table = Table(show_header=False, box=None, pad_edge=False)
28
34
  for i, match in enumerate(close_matches, 1):
29
- print(f" {i}. {match}")
35
+ table.add_row(f"[cyan]{i}.[/cyan]", f"[green]{match}[/green]")
36
+ console.print(table)
30
37
  else:
31
- print("📋 Here are some available options:")
38
+ console.print("📋 Here are some available options:", style="blue")
32
39
  # Show first 10 installers as examples
33
40
  sample_names = []
34
41
  for inst in all_installers[:10]:
35
42
  exe_name = inst["appName"]
36
43
  sample_names.append(exe_name)
44
+
45
+ table = Table(show_header=False, box=None, pad_edge=False)
37
46
  for i, name in enumerate(sample_names, 1):
38
- print(f" {i}. {name}")
47
+ table.add_row(f"[cyan]{i}.[/cyan]", f"[green]{name}[/green]")
48
+ console.print(table)
39
49
 
40
50
  if len(all_installers) > 10:
41
- print(f" ... and {len(all_installers) - 10} more")
51
+ console.print(f" [dim]... and {len(all_installers) - 10} more[/dim]")
42
52
 
43
- print("\n💡 Use 'ia' to interactively browse all available installers.")
44
- print(f"💡 Use one of the categories: {list(get_args(WHICH_CAT))}")
53
+ panel = Panel(
54
+ "[bold blue]💡 Use 'ia' to interactively browse all available installers.[/bold blue]\n"
55
+ f"[bold blue]💡 Use one of the categories: {list(get_args(WHICH_CAT))}[/bold blue]",
56
+ title="[yellow]Helpful Tips[/yellow]",
57
+ border_style="yellow"
58
+ )
59
+ console.print(panel)
45
60
 
46
61
 
47
62
  def main_with_parser():
@@ -60,7 +75,7 @@ def main(which: Annotated[Optional[str], typer.Argument(help=f"Choose a category
60
75
  if which != "ia" and which is not None: # install by name
61
76
  total_messages: list[str] = []
62
77
  for a_which in which.split(",") if type(which) == str else which:
63
- all_installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["GITHUB_ESSENTIAL", "CUSTOM_ESSENTIAL", "GITHUB_DEV", "CUSTOM_DEV"])
78
+ all_installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["ESSENTIAL", "DEV"])
64
79
 
65
80
  # Find installer by exe_name or name
66
81
  selected_installer = None
@@ -76,8 +91,11 @@ def main(which: Annotated[Optional[str], typer.Argument(help=f"Choose a category
76
91
  return None
77
92
  message = Installer(selected_installer).install_robust(version=None) # finish the task
78
93
  total_messages.append(message)
79
- for a_message in total_messages:
80
- print(a_message)
94
+
95
+ if total_messages:
96
+ console.print("\n[bold green]📊 Installation Results:[/bold green]")
97
+ for a_message in total_messages:
98
+ console.print(f"[blue]• {a_message}[/blue]")
81
99
  return None
82
100
 
83
101
 
@@ -87,7 +105,7 @@ def install_interactively():
87
105
  from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
88
106
  from machineconfig.utils.installer import get_installers
89
107
  from machineconfig.utils.installer_utils.installer_class import Installer
90
- installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["GITHUB_ESSENTIAL", "CUSTOM_ESSENTIAL", "GITHUB_DEV", "CUSTOM_DEV"])
108
+ installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["ESSENTIAL", "DEV"])
91
109
  with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}")) as progress:
92
110
  task = progress.add_task("✅ Checking installed programs...", total=len(installers))
93
111
  installer_options = []
@@ -110,17 +128,24 @@ def install_interactively():
110
128
  an_installer_data = installers[installer_idx]
111
129
  status_message = Installer(an_installer_data).install_robust(version=None) # finish the task - this returns a status message, not a command
112
130
  installation_messages.append(status_message)
113
- print("\n📊 INSTALLATION SUMMARY:")
114
- print("=" * 50)
115
- for message in installation_messages:
116
- print(message)
131
+ if installation_messages:
132
+ panel = Panel(
133
+ "\n".join([f"[blue]• {message}[/blue]" for message in installation_messages]),
134
+ title="[bold green]📊 Installation Summary[/bold green]",
135
+ border_style="green",
136
+ padding=(1, 2)
137
+ )
138
+ console.print(panel)
117
139
 
118
140
 
119
141
  def get_programs_by_category(program_name: WHICH_CAT):
120
- print(f"""
121
- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
122
- 📦 Installing Category: {program_name}
123
- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
142
+ panel = Panel(
143
+ f"[bold yellow]Installing programs from category: [green]{program_name}[/green][/bold yellow]",
144
+ title="[bold blue]📦 Category Installation[/bold blue]",
145
+ border_style="blue",
146
+ padding=(1, 2)
147
+ )
148
+ console.print(panel)
124
149
  from machineconfig.utils.source_of_truth import LIBRARY_ROOT
125
150
  from machineconfig.utils.installer import get_installers, install_all
126
151
  from machineconfig.utils.installer_utils.installer_abc import parse_apps_installer_linux, parse_apps_installer_windows
@@ -128,31 +153,59 @@ def get_programs_by_category(program_name: WHICH_CAT):
128
153
  from machineconfig.utils.options import choose_from_options
129
154
  match program_name:
130
155
  case "essentials":
131
- installers_ = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["GITHUB_ESSENTIAL", "CUSTOM_ESSENTIAL"])
156
+ installers_ = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["ESSENTIAL"])
132
157
  install_all(installers_data=installers_)
133
158
  case "essentialsDev":
134
- installers_ = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["GITHUB_DEV", "CUSTOM_DEV", "GITHUB_ESSENTIAL", "CUSTOM_ESSENTIAL"])
159
+ installers_ = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["DEV", "ESSENTIAL"])
135
160
  install_all(installers_data=installers_)
136
- case "systymPackages":
161
+ case "systemPackages":
137
162
  if system() == "Windows":
138
163
  options_system = parse_apps_installer_windows(LIBRARY_ROOT.joinpath("setup_windows/apps.ps1").read_text(encoding="utf-8"))
139
164
  elif system() == "Linux":
140
- options_system_1 = parse_apps_installer_linux(LIBRARY_ROOT.joinpath("setup_linux/apps_dev.sh").read_text(encoding="utf-8"))
141
- options_system_2 = parse_apps_installer_linux(LIBRARY_ROOT.joinpath("setup_linux/apps.sh").read_text(encoding="utf-8"))
142
- options_system = {**options_system_1, **options_system_2}
165
+ options_system = parse_apps_installer_linux(LIBRARY_ROOT.joinpath("setup_linux/apps.sh").read_text(encoding="utf-8"))
143
166
  else:
144
167
  raise NotImplementedError(f"❌ System {system()} not supported")
145
- program_names = choose_from_options(multi=True, msg="", options=sorted(list(options_system.keys())), header="🚀 CHOOSE DEV APP", fzf=True)
168
+
169
+ # Create display options that include descriptions for user selection
170
+ display_options = []
171
+ for group_name, (description, _) in options_system.items():
172
+ if description:
173
+ display_options.append(f"{group_name:<20} - {description}")
174
+ else:
175
+ display_options.append(group_name)
176
+
177
+ program_names = choose_from_options(multi=True, msg="", options=sorted(display_options), header="🚀 CHOOSE DEV APP", fzf=True)
146
178
  program = ""
147
- for name in program_names:
148
- print(f"""
149
- ┌────────────────────────────────────────────────────
150
- │ ⚙️ Installing: {name}
151
- └────────────────────────────────────────────────────""")
152
- sub_program = options_system[name]
179
+ for display_name in program_names:
180
+ # Extract the actual group name (everything before " - " if present)
181
+ group_name = display_name.split(" - ")[0].strip() if " - " in display_name else display_name.strip()
182
+
183
+ console.print(f"\n[bold cyan]⚙️ Installing: [yellow]{group_name}[/yellow][/bold cyan]", style="bold")
184
+
185
+ _, sub_program = options_system[group_name] # Extract content from tuple
153
186
  if sub_program.startswith("#winget"):
154
187
  sub_program = sub_program[1:]
155
188
  program += "\n" + sub_program
189
+ from pathlib import Path
190
+ if system() == "Windows":
191
+ temp_script_path = Path("C:/Windows/Temp/temp_install_script.ps1")
192
+ lexer = "powershell"
193
+ else:
194
+ temp_script_path = Path("/tmp/temp_install_script.sh")
195
+ lexer = "bash"
196
+ temp_script_path.write_text(program, encoding="utf-8")
197
+ console.print(f"📝 [blue]Temporary script written to:[/blue] [green]{temp_script_path}[/green]")
198
+ from rich.syntax import Syntax
199
+ console.print(Panel(Syntax(code=program, lexer=lexer), title="📄 Installation Program", subtitle="shell code"), style="bold red")
200
+ console.print("🚀 [bold yellow]Starting installation...[/bold yellow]")
201
+ if system() == "Windows":
202
+ import subprocess
203
+ subprocess.run(["powershell", "-ExecutionPolicy", "Bypass", "-File", str(temp_script_path)], check=True)
204
+ elif system() == "Linux":
205
+ import subprocess
206
+ subprocess.run(["bash", str(temp_script_path)], check=True)
207
+ console.print("✅ [bold green]Installation completed.[/bold green]")
208
+ temp_script_path.unlink(missing_ok=True)
156
209
  case "ia":
157
210
  install_interactively()
158
211
  case "precheckedPackages":
@@ -81,24 +81,7 @@ def install_windows_desktop_apps() -> bool:
81
81
  """Install Windows desktop applications using winget."""
82
82
  if system() != "Windows":
83
83
  console.print("❌ This function is only available on Windows systems.", style="bold red")
84
- return False
85
-
86
- console.print(Panel("💻 [bold cyan]WINDOWS DESKTOP APPS[/bold cyan]\n[italic]Installing Brave, Windows Terminal, PowerShell, and VSCode[/italic]", border_style="cyan"))
87
-
88
- # Install winget applications
89
- winget_commands = [
90
- ('winget install --no-upgrade --name "Windows Terminal" --Id "Microsoft.WindowsTerminal" --source winget --scope user --accept-package-agreements --accept-source-agreements', "Installing Windows Terminal"),
91
- ('winget install --no-upgrade --name "Powershell" --Id "Microsoft.PowerShell" --source winget --scope user --accept-package-agreements --accept-source-agreements', "Installing PowerShell"),
92
- ('winget install --no-upgrade --name "Brave" --Id "Brave.Brave" --source winget --scope user --accept-package-agreements --accept-source-agreements', "Installing Brave Browser"),
93
- ('winget install --no-upgrade --name "Microsoft Visual Studio Code" --Id "Microsoft.VisualStudioCode" --source winget --scope user --accept-package-agreements --accept-source-agreements', "Installing Visual Studio Code"),
94
- ]
95
-
96
- success = True
97
- for command, description in winget_commands:
98
- if not run_command(command, description):
99
- success = False
100
-
101
- # Install Nerd Fonts via Python
84
+ return False
102
85
  console.print("🔧 Installing Nerd Fonts", style="bold cyan")
103
86
  try:
104
87
  from machineconfig.jobs.installer.custom_dev.nerfont_windows_helper import install_nerd_fonts
@@ -106,19 +89,14 @@ def install_windows_desktop_apps() -> bool:
106
89
  console.print("✅ Nerd Fonts installed successfully", style="bold green")
107
90
  except Exception as e:
108
91
  console.print(f"❌ Error installing Nerd Fonts: {e}", style="bold red")
109
- success = False
110
-
111
- # Set Windows Terminal settings via Python
112
92
  console.print("🔧 Setting Windows Terminal settings", style="bold cyan")
113
93
  try:
114
94
  from machineconfig.setup_windows.wt_and_pwsh.set_wt_settings import main as set_wt_settings_main
115
95
  set_wt_settings_main()
116
96
  console.print("✅ Windows Terminal settings configured successfully", style="bold green")
117
97
  except Exception as e:
118
- console.print(f"❌ Error setting Windows Terminal settings: {e}", style="bold red")
119
- success = False
120
-
121
- return success
98
+ console.print(f"❌ Error setting Windows Terminal settings: {e}", style="bold red")
99
+ return True
122
100
 
123
101
 
124
102
  def get_installation_choices() -> list[str]:
@@ -162,9 +140,13 @@ def execute_installations(selected_options: list[str]) -> None:
162
140
  run_command(f"bash {script}", "Installing Linux base system applications")
163
141
 
164
142
  if "upgrade_system" in selected_options:
165
- console.print(Panel("🔄 [bold magenta]SYSTEM UPDATE[/bold magenta]\n[italic]Package management[/italic]", border_style="magenta"))
166
- run_command("sudo nala upgrade -y", "Upgrading system packages")
167
-
143
+ if system() == "Windows":
144
+ console.print(" System upgrade is not applicable on Windows via this script.", style="bold red")
145
+ elif system() == "Linux":
146
+ console.print(Panel("🔄 [bold magenta]SYSTEM UPDATE[/bold magenta]\n[italic]Package management[/italic]", border_style="magenta"))
147
+ run_command("sudo nala upgrade -y", "Upgrading system packages")
148
+ else:
149
+ console.print(f"❌ System upgrade not supported on {system()}.", style="bold red")
168
150
  if "install_uv_repos" in selected_options:
169
151
  console.print(Panel("🐍 [bold green]PYTHON ENVIRONMENT[/bold green]\n[italic]Virtual environment setup[/italic]", border_style="green"))
170
152
  from machineconfig import setup_linux as module
@@ -212,9 +194,6 @@ Set-Service -Name sshd -StartupType 'Automatic'"""
212
194
 
213
195
  if "install_dev_tools" in selected_options:
214
196
  console.print(Panel("🛠️ [bold bright_blue]DEVELOPMENT TOOLS[/bold bright_blue]\n[italic]Software development packages[/italic]", border_style="bright_blue"))
215
- run_command("(curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh) || true", "Installing Rust toolchain")
216
- run_command("sudo nala install libssl-dev -y", "Installing libssl-dev")
217
- run_command("sudo nala install ffmpeg -y", "Installing ffmpeg")
218
197
  console.print("🔧 Installing development applications", style="bold cyan")
219
198
  try:
220
199
  from machineconfig.scripts.python.devops_devapps_install import main as devops_devapps_install_main
@@ -2,7 +2,8 @@
2
2
 
3
3
  from machineconfig.utils.installer_utils.installer_abc import check_if_installed_already
4
4
  from machineconfig.utils.installer_utils.installer_class import Installer
5
- from machineconfig.utils.schemas.installer.installer_types import APP_INSTALLER_CATEGORY, InstallerData, InstallerDataFiles, get_normalized_arch, get_os_name, OPERATING_SYSTEMS, CPU_ARCHITECTURES
5
+ from machineconfig.utils.schemas.installer.installer_types import InstallerData, InstallerDataFiles, get_normalized_arch, get_os_name, OPERATING_SYSTEMS, CPU_ARCHITECTURES
6
+ from machineconfig.jobs.installer.package_groups import PACKAGE_GROUPS, PACKAGE_GROUP2NAMES
6
7
  from rich.console import Console
7
8
  from rich.panel import Panel
8
9
 
@@ -18,7 +19,7 @@ from joblib import Parallel, delayed
18
19
  def check_latest():
19
20
  console = Console() # Added console initialization
20
21
  console.print(Panel("🔍 CHECKING FOR LATEST VERSIONS", title="Status", expand=False)) # Replaced print with Panel
21
- installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["GITHUB_ESSENTIAL", "CUSTOM_ESSENTIAL"])
22
+ installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["ESSENTIAL"])
22
23
  installers_github = []
23
24
  for inst__ in installers:
24
25
  app_name = inst__["appName"]
@@ -91,30 +92,30 @@ def get_installed_cli_apps():
91
92
  return apps
92
93
 
93
94
 
94
- def get_installers(os: OPERATING_SYSTEMS, arch: CPU_ARCHITECTURES, which_cats: list[APP_INSTALLER_CATEGORY]) -> list[InstallerData]:
95
+ def get_installers(os: OPERATING_SYSTEMS, arch: CPU_ARCHITECTURES, which_cats: list[PACKAGE_GROUPS]) -> list[InstallerData]:
95
96
  print(f"\n{'=' * 80}\n🔍 LOADING INSTALLER CONFIGURATIONS 🔍\n{'=' * 80}")
96
- res_all = get_all_installer_data_files(which_cats=which_cats)
97
+ res_all = get_all_installer_data_files()
98
+ acceptable_apps_names: list[str] = []
99
+ for cat in which_cats:
100
+ acceptable_apps_names += PACKAGE_GROUP2NAMES[cat]
97
101
  all_installers: list[InstallerData] = []
98
- for _category, installer_data_files in res_all.items():
99
- suitable_installers = []
100
- for an_installer in installer_data_files["installers"]:
101
- if an_installer["fileNamePattern"][arch][os] is None:
102
+ for installer_data in res_all:
103
+ if installer_data["appName"] in acceptable_apps_names:
104
+ if installer_data["fileNamePattern"][arch][os] is None:
102
105
  continue
103
- suitable_installers.append(an_installer)
104
- all_installers.extend(suitable_installers)
106
+ all_installers.append(installer_data)
105
107
  print(f"✅ Loaded {len(all_installers)} installer configurations\n{'=' * 80}")
106
108
  return all_installers
107
109
 
108
110
 
109
- def get_all_installer_data_files(which_cats: list[APP_INSTALLER_CATEGORY]) -> dict[APP_INSTALLER_CATEGORY, InstallerDataFiles]:
111
+ def get_all_installer_data_files() -> list[InstallerData]:
110
112
  print(f"\n{'=' * 80}\n📂 LOADING CONFIGURATION FILES 📂\n{'=' * 80}")
111
113
  import machineconfig.jobs.installer as module
112
114
  from pathlib import Path
113
115
  print("📂 Loading configuration files...")
114
- res_final: dict[APP_INSTALLER_CATEGORY, InstallerDataFiles] = {key: read_json(Path(module.__file__).parent.joinpath(f"packages_{key.lower()}.json")) for key in which_cats}
116
+ res_raw: InstallerDataFiles = read_json(Path(module.__file__).parent.joinpath("installer_data.json"))
117
+ res_final: list[InstallerData] = res_raw["installers"]
115
118
  print(f"Loaded: {len(res_final)} installer categories")
116
- for k, v in res_final.items():
117
- print(f" - {k}: {len(v['installers'])} items")
118
119
  return res_final
119
120
 
120
121
 
@@ -109,8 +109,7 @@ def main() -> None:
109
109
  current_dir = Path(__file__).parent
110
110
  installer_dir = current_dir.parent.parent / "jobs" / "installer"
111
111
 
112
- standard_json = installer_dir / "packages_standard.json"
113
- dev_json = installer_dir / "packages_dev.json"
112
+ standard_json = installer_dir / "installer_data.json"
114
113
  output_json = current_dir / "github_releases.json"
115
114
 
116
115
  print("🔍 Starting GitHub release data extraction...")
@@ -125,16 +124,7 @@ def main() -> None:
125
124
  all_github_repos.update(repos)
126
125
  print(f" Found {len(repos)} GitHub repos")
127
126
  else:
128
- print(f"⚠️ File not found: {standard_json}")
129
-
130
- if dev_json.exists():
131
- print(f"📄 Reading {dev_json.name}...")
132
- repos = extract_github_repos_from_json(dev_json)
133
- all_github_repos.update(repos)
134
- print(f" Found {len(repos)} GitHub repos")
135
- else:
136
- print(f"⚠️ File not found: {dev_json}")
137
-
127
+ print(f"⚠️ File not found: {standard_json}")
138
128
  print(f"🎯 Total unique GitHub repositories found: {len(all_github_repos)}")
139
129
 
140
130
  if not all_github_repos:
@@ -3,7 +3,7 @@ from machineconfig.utils.path_extended import PathExtended as PathExtended
3
3
  from machineconfig.utils.source_of_truth import WINDOWS_INSTALL_PATH, LINUX_INSTALL_PATH, INSTALL_VERSION_ROOT
4
4
 
5
5
  from pathlib import Path
6
- from typing import Any, Optional
6
+ from typing import Optional
7
7
  import subprocess
8
8
  import platform
9
9
 
@@ -166,75 +166,71 @@ def check_if_installed_already(exe_name: str, version: Optional[str], use_cache:
166
166
  return ("⚠️ NotInstalled", "None", version or "unknown")
167
167
 
168
168
 
169
- def parse_apps_installer_linux(txt: str) -> dict[str, Any]:
169
+ def parse_apps_installer_linux(txt: str) -> dict[str, tuple[str, str]]:
170
170
  """Parse Linux shell installation scripts into logical chunks.
171
171
 
172
- Supports two formats:
173
- 1. Legacy format with 'yes '' | sed 3q; echo "----------------------------- installing' delimiter
174
- 2. New format with # --BLOCK:<name>-- comment signatures
172
+ Splits scripts by # --GROUP:<name>:<description> comment signatures into a dictionary
173
+ mapping block names to (description, shell script content) tuples.
175
174
 
176
175
  Returns:
177
- dict[str, str]: Dictionary mapping block/section names to their installation scripts
176
+ dict[str, tuple[str, str]]: Dictionary mapping block/section names to (description, installation_script) tuples
178
177
  """
179
- # Try new block format first
180
- if "# --BLOCK:" in txt:
181
- import re
182
- # Split by block signatures: # --BLOCK:<name>--
183
- blocks = re.split(r'# --BLOCK:([^-]+)--', txt)
184
- res: dict[str, str] = {}
178
+ chunks = txt.split('# --GROUP:')
179
+ res: dict[str, tuple[str, str]] = {}
180
+
181
+ for chunk in chunks[1:]: # Skip first empty chunk before first group
182
+ lines = chunk.split('\n')
183
+ # First line contains the group name and description in format "NAME:DESCRIPTION"
184
+ group_line = lines[0].strip()
185
+
186
+ # Extract group name and description
187
+ if ':' in group_line:
188
+ parts = group_line.split(':', 1) # Split only on first colon
189
+ group_name = parts[0].strip()
190
+ group_description = parts[1].strip() if len(parts) > 1 else ""
191
+ else:
192
+ group_name = group_line
193
+ group_description = ""
185
194
 
186
- # Process blocks in pairs (block_name, block_content)
187
- for i in range(1, len(blocks), 2):
188
- if i + 1 < len(blocks):
189
- block_name = blocks[i].strip()
190
- block_content = blocks[i + 1].strip()
191
- if block_content:
192
- res[block_name] = block_content
195
+ # Rest is the content
196
+ content = '\n'.join(lines[1:]).strip()
193
197
 
194
- return res
198
+ if group_name and content:
199
+ res[group_name] = (group_description, content)
195
200
 
196
- # Legacy format fallback
197
- txts = txt.split("""yes '' | sed 3q; echo "----------------------------- installing """)
198
- res = {}
199
- for chunk in txts[1:]:
200
- try:
201
- k = chunk.split("----")[0].rstrip().lstrip()
202
- v = "\n".join(chunk.split("\n")[1:])
203
- res[k] = v
204
- except IndexError as e:
205
- print(f"""
206
- ❌ Error parsing chunk:
207
- {"-" * 50}
208
- {chunk}
209
- {"-" * 50}""")
210
- raise e
211
201
  return res
212
202
 
213
203
 
214
- def parse_apps_installer_windows(txt: str) -> dict[str, Any]:
215
- chunks: list[str] = []
216
- for idx, item in enumerate(txt.split(sep="winget install")):
217
- if idx == 0:
218
- continue
219
- if idx == 1:
220
- chunks.append(item)
204
+ def parse_apps_installer_windows(txt: str) -> dict[str, tuple[str, str]]:
205
+ """Parse Windows PowerShell installation scripts into logical chunks.
206
+
207
+ Splits scripts by # --GROUP:<name>:<description> comment signatures into a dictionary
208
+ mapping block names to (description, PowerShell script content) tuples.
209
+
210
+ Returns:
211
+ dict[str, tuple[str, str]]: Dictionary mapping block/section names to (description, installation_script) tuples
212
+ """
213
+ chunks = txt.split('# --GROUP:')
214
+ res: dict[str, tuple[str, str]] = {}
215
+
216
+ for chunk in chunks[1:]: # Skip first chunk before first group
217
+ lines = chunk.split('\n')
218
+ # First line contains the group name and description in format "NAME:DESCRIPTION"
219
+ group_line = lines[0].strip()
220
+
221
+ # Extract group name and description
222
+ if ':' in group_line:
223
+ parts = group_line.split(':', 1) # Split only on first colon
224
+ group_name = parts[0].strip()
225
+ group_description = parts[1].strip() if len(parts) > 1 else ""
221
226
  else:
222
- chunks.append("winget install" + item)
223
- # progs = L(txt.splitlines()).filter(lambda x: x.startswith("winget ") or x.startswith("#winget"))
224
- res: dict[str, str] = {}
225
- for a_chunk in chunks:
226
- try:
227
- name = a_chunk.split("--name ")[1]
228
- if "--Id" not in name:
229
- print(f"⚠️ Warning: {name} does not have an Id, skipping")
230
- continue
231
- name = name.split(" --Id ", maxsplit=1)[0].strip('"').strip('"')
232
- res[name] = a_chunk
233
- except IndexError as e:
234
- print(f"""
235
- ❌ Error parsing chunk:
236
- {"-" * 50}
237
- {a_chunk}
238
- {"-" * 50}""")
239
- raise e
227
+ group_name = group_line
228
+ group_description = ""
229
+
230
+ # Rest is the content
231
+ content = '\n'.join(lines[1:]).strip()
232
+
233
+ if group_name and content:
234
+ res[group_name] = (group_description, content)
235
+
240
236
  return res