pygitgo 1.8.0__tar.gz → 1.8.2__tar.gz

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.
Files changed (80) hide show
  1. {pygitgo-1.8.0/src/pygitgo.egg-info → pygitgo-1.8.2}/PKG-INFO +1 -1
  2. {pygitgo-1.8.0 → pygitgo-1.8.2}/pyproject.toml +1 -1
  3. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/auth/account.py +29 -2
  4. pygitgo-1.8.2/src/pygitgo/auth/manager.py +132 -0
  5. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/auth/ssh_utils.py +91 -9
  6. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/config.py +4 -6
  7. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/git_branch.py +7 -8
  8. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/git_core.py +20 -13
  9. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/git_remote.py +12 -18
  10. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/init.py +24 -7
  11. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/jump.py +40 -44
  12. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/link.py +31 -29
  13. pygitgo-1.8.2/src/pygitgo/commands/new.py +66 -0
  14. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/pull.py +12 -6
  15. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/push.py +9 -6
  16. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/repo.py +65 -12
  17. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/staging.py +1 -1
  18. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/stash.py +26 -12
  19. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/state.py +5 -4
  20. pygitgo-1.8.2/src/pygitgo/commands/undo.py +117 -0
  21. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/user.py +3 -2
  22. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/exceptions.py +0 -1
  23. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/main.py +2 -2
  24. pygitgo-1.8.2/src/pygitgo/utils/bootstrap.py +15 -0
  25. pygitgo-1.8.0/src/pygitgo/utils/colors.py → pygitgo-1.8.2/src/pygitgo/utils/cli_io.py +16 -15
  26. pygitgo-1.8.2/src/pygitgo/utils/colors.py +6 -0
  27. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/utils/config.py +1 -1
  28. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/utils/executor.py +16 -10
  29. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/utils/platform.py +3 -3
  30. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/utils/update_checker.py +5 -5
  31. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/utils/validators.py +1 -1
  32. {pygitgo-1.8.0 → pygitgo-1.8.2/src/pygitgo.egg-info}/PKG-INFO +1 -1
  33. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo.egg-info/SOURCES.txt +5 -0
  34. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_account.py +49 -2
  35. pygitgo-1.8.2/tests/test_bootstrap.py +29 -0
  36. pygitgo-1.8.2/tests/test_cli_io.py +51 -0
  37. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_config_command.py +8 -12
  38. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_config_utils.py +0 -5
  39. pygitgo-1.8.2/tests/test_executor.py +92 -0
  40. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_git_branch.py +8 -8
  41. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_git_core.py +22 -19
  42. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_git_remote.py +26 -27
  43. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_init.py +0 -1
  44. pygitgo-1.8.2/tests/test_integration_git.py +73 -0
  45. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_jump.py +70 -20
  46. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_link.py +102 -12
  47. pygitgo-1.8.2/tests/test_main.py +116 -0
  48. pygitgo-1.8.2/tests/test_manager.py +145 -0
  49. pygitgo-1.8.2/tests/test_new.py +90 -0
  50. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_platform.py +63 -62
  51. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_pull.py +23 -12
  52. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_push.py +21 -8
  53. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_repo.py +0 -3
  54. pygitgo-1.8.2/tests/test_ssh_utils.py +220 -0
  55. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_staging.py +2 -4
  56. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_stash.py +16 -9
  57. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_state.py +83 -0
  58. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_undo.py +75 -47
  59. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_update_checker.py +2 -3
  60. pygitgo-1.8.0/src/pygitgo/auth/manager.py +0 -101
  61. pygitgo-1.8.0/src/pygitgo/commands/new.py +0 -21
  62. pygitgo-1.8.0/src/pygitgo/commands/undo.py +0 -114
  63. pygitgo-1.8.0/src/pygitgo/utils/bootstrap.py +0 -26
  64. pygitgo-1.8.0/tests/test_bootstrap.py +0 -54
  65. pygitgo-1.8.0/tests/test_manager.py +0 -79
  66. pygitgo-1.8.0/tests/test_new.py +0 -27
  67. pygitgo-1.8.0/tests/test_ssh_utils.py +0 -119
  68. {pygitgo-1.8.0 → pygitgo-1.8.2}/LICENSE +0 -0
  69. {pygitgo-1.8.0 → pygitgo-1.8.2}/README.md +0 -0
  70. {pygitgo-1.8.0 → pygitgo-1.8.2}/setup.cfg +0 -0
  71. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/__init__.py +0 -0
  72. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/auth/__init__.py +0 -0
  73. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/commands/__init__.py +0 -0
  74. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo/utils/__init__.py +0 -0
  75. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo.egg-info/dependency_links.txt +0 -0
  76. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo.egg-info/entry_points.txt +0 -0
  77. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo.egg-info/requires.txt +0 -0
  78. {pygitgo-1.8.0 → pygitgo-1.8.2}/src/pygitgo.egg-info/top_level.txt +0 -0
  79. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_user.py +0 -0
  80. {pygitgo-1.8.0 → pygitgo-1.8.2}/tests/test_validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygitgo
3
- Version: 1.8.0
3
+ Version: 1.8.2
4
4
  Summary: GitGo CLI - Your Fast Git Companion. Simplifies git push, link, stash, and user management.
5
5
  Author: Huerte
6
6
  License: GPL-3.0-or-later
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pygitgo"
7
- version = "1.8.0"
7
+ version = "1.8.2"
8
8
  description = "GitGo CLI - Your Fast Git Companion. Simplifies git push, link, stash, and user management."
9
9
  readme = "README.md"
10
10
  license = {text = "GPL-3.0-or-later"}
@@ -1,6 +1,7 @@
1
- from pygitgo.utils.colors import info, success, warning, error, BLUE, RESET
1
+ from pygitgo.utils.cli_io import info, success, warning, error
2
2
  from pygitgo.utils.executor import run_command
3
3
  from pygitgo.exceptions import GitCommandError
4
+ from pygitgo.utils.colors import BLUE, RESET
4
5
 
5
6
 
6
7
  def get_user():
@@ -24,7 +25,7 @@ def get_user():
24
25
  def set_user(name, email):
25
26
  run_command(["git", "config", "--global", "user.name", name])
26
27
  run_command(["git", "config", "--global", "user.email", email])
27
- success(f"\nGit user configured Successfully")
28
+ success("\nGit user configured successfully.")
28
29
  print(f"{BLUE}Username{RESET} : {name}")
29
30
  print(f"{BLUE}Email {RESET}: {email}")
30
31
 
@@ -65,3 +66,29 @@ def ensure_user_configure(default_email=None, default_username=None):
65
66
  error("Invalid configuration. Name and Email are required.")
66
67
  return False
67
68
 
69
+ def sanitize_signing_config():
70
+ try:
71
+ gpgsign = run_command(["git", "config", "--global", "commit.gpgsign"]).strip().lower()
72
+ except GitCommandError:
73
+ return
74
+
75
+ if gpgsign != "true":
76
+ return
77
+
78
+ try:
79
+ fmt = run_command(["git", "config", "--global", "gpg.format"]).strip().lower()
80
+ except GitCommandError:
81
+ fmt = ""
82
+
83
+ if fmt == "ssh":
84
+ return
85
+
86
+ warning("Detected 'commit.gpgsign=true' with no GPG key configured.")
87
+ warning("Disabling global GPG signing to prevent commit failures.")
88
+
89
+ try:
90
+ run_command(["git", "config", "--global", "--unset", "gpg.program"])
91
+ except GitCommandError:
92
+ pass
93
+
94
+
@@ -0,0 +1,132 @@
1
+ from pygitgo.utils.cli_io import info, success, warning, error
2
+ from pygitgo.utils.platform import get_platform
3
+ from pygitgo.utils.executor import run_command
4
+ from pygitgo.exceptions import GitCommandError
5
+ from pygitgo.utils.platform import open_url
6
+ from . import ssh_utils
7
+ import os
8
+
9
+
10
+ def _configure_ssh_signing(key_path):
11
+ try:
12
+ run_command(["git", "config", "--global", "gpg.format", "ssh"])
13
+ run_command(["git", "config", "--global", "user.signingkey", str(key_path)])
14
+ except Exception:
15
+ pass
16
+
17
+
18
+ def login():
19
+ from .account import sanitize_signing_config, ensure_user_configure
20
+
21
+ sanitize_signing_config()
22
+
23
+ key_path = ssh_utils.get_ssh_key_path()
24
+ already_logged_in = key_path.exists()
25
+ ok_text = "Already logged in via GitGo." if already_logged_in else "GitHub connection verified."
26
+
27
+ if ssh_utils.check_connection(ok_text=ok_text, fail_text="Not connected to GitHub."):
28
+ if already_logged_in:
29
+ _configure_ssh_signing(key_path)
30
+ ensure_user_configure(default_username=ssh_utils.get_github_username())
31
+ else:
32
+ warning("GitHub SSH connection is active, but NOT via a GitGo-managed key.")
33
+ warning("To use GitGo's full login (SSH + verified commits), you must log out first.")
34
+ info("Run: gitgo user logout")
35
+ info("Then run: gitgo user login")
36
+
37
+ return True
38
+
39
+ info("Initiating login sequence...")
40
+ while True:
41
+ email = input("Enter your email for GitHub: ").strip()
42
+ if "@" in email and "." in email:
43
+ break
44
+ else:
45
+ error("Please enter a valid email address.")
46
+
47
+ key_path = ssh_utils.generate_ssh_key(email=email)
48
+ pub_key_path = str(key_path) + ".pub"
49
+
50
+ with open(pub_key_path, "r") as f:
51
+ pub_key = f.read()
52
+
53
+ if not pub_key:
54
+ error("Failed to read the generated public key.")
55
+ return False
56
+
57
+ success("SSH Key generated successfully!")
58
+
59
+ print()
60
+ print(" Your SSH public key:")
61
+ print()
62
+ print(f" {pub_key.strip()}")
63
+ print()
64
+
65
+ info("Copy the key above, then add it TWICE on GitHub:")
66
+ info(" 1. Authentication Key (for pushing and pulling)")
67
+ info(" 2. Signing Key (for Verified commits)")
68
+ info("Same key text for both entries.")
69
+
70
+ input("\nOnce you've copied the key, press Enter to open GitHub...")
71
+
72
+ open_url("https://github.com/settings/ssh/new")
73
+
74
+ input(
75
+ "\nAfter adding both keys on GitHub,\n"
76
+ "come back here and press Enter to verify the connection..."
77
+ )
78
+
79
+ ssh_utils.clear_ssh_cache()
80
+
81
+ if ssh_utils.check_connection(ok_text="Login successful. You are connected.", fail_text="SSH key not recognised by GitHub."):
82
+ github_username = ssh_utils.get_github_username()
83
+ ensure_user_configure(default_email=email, default_username=github_username)
84
+ return True
85
+
86
+ error("Login Failed. The SSH key may not have been added to GitHub correctly.")
87
+ info("Possible causes:")
88
+ info(" 1. The key was not pasted on GitHub")
89
+
90
+ if get_platform() == "windows":
91
+ info(" 2. SSH agent is not running — run in PowerShell (as Administrator):")
92
+ info(" Set-Service ssh-agent -StartupType Automatic")
93
+ info(" Start-Service ssh-agent")
94
+ info(" Then run 'gitgo user login' again.")
95
+ else:
96
+ info(" 2. SSH agent is not running (try: eval $(ssh-agent) && ssh-add)")
97
+
98
+ info(" 3. Network or firewall is blocking SSH connections")
99
+ info("Need help? Full guide: https://github.com/Huerte/GitGo/blob/main/docs/login-guide.md")
100
+ return False
101
+
102
+
103
+ def logout():
104
+ key_path = ssh_utils.get_ssh_key_path()
105
+ if not key_path.exists():
106
+ warning("You are already logged out (no keys found).")
107
+ return False
108
+
109
+ try:
110
+
111
+ os.remove(key_path)
112
+
113
+ pub_key_path = str(key_path) + ".pub"
114
+ if os.path.exists(pub_key_path):
115
+ os.remove(pub_key_path)
116
+
117
+ try:
118
+ run_command(["git", "config", "--global", "--unset-all", "user.name"], loading_msg="Clearing Git username...", ok_text="Git username cleared.")
119
+ except GitCommandError:
120
+ pass
121
+
122
+ try:
123
+ run_command(["git", "config", "--global", "--unset-all", "user.email"], loading_msg="Clearing Git email...", ok_text="Git email cleared.")
124
+ except GitCommandError:
125
+ pass
126
+
127
+ success("User successfully logged out.")
128
+ return True
129
+
130
+ except Exception as e:
131
+ error(f"Failed to remove SSH keys\nCAUSE OF ERROR: {e}")
132
+ return False
@@ -1,14 +1,19 @@
1
1
  from pygitgo.exceptions import GitCommandError, GitGoError
2
- from pygitgo.utils.colors import info, success, warning
2
+ from pygitgo.utils.cli_io import info, success, warning
3
+ from pygitgo.utils.platform import get_platform
3
4
  from pygitgo.utils.executor import run_command
4
5
  from pathlib import Path
5
6
  import subprocess
7
+ import time
6
8
  import os
7
9
  import re
8
10
 
9
11
 
10
12
  SSH_TIMEOUT_SECONDS = 10
11
13
 
14
+ _cached_ssh_response = None
15
+ _cache_populated = False
16
+
12
17
 
13
18
  def ensure_github_known_host():
14
19
  known_hosts = Path.home() / ".ssh" / "known_hosts"
@@ -47,13 +52,50 @@ def _get_github_ssh_response():
47
52
  except (subprocess.TimeoutExpired, OSError):
48
53
  return None
49
54
 
50
- def check_connection():
55
+
56
+ def _get_cached_ssh_response():
57
+ global _cached_ssh_response, _cache_populated
58
+ if not _cache_populated:
59
+ _cached_ssh_response = _get_github_ssh_response()
60
+ _cache_populated = True
61
+ return _cached_ssh_response
62
+
63
+
64
+ def clear_ssh_cache():
65
+ global _cached_ssh_response, _cache_populated
66
+ _cached_ssh_response = None
67
+ _cache_populated = False
68
+
69
+
70
+ def check_connection(ok_text=None, fail_text=None):
71
+ from yaspin import yaspin
72
+ import sys
73
+
51
74
  ensure_github_known_host()
52
- output = _get_github_ssh_response()
53
- return output is not None and "successfully authenticated" in output
75
+
76
+ kwargs = {"text": ok_text or "Verifying GitHub connection..."}
77
+ if sys.stdout.isatty():
78
+ kwargs["color"] = "cyan"
79
+ spinner = yaspin(**kwargs)
80
+ spinner.start()
81
+
82
+ output = _get_cached_ssh_response()
83
+ connected = output is not None and "successfully authenticated" in output
84
+
85
+ if connected:
86
+ if ok_text:
87
+ spinner.text = ok_text
88
+ spinner.ok("✔")
89
+ else:
90
+ if fail_text:
91
+ spinner.text = fail_text
92
+ spinner.fail("✖")
93
+
94
+ return connected
95
+
54
96
 
55
97
  def get_github_username():
56
- output = _get_github_ssh_response()
98
+ output = _get_cached_ssh_response()
57
99
  if output and "Hi " in output and "!" in output:
58
100
  try:
59
101
  return output.split("Hi ")[1].split("!")[0]
@@ -73,6 +115,9 @@ def generate_ssh_key(email):
73
115
  key_path.parent.mkdir(parents=True)
74
116
 
75
117
  if key_path.exists():
118
+ from pygitgo.utils.cli_io import confirm
119
+ if not confirm(f"SSH key {key_path} already exists. Overwrite it? (y/n): ", destructive=True):
120
+ raise GitGoError("SSH key generation aborted. Please back up your existing key or configure it manually.")
76
121
  os.remove(key_path)
77
122
  if (key_path.parent / f"{key_path.name}.pub").exists():
78
123
  os.remove(key_path.parent / f"{key_path.name}.pub")
@@ -92,10 +137,8 @@ def generate_ssh_key(email):
92
137
  "\nFailed to generate SSH key. Is 'ssh-keygen' installed on your system?\n"
93
138
  f"Details: {e}"
94
139
  )
95
- try:
96
- run_command(["ssh-add", str(key_path)])
97
- except (GitCommandError, OSError):
98
- pass
140
+
141
+ ensure_ssh_agent(key_path, quiet=True)
99
142
 
100
143
  return key_path
101
144
 
@@ -114,3 +157,42 @@ def convert_https_to_ssh(url):
114
157
 
115
158
  def is_ssh_url(url):
116
159
  return url.strip().startswith("git@")
160
+
161
+ def _try_ssh_add(key_path):
162
+ try:
163
+ run_command(["ssh-add", str(key_path)])
164
+ return True
165
+ except (GitCommandError, OSError):
166
+ return False
167
+
168
+ def ensure_ssh_agent(key_path, quiet=False):
169
+ if _try_ssh_add(key_path):
170
+ return True
171
+
172
+ if get_platform() == "windows":
173
+ try:
174
+ subprocess.run(
175
+ ["sc", "start", "ssh-agent"],
176
+ capture_output=True, timeout=5
177
+ )
178
+ except Exception:
179
+ pass
180
+
181
+ time.sleep(1)
182
+
183
+ if _try_ssh_add(key_path):
184
+ return True
185
+
186
+ if not quiet:
187
+ warning("SSH agent is not running. Key may not persist across sessions.")
188
+ info("To fix this permanently, run in PowerShell (as Administrator):")
189
+ info(" Set-Service ssh-agent -StartupType Automatic")
190
+ info(" Start-Service ssh-agent")
191
+
192
+ else:
193
+ if not quiet:
194
+ warning("SSH agent is not running. Key may not persist across sessions.")
195
+ info("Run: eval $(ssh-agent) && ssh-add")
196
+
197
+ return False
198
+
@@ -1,5 +1,6 @@
1
1
  from pygitgo.utils.config import get_config, set_config
2
- from pygitgo.utils.colors import error, warning, info
2
+ from pygitgo.utils.cli_io import warning, info
3
+ from pygitgo.exceptions import GitGoError
3
4
 
4
5
 
5
6
  def config_operation(args):
@@ -10,14 +11,11 @@ def config_operation(args):
10
11
 
11
12
  VALID_KEYS = ["default-branch", "default-message"]
12
13
  if key not in VALID_KEYS:
13
- error(f"\nInvalid configuration key: '{key}'")
14
- warning(f"Valid keys are: {', '.join(VALID_KEYS)}\n")
15
- return
14
+ raise GitGoError(f"Invalid configuration key: '{key}'. Valid keys are: {', '.join(VALID_KEYS)}")
16
15
 
17
16
  if action == 'set':
18
17
  if not value:
19
- error("\nYou must provide a value to set!\n")
20
- return
18
+ raise GitGoError("You must provide a value to set!")
21
19
  set_config(key, value)
22
20
  elif action == 'get':
23
21
  current_value = get_config(key, None)
@@ -1,4 +1,4 @@
1
- from pygitgo.utils.colors import success, error, info
1
+ from pygitgo.utils.cli_io import success, error, info, confirm
2
2
  from pygitgo.exceptions import GitCommandError, GitGoError
3
3
  from pygitgo.utils.executor import run_command
4
4
  from pygitgo.utils.config import get_config
@@ -30,11 +30,11 @@ def is_branch_exist(branch):
30
30
  return bool(run_command(["git", "branch", "-r", "--list", f"*/{branch}"])) or bool(run_command(["git", "branch", "--list", branch]))
31
31
 
32
32
 
33
- def git_new_branch(branch):
33
+ def git_new_branch(branch, ok_text=None):
34
34
  try:
35
- run_command(["git", "checkout", "-b", branch], loading_msg=f"Creating branch '{branch}'...")
36
- print()
37
- success(f"Branch '{branch}' created.")
35
+ if not ok_text:
36
+ ok_text = f"Branch '{branch}' created."
37
+ run_command(["git", "checkout", "-b", branch], loading_msg=f"Creating branch '{branch}'...", ok_text=ok_text)
38
38
  except GitCommandError:
39
39
  try:
40
40
  current = get_current_branch()
@@ -44,9 +44,8 @@ def git_new_branch(branch):
44
44
  if current == branch:
45
45
  info(f"Already on branch '{branch}'. Continuing...")
46
46
  else:
47
- error(f"Failed to create branch '{branch}'! It may already exist.")
48
- choice = input("\nWould you like to jump to the existing branch instead? (y/n): ").strip().lower()
49
- if choice == "y":
47
+ error(f"Failed to create branch '{branch}'. It may already exist.")
48
+ if confirm(f"Switch to the existing branch '{branch}' instead? (y/n): "):
50
49
  from pygitgo.commands.jump import jump_operation
51
50
  jump_operation(Namespace(branch=branch))
52
51
  else:
@@ -1,9 +1,10 @@
1
1
  from pygitgo.auth.ssh_utils import convert_https_to_ssh, get_ssh_key_path, is_ssh_url, check_connection
2
- from pygitgo.utils.colors import info, success, warning, error
3
2
  from pygitgo.exceptions import GitGoError, GitCommandError
3
+ from pygitgo.auth.account import sanitize_signing_config
4
4
  from pygitgo.commands.git_remote import handle_rebase
5
5
  from pygitgo.utils.config import get_default_branch
6
6
  from pygitgo.utils.executor import run_command
7
+ from pygitgo.utils.cli_io import info, warning
7
8
  import os
8
9
 
9
10
 
@@ -18,44 +19,52 @@ def _get_signing_flags():
18
19
  ]
19
20
 
20
21
 
21
- def git_commit(commit_message, loading_msg="Commiting changes...", skip_staging=False):
22
+ def git_commit(commit_message, loading_msg="Committing changes...", skip_staging=False, ok_text=None):
23
+ if not ok_text:
24
+ ok_text = "Changes committed."
22
25
  try:
23
26
  status_result = run_command(["git", "status", "--porcelain"])
24
27
  if not status_result.strip():
25
28
  return False
26
29
  except GitCommandError:
27
30
  return False
31
+
32
+ sanitize_signing_config()
28
33
 
29
34
  if not skip_staging:
30
- run_command(["git", "add", "."], loading_msg="Staging files...")
35
+ run_command(["git", "add", "."], loading_msg="Staging files...", ok_text="Files staged.")
31
36
 
32
37
  clean_message = commit_message.strip('"\'')
33
38
 
34
39
  signing_flags = _get_signing_flags()
35
40
  commit_command = ["git"] + signing_flags + ["commit", "-S", "-m", clean_message]
36
- run_command(commit_command, loading_msg=loading_msg)
41
+ run_command(commit_command, loading_msg=loading_msg, ok_text=ok_text)
37
42
 
38
43
  return True
39
44
 
40
45
 
41
- def git_init():
46
+ def git_init(ok_text=None):
42
47
  if os.path.isdir(".git"):
43
48
  warning("Already a git repository! Skipping init...")
44
49
  return False
45
50
 
46
51
  default_main_branch = get_default_branch()
47
52
 
53
+ if not ok_text:
54
+ ok_text = "Git repository initialized."
55
+
48
56
  try:
49
- run_command(["git", "init", "-b", default_main_branch], loading_msg="Initializing git repository...")
57
+ run_command(["git", "init", "-b", default_main_branch], loading_msg="Initializing git repository...", ok_text=ok_text)
50
58
  except GitCommandError:
51
59
  run_command(["git", "init"], loading_msg="Initializing git repository...")
52
- run_command(["git", "checkout", "-b", default_main_branch])
60
+ run_command(["git", "checkout", "-b", default_main_branch], ok_text=ok_text)
53
61
 
54
- success("Git repository initialized.")
55
62
  return True
56
63
 
57
64
 
58
- def git_push(branch):
65
+ def git_push(branch, ok_text=None):
66
+ if not ok_text:
67
+ ok_text = f"Pushed to remote branch '{branch}'."
59
68
  try:
60
69
  remote_url = run_command(["git", "remote", "get-url", "origin"]).strip()
61
70
  except GitCommandError:
@@ -64,13 +73,11 @@ def git_push(branch):
64
73
  if remote_url and not is_ssh_url(remote_url) and check_connection():
65
74
  ssh_url = convert_https_to_ssh(remote_url)
66
75
  if ssh_url:
67
- run_command(["git", "remote", "set-url", "origin", ssh_url], loading_msg="Converting remote from HTTPS to SSH for secure push...")
68
- success(f"Remote updated to: {ssh_url}")
76
+ run_command(["git", "remote", "set-url", "origin", ssh_url], loading_msg="Converting remote from HTTPS to SSH for secure push...", ok_text=f"Remote updated to: {ssh_url}")
69
77
 
70
78
  try:
71
- run_command(["git", "push", "-u", "origin", branch], loading_msg=f"Pushing to remote branch '{branch}'...")
79
+ run_command(["git", "push", "-u", "origin", branch], loading_msg=f"Pushing to remote branch '{branch}'...", ok_text=ok_text, err_text="Push failed: verify your remote URL and SSH key, then try again.")
72
80
  except (GitCommandError, OSError) as e:
73
- error("Push failed — verify your remote URL and SSH key, then try again.")
74
81
  info("Run: git remote -v to inspect your current remote.")
75
82
  if "rebase in progress" in str(e):
76
83
  handle_rebase()
@@ -1,5 +1,5 @@
1
- from pygitgo.utils.colors import info, success, warning, error
2
1
  from pygitgo.exceptions import GitCommandError, GitGoError
2
+ from pygitgo.utils.cli_io import info, warning, error
3
3
  from pygitgo.utils.executor import run_command
4
4
 
5
5
 
@@ -9,30 +9,25 @@ def add_remote_origin(repo_url):
9
9
  try:
10
10
  existing_remote = run_command(["git", "remote", "get-url", "origin"])
11
11
  warning(f"Remote origin already exists: {existing_remote}")
12
- run_command(["git", "remote", "set-url", "origin", clean_url], loading_msg="Updating remote URL...")
12
+ run_command(["git", "remote", "set-url", "origin", clean_url], loading_msg="Updating remote URL...", ok_text=f"Remote origin set to: {clean_url}")
13
13
  except GitCommandError:
14
- run_command(["git", "remote", "add", "origin", clean_url], loading_msg="Adding remote origin...")
14
+ run_command(["git", "remote", "add", "origin", clean_url], loading_msg="Adding remote origin...", ok_text=f"Remote origin set to: {clean_url}")
15
15
 
16
- success(f"Remote origin set to: {clean_url}")
17
16
 
18
-
19
- def confirm_remote_link():
17
+ def confirm_remote_link(ok_text=None):
18
+ if not ok_text:
19
+ ok_text = "Remote is reachable."
20
20
  try:
21
- run_command(["git", "ls-remote", "origin"], loading_msg="Testing connection to remote...")
22
- success("Remote is reachable.")
21
+ run_command(["git", "ls-remote", "origin"], loading_msg="Testing connection to remote...", ok_text=ok_text)
23
22
  return True
24
23
  except GitCommandError:
25
- error("Connection failed — verify the URL and your SSH key.")
26
24
  info("Run: git remote -v to inspect your current remote.")
27
- return False
28
-
29
-
30
-
25
+ raise GitGoError("Connection failed — verify the URL and your SSH key.")
31
26
 
32
27
 
33
28
  def check_and_sync_branch(branch):
34
29
  try:
35
- run_command(["git", "fetch", "origin"], loading_msg="Checking if branch is up to date...")
30
+ run_command(["git", "fetch", "origin"], loading_msg="Checking if branch is up to date...", ok_text="Remote fetched.")
36
31
 
37
32
  try:
38
33
  local_commit = run_command(["git", "rev-parse", branch])
@@ -44,14 +39,13 @@ def check_and_sync_branch(branch):
44
39
  )
45
40
  if behind_check and int(behind_check) > 0:
46
41
  warning(f"Local branch is behind remote by {behind_check} commit(s). Pulling changes...")
47
- output = run_command(["git", "pull", "--rebase", "origin", branch], loading_msg="Pulling changes from remote...")
42
+ output = run_command(["git", "pull", "--rebase", "origin", branch], loading_msg="Pulling changes from remote...", ok_text="Synced with remote.")
48
43
  if output:
49
44
  print(output)
50
- success("Synced with remote.")
51
45
  else:
52
- success("Branch is up to date.")
46
+ info("Branch is up to date.")
53
47
  else:
54
- success("Branch is already up to date.")
48
+ info("Branch is already up to date.")
55
49
  except (GitCommandError, ValueError):
56
50
  warning("Remote branch doesn't exist yet. First push will create it.")
57
51
  except (GitCommandError, OSError):
@@ -1,6 +1,6 @@
1
1
  from pygitgo.commands.git_core import git_init
2
- from pygitgo.utils.colors import info, success
3
2
  from pygitgo.exceptions import GitGoError
3
+ from pygitgo.utils.cli_io import info
4
4
  import urllib.request
5
5
  import urllib.error
6
6
  import zipfile
@@ -172,19 +172,31 @@ def _parse_template_slug(template):
172
172
 
173
173
 
174
174
  def _download_and_extract_template(template_slug, target_dir):
175
+ from yaspin import yaspin
176
+ import sys
175
177
  url = f"https://api.github.com/repos/{template_slug}/zipball"
176
178
  req = urllib.request.Request(url, headers={"User-Agent": "GitGo-CLI"})
177
- info(f"Downloading template from GitHub: {template_slug}...")
179
+ kwargs = {"text": f"Downloading template from GitHub: {template_slug}..."}
180
+ if sys.stdout.isatty():
181
+ kwargs["color"] = "cyan"
182
+ spinner = yaspin(**kwargs)
183
+ spinner.start()
178
184
  try:
179
185
  with urllib.request.urlopen(req, timeout=30) as response:
180
186
  zip_data = response.read()
181
187
  except urllib.error.HTTPError as e:
182
188
  if e.code == 404:
189
+ spinner.text = f"Template '{template_slug}' not found on GitHub."
190
+ spinner.fail("✖")
183
191
  raise GitGoError(
184
192
  f"Template repository '{template_slug}' not found on GitHub."
185
193
  )
194
+ spinner.text = f"Failed to download template: HTTP {e.code}"
195
+ spinner.fail("✖")
186
196
  raise GitGoError(f"Failed to download template: HTTP {e.code}")
187
197
  except Exception as e:
198
+ spinner.text = f"Network error downloading template: {e}"
199
+ spinner.fail("✖")
188
200
  raise GitGoError(f"Network error downloading template: {e}")
189
201
 
190
202
  try:
@@ -207,8 +219,11 @@ def _download_and_extract_template(template_slug, target_dir):
207
219
  os.makedirs(os.path.dirname(dest), exist_ok=True)
208
220
  with zf.open(member) as src, open(dest, "wb") as out:
209
221
  out.write(src.read())
210
- success("Template extracted successfully.")
222
+ spinner.text = "Template extracted successfully."
223
+ spinner.ok("✔")
211
224
  except Exception as e:
225
+ spinner.text = f"Failed to extract template: {e}"
226
+ spinner.fail("✖")
212
227
  raise GitGoError(f"Failed to extract template: {e}")
213
228
 
214
229
 
@@ -277,7 +292,7 @@ def _scaffold_language(lang, target_dir, name):
277
292
  info(f"Created {name}.csproj and Program.cs")
278
293
 
279
294
 
280
- def init_operation(args):
295
+ def init_operation(args, standalone=False):
281
296
  target_dir = args.name
282
297
 
283
298
  if os.path.exists(target_dir) and os.listdir(target_dir):
@@ -294,10 +309,12 @@ def init_operation(args):
294
309
  _scaffold_language(args.lang, target_dir, target_dir)
295
310
 
296
311
  os.chdir(target_dir)
297
- git_init()
312
+ git_init(ok_text=f"Initialized empty Git repository in {os.path.abspath('.')}")
298
313
 
299
- success(f"\nInitialized empty Git repository in {os.path.abspath('.')}")
300
- info("Next step: Create a remote repo with 'gitgo new <name>'")
314
+ if standalone:
315
+ from pygitgo.utils.cli_io import banner
316
+ banner("LOCAL SCAFFOLD CONSTRUCTED.", "PROJECT STRUCTURE AND REPOSITORY INITIALIZED.")
317
+ info(f"Next steps:\n cd {target_dir}\n gitgo repo\n gitgo link <url>")
301
318
 
302
319
  except Exception as e:
303
320
  if os.path.exists(target_dir) and not os.listdir(target_dir):