machineconfig 7.53__py3-none-any.whl → 7.69__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 (90) hide show
  1. machineconfig/cluster/sessions_managers/utils/maker.py +21 -9
  2. machineconfig/jobs/installer/custom/boxes.py +2 -2
  3. machineconfig/jobs/installer/custom/hx.py +15 -12
  4. machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
  5. machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +1 -1
  6. machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +1 -1
  7. machineconfig/jobs/installer/custom_dev/sysabc.py +39 -34
  8. machineconfig/jobs/installer/custom_dev/wezterm.py +0 -4
  9. machineconfig/jobs/installer/installer_data.json +103 -35
  10. machineconfig/jobs/installer/package_groups.py +28 -13
  11. machineconfig/scripts/__init__.py +0 -4
  12. machineconfig/scripts/linux/wrap_mcfg +1 -1
  13. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +3 -0
  14. machineconfig/scripts/python/croshell.py +22 -17
  15. machineconfig/scripts/python/devops.py +3 -4
  16. machineconfig/scripts/python/devops_navigator.py +0 -4
  17. machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
  18. machineconfig/scripts/python/fire_jobs.py +17 -15
  19. machineconfig/scripts/python/ftpx.py +13 -11
  20. machineconfig/scripts/python/helpers/ast_search.py +74 -0
  21. machineconfig/scripts/python/helpers/repo_rag.py +325 -0
  22. machineconfig/scripts/python/helpers/symantic_search.py +25 -0
  23. machineconfig/scripts/python/helpers_cloud/cloud_copy.py +28 -21
  24. machineconfig/scripts/python/helpers_cloud/cloud_helpers.py +1 -1
  25. machineconfig/scripts/python/helpers_cloud/cloud_sync.py +8 -7
  26. machineconfig/scripts/python/helpers_croshell/crosh.py +2 -2
  27. machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +22 -13
  28. machineconfig/scripts/python/helpers_devops/cli_self.py +7 -6
  29. machineconfig/scripts/python/helpers_devops/cli_share_file.py +2 -2
  30. machineconfig/scripts/python/helpers_devops/cli_share_server.py +1 -1
  31. machineconfig/scripts/python/helpers_devops/cli_terminal.py +1 -1
  32. machineconfig/scripts/python/helpers_devops/cli_utils.py +2 -73
  33. machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
  34. machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -3
  35. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +3 -4
  36. machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
  37. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +13 -5
  38. machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +1 -1
  39. machineconfig/scripts/python/helpers_repos/entrypoint.py +2 -1
  40. machineconfig/scripts/python/helpers_repos/record.py +2 -1
  41. machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +5 -5
  42. machineconfig/scripts/python/helpers_utils/download.py +152 -0
  43. machineconfig/scripts/python/helpers_utils/path.py +4 -2
  44. machineconfig/scripts/python/interactive.py +11 -14
  45. machineconfig/scripts/python/{machineconfig.py → mcfg_entry.py} +4 -0
  46. machineconfig/scripts/python/msearch.py +21 -2
  47. machineconfig/scripts/python/nw/devops_add_ssh_key.py +21 -5
  48. machineconfig/scripts/python/nw/ssh_debug_linux.py +7 -7
  49. machineconfig/scripts/python/nw/ssh_debug_windows.py +4 -4
  50. machineconfig/scripts/python/nw/wsl_windows_transfer.py +3 -2
  51. machineconfig/scripts/python/sessions.py +35 -20
  52. machineconfig/scripts/python/terminal.py +2 -2
  53. machineconfig/scripts/python/utils.py +12 -10
  54. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  55. machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
  56. machineconfig/settings/shells/pwsh/init.ps1 +1 -0
  57. machineconfig/settings/shells/wezterm/wezterm.lua +2 -0
  58. machineconfig/settings/shells/zsh/init.sh +0 -7
  59. machineconfig/settings/yazi/shell/yazi_cd.ps1 +29 -5
  60. machineconfig/setup_linux/web_shortcuts/interactive.sh +12 -11
  61. machineconfig/setup_windows/uv.ps1 +8 -1
  62. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +12 -11
  63. machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +4 -2
  64. machineconfig/utils/accessories.py +7 -4
  65. machineconfig/utils/code.py +6 -4
  66. machineconfig/utils/files/headers.py +2 -2
  67. machineconfig/utils/installer_utils/install_from_url.py +180 -0
  68. machineconfig/utils/installer_utils/installer_class.py +56 -46
  69. machineconfig/utils/installer_utils/{installer.py → installer_cli.py} +71 -65
  70. machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +1 -25
  71. machineconfig/utils/meta.py +28 -15
  72. machineconfig/utils/options.py +4 -4
  73. machineconfig/utils/path_extended.py +40 -19
  74. machineconfig/utils/path_helper.py +33 -31
  75. machineconfig/utils/schemas/layouts/layout_types.py +1 -1
  76. machineconfig/utils/ssh.py +330 -99
  77. machineconfig/utils/ve.py +11 -4
  78. machineconfig-7.69.dist-info/METADATA +124 -0
  79. {machineconfig-7.53.dist-info → machineconfig-7.69.dist-info}/RECORD +85 -83
  80. {machineconfig-7.53.dist-info → machineconfig-7.69.dist-info}/entry_points.txt +2 -2
  81. machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
  82. machineconfig/scripts/python/explore.py +0 -49
  83. machineconfig/scripts/python/nw/add_ssh_key.py +0 -148
  84. machineconfig/settings/lf/linux/exe/fzf_nano.sh +0 -16
  85. machineconfig-7.53.dist-info/METADATA +0 -94
  86. /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
  87. /machineconfig/scripts/{Restore-ThunderbirdProfile.ps1 → windows/mounts/Restore-ThunderbirdProfile.ps1} +0 -0
  88. /machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +0 -0
  89. {machineconfig-7.53.dist-info → machineconfig-7.69.dist-info}/WHEEL +0 -0
  90. {machineconfig-7.53.dist-info → machineconfig-7.69.dist-info}/top_level.txt +0 -0
@@ -1,49 +0,0 @@
1
-
2
- import typer
3
-
4
-
5
- def machineconfig_lf():
6
- # from machineconfig.scripts.python.helpers_msearch import FZFG_LINUX_PATH, FZFG_WINDOWS_PATH
7
- import platform
8
- if platform.system() == "Linux":
9
- script = """
10
- tmp="$(mktemp)"
11
- lf -last-dir-path="$tmp" "$@"
12
- if [ -f "$tmp" ]; then
13
- dir="$(cat "$tmp")"
14
- rm -f "$tmp"
15
- if [ -d "$dir" ]; then
16
- if [ "$dir" != "$(pwd)" ]; then
17
- cd "$dir"
18
- fi
19
- fi
20
- fi
21
- """
22
- elif platform.system() == "Windows":
23
- script = r"""
24
- $tmp = [System.IO.Path]::GetTempFileName()
25
- ~\AppData\Local\Microsoft\WindowsApps\lf.exe -last-dir-path="$tmp" $args
26
- if (Test-Path -PathType Leaf "$tmp")
27
- {
28
- $dir = Get-Content "$tmp"
29
- Remove-Item -Force "$tmp"
30
- if (Test-Path -PathType Container "$dir")
31
- {
32
- if ("$dir" -ne "$pwd")
33
- {
34
- Set-Location "$dir"
35
- }
36
- }
37
- }
38
- """
39
- else:
40
- raise RuntimeError("Unsupported platform")
41
-
42
- from machineconfig.utils.code import exit_then_run_shell_script
43
- exit_then_run_shell_script(script=script, strict=False)
44
-
45
-
46
- def main():
47
- app = typer.Typer(add_completion=False, no_args_is_help=True)
48
- app.command(name="lf", help="machineconfig lf wrapper.", no_args_is_help=False)(machineconfig_lf)
49
- app()
@@ -1,148 +0,0 @@
1
- """SSH"""
2
-
3
- from platform import system
4
- from machineconfig.utils.source_of_truth import LIBRARY_ROOT
5
- from machineconfig.utils.path_extended import PathExtended
6
- from rich.console import Console
7
- from rich.panel import Panel
8
- from rich import box # Import box
9
- from typing import Optional, Annotated
10
- import typer
11
-
12
-
13
- console = Console()
14
-
15
-
16
- def get_add_ssh_key_script(path_to_key: PathExtended):
17
- console.print(Panel("🔑 SSH KEY CONFIGURATION", title="[bold blue]SSH Setup[/bold blue]"))
18
- if system() == "Linux" or system() == "Darwin":
19
- authorized_keys = PathExtended.home().joinpath(".ssh/authorized_keys")
20
- console.print(Panel(f"🐧 Linux SSH configuration\n📄 Authorized keys file: {authorized_keys}", title="[bold blue]System Info[/bold blue]"))
21
- elif system() == "Windows":
22
- authorized_keys = PathExtended("C:/ProgramData/ssh/administrators_authorized_keys")
23
- console.print(Panel(f"🪟 Windows SSH configuration\n📄 Authorized keys file: {authorized_keys}", title="[bold blue]System Info[/bold blue]"))
24
- else:
25
- console.print(Panel("❌ ERROR: Unsupported operating system\nOnly Linux and Windows are supported", title="[bold red]Error[/bold red]"))
26
- raise NotImplementedError
27
-
28
- if authorized_keys.exists():
29
- split = "\n"
30
- keys_text = authorized_keys.read_text(encoding="utf-8").split(split)
31
- key_count = len([k for k in keys_text if k.strip()])
32
- console.print(Panel(f"🔍 Current SSH authorization status\n✅ Found {key_count} authorized key(s)", title="[bold blue]Status[/bold blue]"))
33
- if path_to_key.read_text(encoding="utf-8") in authorized_keys.read_text(encoding="utf-8"):
34
- console.print(Panel(f"⚠️ Key already authorized\nKey: {path_to_key.name}\nStatus: Already present in authorized_keys file\nNo action required", title="[bold yellow]Warning[/bold yellow]"))
35
- program = ""
36
- else:
37
- console.print(Panel(f"➕ Adding new SSH key to authorized keys\n🔑 Key file: {path_to_key.name}", title="[bold blue]Action[/bold blue]"))
38
- if system() == "Linux":
39
- program = f"cat {path_to_key} >> ~/.ssh/authorized_keys"
40
- elif system() == "Windows":
41
- program_path = LIBRARY_ROOT.joinpath("setup_windows/add-sshkey.ps1")
42
- program = program_path.expanduser().read_text(encoding="utf-8")
43
- place_holder = r'$sshfile = "$env:USERPROFILE\.ssh\pubkey.pub"'
44
- assert place_holder in program, f"This section performs string manipulation on the script {program_path} to add the key to the authorized_keys file. The script has changed and the string {place_holder} is not found."
45
- program = program.replace(place_holder, f'$sshfile = "{path_to_key}"')
46
- console.print(Panel("🔧 Configured PowerShell script for Windows\n📝 Replaced placeholder with actual key path", title="[bold blue]Configuration[/bold blue]"))
47
- else:
48
- raise NotImplementedError
49
- else:
50
- console.print(Panel(f"📝 Creating new authorized_keys file\n🔑 Using key: {path_to_key.name}", title="[bold blue]Action[/bold blue]"))
51
- if system() == "Linux":
52
- program = f"cat {path_to_key} > ~/.ssh/authorized_keys"
53
- else:
54
- program_path = LIBRARY_ROOT.joinpath("setup_windows/openssh-server_add-sshkey.ps1")
55
- program = PathExtended(program_path).expanduser().read_text(encoding="utf-8").replace('$sshfile=""', f'$sshfile="{path_to_key}"')
56
- console.print(Panel("🔧 Configured PowerShell script for Windows\n📝 Set key path in script", title="[bold blue]Configuration[/bold blue]"))
57
-
58
- if system() == "Linux" or system() == "Darwin":
59
- program += """
60
- sudo chmod 700 ~/.ssh
61
- sudo chmod 644 ~/.ssh/authorized_keys
62
- sudo chmod 644 ~/.ssh/*.pub
63
- sudo service ssh --full-restart
64
- # from superuser.com/questions/215504/permissions-on-private-key-in-ssh-folder
65
- """
66
- return program
67
-
68
-
69
-
70
- """
71
- # Common pitfalls:
72
- # 🚫 Wrong line endings (LF/CRLF) in config files
73
- # 🌐 Network port conflicts (try 2222 -> 2223) between WSL and Windows
74
- # sudo service ssh restart
75
- # sudo service ssh status
76
- # sudo nano /etc/ssh/sshd_config
77
- """
78
-
79
-
80
- def main(pub_path: Annotated[Optional[str], typer.Argument(..., help="Path to the public key file")] = None,
81
- pub_choose: Annotated[bool, typer.Option(..., "--choose", "-c", help="Choose from available public keys in ~/.ssh")] = False,
82
- pub_val: Annotated[bool, typer.Option(..., "--paste", "-p", help="Paste the public key content manually")] = False,
83
- from_github: Annotated[Optional[str], typer.Option(..., "--from-github", "-g", help="Fetch public keys from a GitHub username")] = None
84
- ) -> None:
85
- if pub_path:
86
- key_path = PathExtended(pub_path).expanduser().absolute()
87
- if not key_path.exists():
88
- console.print(Panel(f"❌ ERROR: Provided key path does not exist\nPath: {key_path}", title="[bold red]Error[/bold red]"))
89
- raise FileNotFoundError(f"Provided key path does not exist: {key_path}")
90
- console.print(Panel(f"📄 Using provided public key file: {key_path}", title="[bold blue]Info[/bold blue]"))
91
- program = get_add_ssh_key_script(key_path)
92
- from machineconfig.utils.code import run_shell_script
93
- run_shell_script(script=program)
94
- console.print(Panel("✅ SSH KEY AUTHORIZATION COMPLETED", box=box.DOUBLE_EDGE, title_align="left"))
95
- return
96
- elif pub_choose:
97
- console.print(Panel("🔐 SSH PUBLIC KEY AUTHORIZATION TOOL", box=box.DOUBLE_EDGE, title_align="left"))
98
- console.print(Panel("🔍 Searching for public keys...", title="[bold blue]SSH Setup[/bold blue]", border_style="blue"))
99
- pub_keys = PathExtended.home().joinpath(".ssh").search("*.pub")
100
- if pub_keys:
101
- console.print(Panel(f"✅ Found {len(pub_keys)} public key(s)", title="[bold green]Status[/bold green]", border_style="green"))
102
- else:
103
- console.print(Panel("⚠️ No public keys found", title="[bold yellow]Warning[/bold yellow]", border_style="yellow"))
104
- return
105
- console.print(Panel(f"🔄 Processing all {len(pub_keys)} public keys...", title="[bold blue]Processing[/bold blue]", border_style="blue"))
106
- program = "\n\n\n".join([get_add_ssh_key_script(key) for key in pub_keys])
107
-
108
- elif pub_val:
109
- console.print(Panel("📋 Please provide a filename and paste the public key content", title="[bold blue]Input Required[/bold blue]", border_style="blue"))
110
- key_filename = input("📝 File name (default: my_pasted_key.pub): ") or "my_pasted_key.pub"
111
- key_path = PathExtended.home().joinpath(f".ssh/{key_filename}")
112
- key_path.write_text(input("🔑 Paste the public key here: "), encoding="utf-8")
113
- console.print(Panel(f"💾 Key saved to: {key_path}", title="[bold green]Success[/bold green]", border_style="green"))
114
- program = get_add_ssh_key_script(key_path)
115
- elif from_github:
116
- console.print(Panel(f"🌐 Fetching public keys from GitHub user: {from_github}", title="[bold blue]GitHub Fetch[/bold blue]", border_style="blue"))
117
- import requests
118
- # $pubkey_url = 'https://github.com/thisismygitrepo.keys' # $pubkey_string = (Invoke-WebRequest $pubkey_url).Content
119
- response = requests.get(f"https://api.github.com/users/{from_github}/keys")
120
- if response.status_code != 200:
121
- console.print(Panel(f"❌ ERROR: Failed to fetch keys from GitHub user {from_github}\nStatus Code: {response.status_code}", title="[bold red]Error[/bold red]", border_style="red"))
122
- raise RuntimeError(f"Failed to fetch keys from GitHub user {from_github}: Status Code {response.status_code}")
123
- keys = response.json()
124
- if not keys:
125
- console.print(Panel(f"⚠️ No public keys found for GitHub user: {from_github}", title="[bold yellow]Warning[/bold yellow]", border_style="yellow"))
126
- return
127
- console.print(Panel(f"✅ Found {len(keys)} public key(s) for user: {from_github}", title="[bold green]Success[/bold green]", border_style="green"))
128
- key_path = PathExtended.home().joinpath(f".ssh/{from_github}_github_keys.pub")
129
- key_path.write_text("\n".join([key["key"] for key in keys]), encoding="utf-8")
130
- console.print(Panel(f"💾 Keys saved to: {key_path}", title="[bold green]Success[/bold green]", border_style="green"))
131
- program = get_add_ssh_key_script(key_path)
132
- else:
133
- console.print(Panel("❌ ERROR: No method provided to add SSH key\nUse --help for options", title="[bold red]Error[/bold red]", border_style="red"))
134
- raise ValueError("No method provided to add SSH key. Use --help for options.")
135
- console.print(Panel("🚀 SSH KEY AUTHORIZATION READY\nRun the generated script to apply changes", box=box.DOUBLE_EDGE, title_align="left"))
136
- from machineconfig.utils.code import run_shell_script
137
- run_shell_script(script=program)
138
- import socket
139
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
140
- s.connect(('8.8.8.8',80))
141
- local_ip_v4 = s.getsockname()[0]
142
- s.close()
143
- print(f"This computer is @ {local_ip_v4}")
144
- console.print(Panel("✅ SSH KEY AUTHORIZATION COMPLETED", box=box.DOUBLE_EDGE, title_align="left"))
145
-
146
-
147
- if __name__ == "__main__":
148
- pass
@@ -1,16 +0,0 @@
1
- #!/bin/sh
2
- # 🔍 Fuzzy Finder with Nano Editor Integration
3
-
4
- # 📝 Open selected file in nano
5
- nano (fzf2g) # space used for precedence in execution
6
-
7
- # 💡 Alternative commands (commented):
8
- # 🔎 FZF with bat preview:
9
- # fzf --ansi --preview-window 'right:60%' --preview 'bat --color=always --style=numbers,grid,header --line-range :300 {}'
10
-
11
- # 🪟 Windows Git Bash version:
12
- # & "C:\Program Files\Git\usr\bin\nano.exe" (fzf --ansi --preview-window 'right:60%' --preview 'bat --color=always --style=numbers,grid,header --line-range :300 {}')
13
-
14
- # 📜 PowerShell script integration:
15
- # fzf | nano.ps1
16
-
@@ -1,94 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: machineconfig
3
- Version: 7.53
4
- Summary: Dotfiles management package
5
- Author-email: Alex Al-Saffar <programmer@usa.com>
6
- License: Apache 2.0
7
- Project-URL: Homepage, https://github.com/thisismygitrepo/machineconfig
8
- Project-URL: Bug Tracker, https://github.com/thisismygitrepo/machineconfig/issues
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: Apache Software License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.13
13
- Description-Content-Type: text/markdown
14
- Requires-Dist: cryptography>=44.0.2
15
- Requires-Dist: fire>=0.7.0
16
- Requires-Dist: joblib>=1.5.2
17
- Requires-Dist: paramiko>=3.5.1
18
- Requires-Dist: randomname>=0.2.1
19
- Requires-Dist: requests>=2.32.5
20
- Requires-Dist: rich>=14.0.0
21
- Requires-Dist: tenacity>=9.1.2
22
- Requires-Dist: psutil>=7.0.0
23
- Requires-Dist: gitpython>=3.1.44
24
- Requires-Dist: pyfzf>=0.3.1
25
- Requires-Dist: rclone-python>=0.1.23
26
- Requires-Dist: questionary>=2.1.1
27
- Requires-Dist: typer-slim>=0.19.2
28
- Requires-Dist: typer>=0.19.2
29
- Provides-Extra: windows
30
- Requires-Dist: pywin32; extra == "windows"
31
- Provides-Extra: plot
32
- Requires-Dist: sqlalchemy>=2.0.43; extra == "plot"
33
- Requires-Dist: ipykernel>=6.30.1; extra == "plot"
34
- Requires-Dist: ipython>=9.5.0; extra == "plot"
35
- Requires-Dist: jupyterlab>=4.4.9; extra == "plot"
36
- Requires-Dist: kaleido>=1.1.0; extra == "plot"
37
- Requires-Dist: matplotlib>=3.10.6; extra == "plot"
38
- Requires-Dist: nbformat>=5.10.4; extra == "plot"
39
- Requires-Dist: numpy>=2.3.3; extra == "plot"
40
- Requires-Dist: plotly>=6.3.0; extra == "plot"
41
- Requires-Dist: polars>=1.33.1; extra == "plot"
42
- Requires-Dist: python-magic>=0.4.27; extra == "plot"
43
-
44
-
45
- <p align="center">
46
-
47
- <a href="https://github.com/thisismygitrepo/machineconfig/commits">
48
- <img src="https://img.shields.io/github/commit-activity/m/thisismygitrepo/machineconfig" />
49
- </a>
50
-
51
- </p>
52
-
53
-
54
- # Welcome to machineconfig
55
-
56
- Machineconfig is a package for managing configuration files (aka dotfiles). The idea is to collect those critical, time-consuming-files-to-setup in one directory and reference them via symbolic links from their original locations. Thus, when a new machine is to be setup, all that is required is to clone the repo in that machine and create the symbolic links.
57
- Dotfiles are divided into private and public. Examples of private ones are, `~/.gitconfig`, `~/.ssh`, etc. Whereas public config files are ones like `lfrc`. The private dotfiles are placed @ `~/dotfiles`. The files therein are encrypted before backedup.
58
-
59
- # Install On Windows:
60
-
61
- ```powershell
62
- # Temporary install:
63
- iex (iwr bit.ly/cfgwindows).Content
64
- # Or, if UV is installed:
65
- iex (uvx machineconfig define)
66
- # Permanent install:
67
- powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" # Skip if UV is already installed
68
- uv tool install --upgrade --python 3.14 machineconfig both
69
- devops config copy-assets
70
-
71
- ```
72
-
73
- # Install On Linux and MacOS
74
-
75
- ```bash
76
- # Temporary install:
77
- . <(curl -L bit.ly/cfglinux)
78
- # Or, if UV is installed:
79
- . <(uvx machineconfig define)
80
- # Permanent install:
81
- curl -LsSf https://astral.sh/uv/install.sh | sh # Skip if UV is already installed
82
- uv tool install --upgrade --upgrade 3.14 machineconfig
83
- ```
84
-
85
-
86
- # Author
87
- Alex Al-Saffar. [email](mailto:programmer@usa.com)
88
-
89
- # Contributor
90
- Ruby Chan. [email](mailto:ruby.chan@sa.gov.au)
91
-
92
-
93
- [![Alex's github activity graph](https://github-readme-activity-graph.vercel.app/graph?username=thisismygitrepo)](https://github.com/ashutosh00710/github-readme-activity-graph)
94
-