gitpush-tool 0.2.10__tar.gz → 0.3.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitpush-tool
3
- Version: 0.2.10
3
+ Version: 0.3.0
4
4
  Summary: Supercharged Git push tool with automatic GitHub repo creation and pushing
5
5
  Home-page: https://github.com/inevitablegs/gitpush
6
6
  Author: Ganesh Sonawane
@@ -0,0 +1 @@
1
+ __version__ = "0.3.0"
@@ -0,0 +1,970 @@
1
+ import os
2
+ import argparse
3
+ import sys
4
+ import subprocess
5
+ import shutil
6
+ import platform
7
+
8
+ import os
9
+ import sys
10
+ import json
11
+ import platform
12
+ import shutil
13
+ import tempfile
14
+ import urllib.request
15
+ import subprocess
16
+ from typing import Optional, Tuple
17
+
18
+ def check_gh_installed() -> bool:
19
+ """Check if GitHub CLI is installed with proper verification"""
20
+ if shutil.which("gh"):
21
+ try:
22
+ # Verify gh is actually working
23
+ subprocess.run(["gh", "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
24
+ return True
25
+ except:
26
+ # Found but not working - might be PATH issue
27
+ return False
28
+ return False
29
+
30
+ def install_gh_cli() -> bool:
31
+ """Main installation function with comprehensive error handling"""
32
+ system = platform.system()
33
+ machine = platform.machine().lower()
34
+
35
+ print("\n🔧 Installing GitHub CLI...")
36
+ print(f"📋 System: {system}, Architecture: {machine}")
37
+
38
+ try:
39
+ if system == "Windows":
40
+ return install_gh_cli_windows()
41
+ elif system == "Darwin":
42
+ return install_gh_cli_mac()
43
+ elif system == "Linux":
44
+ return install_gh_cli_linux()
45
+ else:
46
+ print(f"❌ Unsupported OS: {system}")
47
+ return False
48
+ except Exception as e:
49
+ print(f"❌ Installation failed: {str(e)}")
50
+ return False
51
+
52
+ def install_gh_cli_windows() -> bool:
53
+ """Windows installation with multiple fallback methods and PATH management"""
54
+ methods = [
55
+ try_winget_install,
56
+ try_scoop_install,
57
+ try_choco_install,
58
+ try_direct_msi_install,
59
+ try_direct_zip_install
60
+ ]
61
+
62
+ for method in methods:
63
+ if method():
64
+ if verify_gh_installation():
65
+ return True
66
+ print("⚠️ Trying next installation method...")
67
+
68
+ print("❌ All installation methods failed")
69
+ return False
70
+
71
+ def try_winget_install() -> bool:
72
+ """Attempt installation via winget"""
73
+ if not shutil.which("winget"):
74
+ return False
75
+
76
+ print("\n🔄 Attempting winget installation...")
77
+ try:
78
+ subprocess.run(
79
+ ["winget", "install", "--id", "GitHub.cli", "--silent", "--accept-package-agreements", "--accept-source-agreements"],
80
+ check=True,
81
+ stdout=subprocess.PIPE,
82
+ stderr=subprocess.PIPE
83
+ )
84
+ return True
85
+ except subprocess.CalledProcessError as e:
86
+ print(f"⚠️ winget failed: {e.stderr.decode().strip() if e.stderr else 'Unknown error'}")
87
+ return False
88
+
89
+ def try_scoop_install() -> bool:
90
+ """Attempt installation via scoop"""
91
+ if not shutil.which("scoop"):
92
+ return False
93
+
94
+ print("\n🔄 Attempting scoop installation...")
95
+ try:
96
+ subprocess.run(
97
+ ["scoop", "install", "gh"],
98
+ check=True,
99
+ stdout=subprocess.PIPE,
100
+ stderr=subprocess.PIPE
101
+ )
102
+ return True
103
+ except subprocess.CalledProcessError as e:
104
+ print(f"⚠️ scoop failed: {e.stderr.decode().strip() if e.stderr else 'Unknown error'}")
105
+ return False
106
+
107
+ def try_choco_install() -> bool:
108
+ """Attempt installation via chocolatey"""
109
+ if not shutil.which("choco"):
110
+ return False
111
+
112
+ print("\n🔄 Attempting chocolatey installation...")
113
+ try:
114
+ subprocess.run(
115
+ ["choco", "install", "gh", "-y"],
116
+ check=True,
117
+ stdout=subprocess.PIPE,
118
+ stderr=subprocess.PIPE
119
+ )
120
+ return True
121
+ except subprocess.CalledProcessError as e:
122
+ print(f"⚠️ chocolatey failed: {e.stderr.decode().strip() if e.stderr else 'Unknown error'}")
123
+ return False
124
+
125
+ def try_direct_msi_install() -> bool:
126
+ """Direct MSI installation with proper PATH handling"""
127
+ print("\n🔄 Attempting direct MSI installation...")
128
+ try:
129
+ # Get latest release info
130
+ release_info = get_github_release_info()
131
+ if not release_info:
132
+ return False
133
+
134
+ # Find appropriate MSI
135
+ msi_asset = next(
136
+ (a for a in release_info.get('assets', [])
137
+ if a['name'].endswith('_windows_amd64.msi') or
138
+ a['name'].endswith('_windows_386.msi')),
139
+ None
140
+ )
141
+
142
+ if not msi_asset:
143
+ print("❌ Could not find Windows MSI installer")
144
+ return False
145
+
146
+ # Download MSI
147
+ temp_dir = tempfile.mkdtemp()
148
+ msi_path = os.path.join(temp_dir, msi_asset['name'])
149
+ print(f"⬇️ Downloading {msi_asset['name']}...")
150
+ download_file(msi_asset['browser_download_url'], msi_path)
151
+
152
+ # Install with appropriate privileges
153
+ print("🛠 Installing...")
154
+ try:
155
+ # Try with admin privileges first
156
+ subprocess.run(
157
+ ["msiexec", "/i", msi_path, "/quiet", "/norestart"],
158
+ check=True,
159
+ stdout=subprocess.PIPE,
160
+ stderr=subprocess.PIPE
161
+ )
162
+ except subprocess.CalledProcessError:
163
+ # Fallback to user installation
164
+ print("⚠️ Admin install failed, trying user installation...")
165
+ subprocess.run(
166
+ ["msiexec", "/i", msi_path, "/quiet", "/norestart", "ALLUSERS=2"],
167
+ check=True,
168
+ stdout=subprocess.PIPE,
169
+ stderr=subprocess.PIPE
170
+ )
171
+
172
+ # Clean up
173
+ shutil.rmtree(temp_dir, ignore_errors=True)
174
+
175
+ # Add to PATH if needed
176
+ program_files = os.environ.get("ProgramFiles", "C:\\Program Files")
177
+ gh_path = os.path.join(program_files, "GitHub CLI", "gh.exe")
178
+ if os.path.exists(gh_path):
179
+ add_to_path(os.path.dirname(gh_path))
180
+ return True
181
+
182
+ local_appdata = os.environ.get("LOCALAPPDATA", "")
183
+ gh_path = os.path.join(local_appdata, "GitHub CLI", "gh.exe")
184
+ if os.path.exists(gh_path):
185
+ add_to_path(os.path.dirname(gh_path))
186
+ return True
187
+
188
+ print("❌ Installation completed but couldn't find gh.exe")
189
+ return False
190
+
191
+ except Exception as e:
192
+ print(f"❌ MSI installation failed: {str(e)}")
193
+ shutil.rmtree(temp_dir, ignore_errors=True)
194
+ return False
195
+
196
+ def try_direct_zip_install() -> bool:
197
+ """Fallback ZIP installation for Windows"""
198
+ print("\n🔄 Attempting direct ZIP installation...")
199
+ try:
200
+ # Get latest release info
201
+ release_info = get_github_release_info()
202
+ if not release_info:
203
+ return False
204
+
205
+ # Find appropriate ZIP
206
+ zip_asset = next(
207
+ (a for a in release_info.get('assets', [])
208
+ if a['name'].endswith('windows_amd64.zip') or
209
+ a['name'].endswith('windows_386.zip')),
210
+ None
211
+ )
212
+
213
+ if not zip_asset:
214
+ print("❌ Could not find Windows ZIP package")
215
+ return False
216
+
217
+ # Download and extract
218
+ temp_dir = tempfile.mkdtemp()
219
+ zip_path = os.path.join(temp_dir, zip_asset['name'])
220
+ print(f"⬇️ Downloading {zip_asset['name']}...")
221
+ download_file(zip_asset['browser_download_url'], zip_path)
222
+
223
+ print("📦 Extracting...")
224
+ shutil.unpack_archive(zip_path, temp_dir)
225
+
226
+ # Find the binary
227
+ for root, _, files in os.walk(temp_dir):
228
+ if "gh.exe" in files:
229
+ bin_dir = root
230
+ break
231
+ else:
232
+ print("❌ Could not find gh.exe in extracted files")
233
+ shutil.rmtree(temp_dir, ignore_errors=True)
234
+ return False
235
+
236
+ # Install to local apps directory
237
+ install_dir = os.path.join(os.environ.get("LOCALAPPDATA", ""), "GitHubCLI")
238
+ os.makedirs(install_dir, exist_ok=True)
239
+
240
+ # Copy files
241
+ for item in os.listdir(bin_dir):
242
+ src = os.path.join(bin_dir, item)
243
+ dst = os.path.join(install_dir, item)
244
+ if os.path.isdir(src):
245
+ shutil.copytree(src, dst, dirs_exist_ok=True)
246
+ else:
247
+ shutil.copy2(src, dst)
248
+
249
+ # Add to PATH
250
+ add_to_path(install_dir)
251
+
252
+ # Clean up
253
+ shutil.rmtree(temp_dir, ignore_errors=True)
254
+
255
+ return True
256
+
257
+ except Exception as e:
258
+ print(f"❌ ZIP installation failed: {str(e)}")
259
+ shutil.rmtree(temp_dir, ignore_errors=True)
260
+ return False
261
+
262
+ def install_gh_cli_mac() -> bool:
263
+ """macOS installation with multiple methods"""
264
+ methods = [
265
+ try_brew_install,
266
+ try_direct_pkg_install,
267
+ try_direct_tar_install
268
+ ]
269
+
270
+ for method in methods:
271
+ if method():
272
+ if verify_gh_installation():
273
+ return True
274
+ print("⚠️ Trying next installation method...")
275
+
276
+ print("❌ All installation methods failed")
277
+ return False
278
+
279
+ def try_brew_install() -> bool:
280
+ """Attempt installation via Homebrew"""
281
+ if not shutil.which("brew"):
282
+ return False
283
+
284
+ print("\n🔄 Attempting Homebrew installation...")
285
+ try:
286
+ subprocess.run(
287
+ ["brew", "install", "gh"],
288
+ check=True,
289
+ stdout=subprocess.PIPE,
290
+ stderr=subprocess.PIPE
291
+ )
292
+ return True
293
+ except subprocess.CalledProcessError as e:
294
+ print(f"⚠️ Homebrew failed: {e.stderr.decode().strip() if e.stderr else 'Unknown error'}")
295
+ return False
296
+
297
+ def try_direct_pkg_install() -> bool:
298
+ """Direct PKG installation for macOS"""
299
+ print("\n🔄 Attempting direct PKG installation...")
300
+ try:
301
+ release_info = get_github_release_info()
302
+ if not release_info:
303
+ return False
304
+
305
+ pkg_asset = next(
306
+ (a for a in release_info.get('assets', [])
307
+ if a['name'].endswith('.pkg') and 'macOS' in a['name']),
308
+ None
309
+ )
310
+
311
+ if not pkg_asset:
312
+ print("❌ Could not find macOS PKG installer")
313
+ return False
314
+
315
+ temp_dir = tempfile.mkdtemp()
316
+ pkg_path = os.path.join(temp_dir, pkg_asset['name'])
317
+ print(f"⬇️ Downloading {pkg_asset['name']}...")
318
+ download_file(pkg_asset['browser_download_url'], pkg_path)
319
+
320
+ print("🛠 Installing...")
321
+ subprocess.run(
322
+ ["sudo", "installer", "-pkg", pkg_path, "-target", "/"],
323
+ check=True,
324
+ stdout=subprocess.PIPE,
325
+ stderr=subprocess.PIPE
326
+ )
327
+
328
+ shutil.rmtree(temp_dir, ignore_errors=True)
329
+ return True
330
+
331
+ except Exception as e:
332
+ print(f"❌ PKG installation failed: {str(e)}")
333
+ shutil.rmtree(temp_dir, ignore_errors=True)
334
+ return False
335
+
336
+ def try_direct_tar_install() -> bool:
337
+ """Fallback tar.gz installation for macOS"""
338
+ print("\n🔄 Attempting direct tar.gz installation...")
339
+ try:
340
+ release_info = get_github_release_info()
341
+ if not release_info:
342
+ return False
343
+
344
+ tar_asset = next(
345
+ (a for a in release_info.get('assets', [])
346
+ if a['name'].endswith('macOS_amd64.tar.gz')),
347
+ None
348
+ )
349
+
350
+ if not tar_asset:
351
+ print("❌ Could not find macOS tar.gz package")
352
+ return False
353
+
354
+ temp_dir = tempfile.mkdtemp()
355
+ tar_path = os.path.join(temp_dir, tar_asset['name'])
356
+ print(f"⬇️ Downloading {tar_asset['name']}...")
357
+ download_file(tar_asset['browser_download_url'], tar_path)
358
+
359
+ print("📦 Extracting...")
360
+ subprocess.run(
361
+ ["tar", "-xzf", tar_path, "-C", temp_dir],
362
+ check=True,
363
+ stdout=subprocess.PIPE,
364
+ stderr=subprocess.PIPE
365
+ )
366
+
367
+ # Find the binary
368
+ for root, _, files in os.walk(temp_dir):
369
+ if "gh" in files:
370
+ bin_path = os.path.join(root, "gh")
371
+ break
372
+ else:
373
+ print("❌ Could not find gh binary in extracted files")
374
+ shutil.rmtree(temp_dir, ignore_errors=True)
375
+ return False
376
+
377
+ # Install to /usr/local/bin
378
+ print("🛠 Installing to /usr/local/bin...")
379
+ subprocess.run(
380
+ ["sudo", "install", "-m", "755", bin_path, "/usr/local/bin/gh"],
381
+ check=True,
382
+ stdout=subprocess.PIPE,
383
+ stderr=subprocess.PIPE
384
+ )
385
+
386
+ shutil.rmtree(temp_dir, ignore_errors=True)
387
+ return True
388
+
389
+ except Exception as e:
390
+ print(f"❌ tar.gz installation failed: {str(e)}")
391
+ shutil.rmtree(temp_dir, ignore_errors=True)
392
+ return False
393
+
394
+ def install_gh_cli_linux() -> bool:
395
+ """Linux installation with distro detection and multiple methods"""
396
+ methods = []
397
+
398
+ # Detect distribution
399
+ if os.path.exists("/etc/debian_version"):
400
+ methods.extend([
401
+ try_apt_install,
402
+ try_deb_install
403
+ ])
404
+ elif os.path.exists("/etc/redhat-release"):
405
+ methods.extend([
406
+ try_yum_install,
407
+ try_dnf_install
408
+ ])
409
+ elif os.path.exists("/etc/arch-release"):
410
+ methods.extend([
411
+ try_pacman_install
412
+ ])
413
+ else:
414
+ print("⚠️ Unknown Linux distribution, trying generic methods")
415
+
416
+ # Add fallback methods
417
+ methods.extend([
418
+ try_tar_install_linux,
419
+ try_script_install
420
+ ])
421
+
422
+ for method in methods:
423
+ if method():
424
+ if verify_gh_installation():
425
+ return True
426
+ print("⚠️ Trying next installation method...")
427
+
428
+ print("❌ All installation methods failed")
429
+ return False
430
+
431
+ def try_apt_install() -> bool:
432
+ """APT installation for Debian/Ubuntu"""
433
+ if not shutil.which("apt"):
434
+ return False
435
+
436
+ print("\n🔄 Attempting apt installation...")
437
+ try:
438
+ subprocess.run(
439
+ ["sudo", "apt", "update"],
440
+ check=True,
441
+ stdout=subprocess.PIPE,
442
+ stderr=subprocess.PIPE
443
+ )
444
+ subprocess.run(
445
+ ["sudo", "apt", "install", "-y", "gh"],
446
+ check=True,
447
+ stdout=subprocess.PIPE,
448
+ stderr=subprocess.PIPE
449
+ )
450
+ return True
451
+ except subprocess.CalledProcessError as e:
452
+ print(f"⚠️ apt failed: {e.stderr.decode().strip() if e.stderr else 'Unknown error'}")
453
+ return False
454
+
455
+ def try_deb_install() -> bool:
456
+ """Direct DEB package installation"""
457
+ print("\n🔄 Attempting deb package installation...")
458
+ try:
459
+ release_info = get_github_release_info()
460
+ if not release_info:
461
+ return False
462
+
463
+ deb_asset = next(
464
+ (a for a in release_info.get('assets', [])
465
+ if a['name'].endswith('linux_amd64.deb') or
466
+ a['name'].endswith('linux_arm64.deb')),
467
+ None
468
+ )
469
+
470
+ if not deb_asset:
471
+ print("❌ Could not find DEB package")
472
+ return False
473
+
474
+ temp_dir = tempfile.mkdtemp()
475
+ deb_path = os.path.join(temp_dir, deb_asset['name'])
476
+ print(f"⬇️ Downloading {deb_asset['name']}...")
477
+ download_file(deb_asset['browser_download_url'], deb_path)
478
+
479
+ print("🛠 Installing...")
480
+ subprocess.run(
481
+ ["sudo", "dpkg", "-i", deb_path],
482
+ check=True,
483
+ stdout=subprocess.PIPE,
484
+ stderr=subprocess.PIPE
485
+ )
486
+
487
+ # Fix potential dependencies
488
+ subprocess.run(
489
+ ["sudo", "apt", "--fix-broken", "install", "-y"],
490
+ stdout=subprocess.PIPE,
491
+ stderr=subprocess.PIPE
492
+ )
493
+
494
+ shutil.rmtree(temp_dir, ignore_errors=True)
495
+ return True
496
+
497
+ except Exception as e:
498
+ print(f"❌ DEB installation failed: {str(e)}")
499
+ shutil.rmtree(temp_dir, ignore_errors=True)
500
+ return False
501
+
502
+ def try_yum_install() -> bool:
503
+ """YUM installation for RHEL/CentOS"""
504
+ if not shutil.which("yum"):
505
+ return False
506
+
507
+ print("\n🔄 Attempting yum installation...")
508
+ try:
509
+ subprocess.run(
510
+ ["sudo", "yum", "install", "-y", "gh"],
511
+ check=True,
512
+ stdout=subprocess.PIPE,
513
+ stderr=subprocess.PIPE
514
+ )
515
+ return True
516
+ except subprocess.CalledProcessError as e:
517
+ print(f"⚠️ yum failed: {e.stderr.decode().strip() if e.stderr else 'Unknown error'}")
518
+ return False
519
+
520
+ def try_dnf_install() -> bool:
521
+ """DNF installation for Fedora"""
522
+ if not shutil.which("dnf"):
523
+ return False
524
+
525
+ print("\n🔄 Attempting dnf installation...")
526
+ try:
527
+ subprocess.run(
528
+ ["sudo", "dnf", "install", "-y", "gh"],
529
+ check=True,
530
+ stdout=subprocess.PIPE,
531
+ stderr=subprocess.PIPE
532
+ )
533
+ return True
534
+ except subprocess.CalledProcessError as e:
535
+ print(f"⚠️ dnf failed: {e.stderr.decode().strip() if e.stderr else 'Unknown error'}")
536
+ return False
537
+
538
+ def try_pacman_install() -> bool:
539
+ """Pacman installation for Arch"""
540
+ if not shutil.which("pacman"):
541
+ return False
542
+
543
+ print("\n🔄 Attempting pacman installation...")
544
+ try:
545
+ subprocess.run(
546
+ ["sudo", "pacman", "-Sy", "--noconfirm", "github-cli"],
547
+ check=True,
548
+ stdout=subprocess.PIPE,
549
+ stderr=subprocess.PIPE
550
+ )
551
+ return True
552
+ except subprocess.CalledProcessError as e:
553
+ print(f"⚠️ pacman failed: {e.stderr.decode().strip() if e.stderr else 'Unknown error'}")
554
+ return False
555
+
556
+ def try_tar_install_linux() -> bool:
557
+ """Generic tar.gz installation for Linux"""
558
+ print("\n🔄 Attempting tar.gz installation...")
559
+ try:
560
+ release_info = get_github_release_info()
561
+ if not release_info:
562
+ return False
563
+
564
+ tar_asset = next(
565
+ (a for a in release_info.get('assets', [])
566
+ if a['name'].endswith('linux_amd64.tar.gz') or
567
+ a['name'].endswith('linux_arm64.tar.gz')),
568
+ None
569
+ )
570
+
571
+ if not tar_asset:
572
+ print("❌ Could not find tar.gz package")
573
+ return False
574
+
575
+ temp_dir = tempfile.mkdtemp()
576
+ tar_path = os.path.join(temp_dir, tar_asset['name'])
577
+ print(f"⬇️ Downloading {tar_asset['name']}...")
578
+ download_file(tar_asset['browser_download_url'], tar_path)
579
+
580
+ print("📦 Extracting...")
581
+ subprocess.run(
582
+ ["tar", "-xzf", tar_path, "-C", temp_dir],
583
+ check=True,
584
+ stdout=subprocess.PIPE,
585
+ stderr=subprocess.PIPE
586
+ )
587
+
588
+ # Find the binary
589
+ for root, _, files in os.walk(temp_dir):
590
+ if "gh" in files:
591
+ bin_path = os.path.join(root, "gh")
592
+ break
593
+ else:
594
+ print("❌ Could not find gh binary in extracted files")
595
+ shutil.rmtree(temp_dir, ignore_errors=True)
596
+ return False
597
+
598
+ # Install to /usr/local/bin
599
+ print("🛠 Installing to /usr/local/bin...")
600
+ subprocess.run(
601
+ ["sudo", "install", "-m", "755", bin_path, "/usr/local/bin/gh"],
602
+ check=True,
603
+ stdout=subprocess.PIPE,
604
+ stderr=subprocess.PIPE
605
+ )
606
+
607
+ shutil.rmtree(temp_dir, ignore_errors=True)
608
+ return True
609
+
610
+ except Exception as e:
611
+ print(f"❌ tar.gz installation failed: {str(e)}")
612
+ shutil.rmtree(temp_dir, ignore_errors=True)
613
+ return False
614
+
615
+ def try_script_install() -> bool:
616
+ """Fallback script installation"""
617
+ print("\n🔄 Attempting script installation...")
618
+ try:
619
+ subprocess.run(
620
+ ["curl", "-fsSL", "https://cli.github.com/packages/githubcli-archive-keyring.gpg", "|", "sudo", "dd", "of=/usr/share/keyrings/githubcli-archive-keyring.gpg"],
621
+ check=True,
622
+ stdout=subprocess.PIPE,
623
+ stderr=subprocess.PIPE
624
+ )
625
+ subprocess.run(
626
+ ["sudo", "chmod", "go+r", "/usr/share/keyrings/githubcli-archive-keyring.gpg"],
627
+ check=True,
628
+ stdout=subprocess.PIPE,
629
+ stderr=subprocess.PIPE
630
+ )
631
+ subprocess.run(
632
+ ['echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null'],
633
+ shell=True,
634
+ check=True,
635
+ stdout=subprocess.PIPE,
636
+ stderr=subprocess.PIPE
637
+ )
638
+ subprocess.run(
639
+ ["sudo", "apt", "update"],
640
+ check=True,
641
+ stdout=subprocess.PIPE,
642
+ stderr=subprocess.PIPE
643
+ )
644
+ subprocess.run(
645
+ ["sudo", "apt", "install", "-y", "gh"],
646
+ check=True,
647
+ stdout=subprocess.PIPE,
648
+ stderr=subprocess.PIPE
649
+ )
650
+ return True
651
+ except subprocess.CalledProcessError as e:
652
+ print(f"⚠️ Script installation failed: {e.stderr.decode().strip() if e.stderr else 'Unknown error'}")
653
+ return False
654
+
655
+ def get_github_release_info() -> Optional[dict]:
656
+ """Get latest release info from GitHub API"""
657
+ try:
658
+ with urllib.request.urlopen("https://api.github.com/repos/cli/cli/releases/latest") as response:
659
+ return json.loads(response.read().decode())
660
+ except Exception as e:
661
+ print(f"❌ Failed to get release info: {str(e)}")
662
+ return None
663
+
664
+ def download_file(url: str, path: str) -> bool:
665
+ """Download a file with progress reporting"""
666
+ try:
667
+ def reporthook(count, block_size, total_size):
668
+ percent = int(count * block_size * 100 / total_size)
669
+ sys.stdout.write(f"\rDownloading... {percent}%")
670
+ sys.stdout.flush()
671
+
672
+ urllib.request.urlretrieve(url, path, reporthook=reporthook)
673
+ print() # New line after progress
674
+ return True
675
+ except Exception as e:
676
+ print(f"\n❌ Download failed: {str(e)}")
677
+ return False
678
+
679
+ def add_to_path(directory: str) -> bool:
680
+ """Add directory to PATH if not already present"""
681
+ try:
682
+ current_path = os.environ.get("PATH", "")
683
+ if directory not in current_path.split(os.pathsep):
684
+ if platform.system() == "Windows":
685
+ # Permanent PATH modification on Windows
686
+ import winreg
687
+ with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root:
688
+ with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as key:
689
+ path_value, _ = winreg.QueryValueEx(key, "PATH")
690
+ new_path = f"{path_value};{directory}" if path_value else directory
691
+ winreg.SetValueEx(key, "PATH", 0, winreg.REG_EXPAND_SZ, new_path)
692
+ # Notify other processes of PATH change
693
+ import ctypes
694
+ ctypes.windll.user32.SendMessageTimeoutW(
695
+ 0xFFFF, 0x001A, 0, "Environment", 0x02, 5000, None
696
+ )
697
+ else:
698
+ # For Unix-like systems, modify current session PATH
699
+ os.environ["PATH"] = f"{directory}{os.pathsep}{os.environ.get('PATH', '')}"
700
+ # Add to shell profile files
701
+ profile_files = [
702
+ os.path.expanduser("~/.bashrc"),
703
+ os.path.expanduser("~/.zshrc"),
704
+ os.path.expanduser("~/.profile")
705
+ ]
706
+ export_line = f'\nexport PATH="{directory}:$PATH"\n'
707
+ for profile in profile_files:
708
+ if os.path.exists(profile):
709
+ with open(profile, "a") as f:
710
+ f.write(export_line)
711
+ print(f"✅ Added {directory} to PATH")
712
+ return True
713
+ except Exception as e:
714
+ print(f"⚠️ Failed to update PATH: {str(e)}")
715
+ return False
716
+
717
+ def verify_gh_installation() -> bool:
718
+ """Verify gh is properly installed and in PATH"""
719
+ if not shutil.which("gh"):
720
+ print("❌ GitHub CLI not found in PATH after installation")
721
+ return False
722
+
723
+ try:
724
+ result = subprocess.run(
725
+ ["gh", "--version"],
726
+ check=True,
727
+ stdout=subprocess.PIPE,
728
+ stderr=subprocess.PIPE,
729
+ text=True
730
+ )
731
+ print(f"✅ GitHub CLI installed: {result.stdout.splitlines()[0]}")
732
+ return True
733
+ except subprocess.CalledProcessError:
734
+ print("❌ GitHub CLI found but not working")
735
+ return False
736
+
737
+ def check_and_install_gh() -> bool:
738
+ """Main function to check and install GitHub CLI"""
739
+ if check_gh_installed():
740
+ return True
741
+
742
+ if not install_gh_cli():
743
+ print("\n❌ Failed to install GitHub CLI. Please try manual installation:")
744
+ print("Visit https://github.com/cli/cli#installation for instructions")
745
+ return False
746
+
747
+ if not check_gh_installed():
748
+ print("\n⚠️ Installation completed but GitHub CLI not detected in PATH")
749
+ print("Please restart your terminal or add the installation directory to your PATH")
750
+ return False
751
+
752
+ return True
753
+
754
+ def gh_authenticated():
755
+ """Check if user is authenticated with GitHub CLI"""
756
+ try:
757
+ result = subprocess.run(["gh", "auth", "status"], capture_output=True, text=True, check=True)
758
+ return "Logged in to github.com" in result.stderr
759
+ except (subprocess.CalledProcessError, FileNotFoundError):
760
+ return False
761
+
762
+ def authenticate_with_gh():
763
+ """Authenticate user with GitHub CLI"""
764
+ print("\n🔑 GitHub authentication required.")
765
+ print("The tool will use the GitHub CLI (gh) to open a browser for secure login.")
766
+
767
+ try:
768
+ subprocess.run(["gh", "auth", "login", "--web", "-h", "github.com"], check=True)
769
+ return True
770
+ except subprocess.CalledProcessError:
771
+ print("❌ Authentication failed. Please try running 'gh auth login' manually.", file=sys.stderr)
772
+ return False
773
+
774
+ def initialize_git_repository():
775
+ """Initialize git repository if not already initialized"""
776
+ if os.path.exists(".git"):
777
+ return False
778
+
779
+ print("🛠 Initializing git repository")
780
+ try:
781
+ subprocess.run(["git", "init"], check=True, capture_output=True)
782
+ subprocess.run(["git", "branch", "-M", "main"], check=True, capture_output=True)
783
+
784
+ if not os.path.exists(".gitignore"):
785
+ with open(".gitignore", "w") as f:
786
+ f.write("""# Python
787
+ __pycache__/
788
+ *.py[cod]
789
+ *.so
790
+ .Python
791
+ env/
792
+ venv/
793
+ .env
794
+
795
+ # IDE
796
+ .vscode/
797
+ .idea/
798
+ *.swp
799
+ *.swo
800
+
801
+ # System
802
+ .DS_Store
803
+ Thumbs.db
804
+
805
+ # Project specific
806
+ *.log
807
+ *.tmp
808
+ *.bak
809
+ """)
810
+ print("📁 Created .gitignore file")
811
+ return True
812
+ except subprocess.CalledProcessError as e:
813
+ print(f"❌ Failed to initialize Git repository: {e.stderr.decode().strip()}", file=sys.stderr)
814
+ return False
815
+
816
+ def create_initial_commit(commit_message="Initial commit"):
817
+ """Create initial commit if no commits exist"""
818
+ try:
819
+ result = subprocess.run(["git", "rev-list", "--count", "HEAD"],
820
+ capture_output=True, text=True)
821
+ commit_count = int(result.stdout.strip()) if result.stdout.strip().isdigit() else 0
822
+
823
+ if commit_count == 0:
824
+ print("📦 Creating initial commit")
825
+ subprocess.run(["git", "add", "."], check=True)
826
+ subprocess.run(["git", "commit", "-m", commit_message], check=True)
827
+ return True
828
+ return False
829
+ except subprocess.CalledProcessError as e:
830
+ if "nothing to commit" in e.stderr.decode():
831
+ print(f"❌ Failed to create initial commit: No files found to commit.", file=sys.stderr)
832
+ print("➡️ Add some files to your project directory before creating a repository.", file=sys.stderr)
833
+ else:
834
+ print(f"❌ Failed to create initial commit: {e.stderr.decode().strip()}", file=sys.stderr)
835
+ return False
836
+
837
+ def create_with_gh_cli(repo_name, private=False, description="", commit_message="Initial commit"):
838
+ """Create and push to new repository using GitHub CLI"""
839
+ try:
840
+ if not os.path.exists(".git"):
841
+ if not initialize_git_repository():
842
+ return False
843
+
844
+ if not create_initial_commit(commit_message):
845
+ if subprocess.run(["git", "status"], capture_output=True).returncode != 0:
846
+ return False
847
+ print("ℹ️ Using existing commits")
848
+
849
+ private_flag = "--private" if private else "--public"
850
+ cmd = ["gh", "repo", "create", repo_name, private_flag,
851
+ "--source=.", "--remote=origin", "--push"]
852
+
853
+ if description:
854
+ cmd.extend(["--description", description])
855
+
856
+ print("🚀 Creating repository and pushing code...")
857
+ process = subprocess.run(cmd, check=True, capture_output=True, text=True)
858
+
859
+ repo_url = process.stderr.strip()
860
+ print(f"✅ Successfully created repository: {repo_url}")
861
+ return True
862
+
863
+ except subprocess.CalledProcessError as e:
864
+ error_message = e.stderr.strip()
865
+ if "already exists" in error_message:
866
+ print(f"❌ Failed to create repository: {error_message}", file=sys.stderr)
867
+ print("➡️ Please choose a different repository name.", file=sys.stderr)
868
+ else:
869
+ print(f"❌ Failed to create repository: {error_message}", file=sys.stderr)
870
+ return False
871
+ except Exception as e:
872
+ print(f"❌ An unexpected error occurred: {str(e)}", file=sys.stderr)
873
+ return False
874
+
875
+ def standard_git_push(commit_message, branch, remote, force=False, tags=False):
876
+ """Handle standard git push operations"""
877
+ try:
878
+ subprocess.run(["git", "add", "."], check=True)
879
+
880
+ if commit_message:
881
+ print(f"📦 Committing with message: '{commit_message}'")
882
+ subprocess.run(["git", "commit", "-m", commit_message, "--allow-empty-message"], check=True)
883
+ else:
884
+ print("ℹ️ No commit message provided. Pushing only staged changes.")
885
+
886
+ push_cmd = ["git", "push"]
887
+ if force:
888
+ push_cmd.append("--force-with-lease")
889
+ print("⚠️ Using safe force push (--force-with-lease).")
890
+ if tags:
891
+ push_cmd.append("--tags")
892
+ if remote and branch:
893
+ push_cmd.extend([remote, branch])
894
+
895
+ print(f"🚀 Executing: {' '.join(push_cmd)}")
896
+ subprocess.run(push_cmd, check=True)
897
+ print("✅ Successfully pushed changes.")
898
+ return True
899
+ except subprocess.CalledProcessError as e:
900
+ error_output = e.stderr.decode().strip() if e.stderr else str(e)
901
+ if "nothing to commit" in error_output:
902
+ print("ℹ️ No changes to commit. Nothing to do.")
903
+ return True
904
+ print(f"❌ Push failed: {error_output}", file=sys.stderr)
905
+ return False
906
+
907
+ def run():
908
+ parser = argparse.ArgumentParser(
909
+ description="🚀 Supercharged Git push tool with GitHub repo creation",
910
+ formatter_class=argparse.RawDescriptionHelpFormatter,
911
+ epilog="""Examples:
912
+ Standard push: gitpush_tool "My new feature"
913
+ Create new repo: gitpush_tool "Initial commit" --new-repo my-awesome-project
914
+ Private repository: gitpush_tool "Initial commit" --new-repo my-secret-project --private
915
+ Force push (safe): gitpush_tool "Rebased feature" --force
916
+ Initialize only: gitpush_tool --init
917
+ """
918
+ )
919
+ parser.add_argument("commit", nargs="?", help="Commit message (optional if just pushing staged changes).")
920
+ parser.add_argument("branch", nargs="?", default=None, help="Branch name (defaults to current branch).")
921
+ parser.add_argument("remote", nargs="?", default="origin", help="Remote name (default: origin).")
922
+ parser.add_argument("--force", action="store_true", help="Force push with --force-with-lease.")
923
+ parser.add_argument("--tags", action="store_true", help="Push all tags.")
924
+ parser.add_argument("--init", action="store_true", help="Initialize a new Git repository and exit.")
925
+ parser.add_argument("--new-repo", metavar="REPO_NAME", help="Create a new GitHub repository with the given name.")
926
+ parser.add_argument("--private", action="store_true", help="Make the new repository private.")
927
+ parser.add_argument("--description", help="Description for the new repository.")
928
+
929
+ args = parser.parse_args()
930
+
931
+ target_branch = args.branch
932
+ if not target_branch:
933
+ try:
934
+ branch_result = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], capture_output=True, text=True, check=True)
935
+ target_branch = branch_result.stdout.strip()
936
+ except subprocess.CalledProcessError:
937
+ target_branch = "main"
938
+
939
+ if args.new_repo:
940
+ if not check_gh_installed():
941
+ sys.exit(1)
942
+
943
+ if not gh_authenticated():
944
+ if not authenticate_with_gh():
945
+ sys.exit(1)
946
+
947
+ if not create_with_gh_cli(
948
+ args.new_repo,
949
+ private=args.private,
950
+ description=args.description or "",
951
+ commit_message=args.commit or "Initial commit"
952
+ ):
953
+ sys.exit(1)
954
+
955
+ elif args.init:
956
+ if initialize_git_repository():
957
+ print("✅ Git repository initialized successfully.")
958
+
959
+ else:
960
+ if not standard_git_push(
961
+ args.commit,
962
+ target_branch,
963
+ args.remote,
964
+ args.force,
965
+ args.tags
966
+ ):
967
+ sys.exit(1)
968
+
969
+ if __name__ == "__main__":
970
+ run()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitpush-tool
3
- Version: 0.2.10
3
+ Version: 0.3.0
4
4
  Summary: Supercharged Git push tool with automatic GitHub repo creation and pushing
5
5
  Home-page: https://github.com/inevitablegs/gitpush
6
6
  Author: Ganesh Sonawane
@@ -6,7 +6,7 @@ long_description = (Path(__file__).parent / "LONG_DESCRIPTION.md").read_text(enc
6
6
 
7
7
  setup(
8
8
  name="gitpush-tool",
9
- version="0.2.10",
9
+ version="0.3.0",
10
10
  packages=find_packages(),
11
11
  install_requires=[],
12
12
  entry_points={
@@ -1 +0,0 @@
1
- __version__ = "0.2.10"
@@ -1,279 +0,0 @@
1
- import os
2
- import argparse
3
- import sys
4
- import subprocess
5
- import shutil
6
- import platform
7
-
8
- def check_gh_installed():
9
- """
10
- Checks if GitHub CLI is installed. If not, it provides a comprehensive list
11
- of platform-specific installation commands and fallbacks.
12
- """
13
- if shutil.which("gh"):
14
- return True
15
-
16
- print("❌ Error: GitHub CLI (gh) is not installed or not in your system's PATH.", file=sys.stderr)
17
- print(" The '--new-repo' feature requires the GitHub CLI for all repository operations.", file=sys.stderr)
18
-
19
- system = platform.system()
20
- install_options = []
21
-
22
- # --- Platform-specific detection with fallbacks ---
23
- if system == "Windows":
24
- print("\n➡️ For Windows, we recommend one of the following package managers:", file=sys.stderr)
25
- if shutil.which("winget"):
26
- install_options.append(("Winget (Recommended)", "winget install --id GitHub.cli --source winget"))
27
- if shutil.which("scoop"):
28
- install_options.append(("Scoop", "scoop install gh"))
29
- if shutil.which("choco"):
30
- install_options.append(("Chocolatey", "choco install gh"))
31
-
32
- elif system == "Darwin":
33
- print("\n➡️ For macOS, we recommend using Homebrew:", file=sys.stderr)
34
- if shutil.which("brew"):
35
- install_options.append(("Homebrew (Recommended)", "brew install gh"))
36
-
37
- elif system == "Linux":
38
- print("\n➡️ For Linux, we recommend one of the following package managers:", file=sys.stderr)
39
- if shutil.which("apt") or shutil.which("apt-get"):
40
- install_options.append(("Debian/Ubuntu/Mint", "sudo apt update && sudo apt install gh -y"))
41
- if shutil.which("dnf"):
42
- install_options.append(("Fedora/RHEL/CentOS", "sudo dnf install gh"))
43
- if shutil.which("yum"):
44
- install_options.append(("Older Fedora/RHEL/CentOS", "sudo yum install gh"))
45
- if shutil.which("pacman"):
46
- install_options.append(("Arch Linux", "sudo pacman -S github-cli"))
47
- if shutil.which("zypper"):
48
- install_options.append(("openSUSE", "sudo zypper install gh"))
49
-
50
- # --- Displaying the options and the ultimate fallback ---
51
- if install_options:
52
- print(" Please copy and run one of the commands below in your terminal:\n", file=sys.stderr)
53
- for name, command in install_options:
54
- print(f" # For {name}:\n {command}\n", file=sys.stderr)
55
-
56
- print(" If none of the above are available on your system, please follow the", file=sys.stderr)
57
- print(" official installation guide for more options (including manual download):", file=sys.stderr)
58
- print(" https://github.com/cli/cli#installation\n", file=sys.stderr)
59
- print("‼️ After installation, please open a NEW terminal and run your command again.", file=sys.stderr)
60
-
61
- return False
62
-
63
- def gh_authenticated():
64
- """Check if user is authenticated with GitHub CLI"""
65
- try:
66
- result = subprocess.run(["gh", "auth", "status"], capture_output=True, text=True, check=True)
67
- return "Logged in to github.com" in result.stderr
68
- except (subprocess.CalledProcessError, FileNotFoundError):
69
- return False
70
-
71
- def authenticate_with_gh():
72
- """Authenticate user with GitHub CLI"""
73
- print("\n🔑 GitHub authentication required.")
74
- print("The tool will use the GitHub CLI (gh) to open a browser for secure login.")
75
-
76
- try:
77
- subprocess.run(["gh", "auth", "login", "--web", "-h", "github.com"], check=True)
78
- return True
79
- except subprocess.CalledProcessError:
80
- print("❌ Authentication failed. Please try running 'gh auth login' manually.", file=sys.stderr)
81
- return False
82
-
83
- def initialize_git_repository():
84
- """Initialize git repository if not already initialized"""
85
- if os.path.exists(".git"):
86
- return False
87
-
88
- print("🛠 Initializing git repository")
89
- try:
90
- subprocess.run(["git", "init"], check=True, capture_output=True)
91
- subprocess.run(["git", "branch", "-M", "main"], check=True, capture_output=True)
92
-
93
- if not os.path.exists(".gitignore"):
94
- with open(".gitignore", "w") as f:
95
- f.write("""# Python
96
- __pycache__/
97
- *.py[cod]
98
- *.so
99
- .Python
100
- env/
101
- venv/
102
- .env
103
-
104
- # IDE
105
- .vscode/
106
- .idea/
107
- *.swp
108
- *.swo
109
-
110
- # System
111
- .DS_Store
112
- Thumbs.db
113
-
114
- # Project specific
115
- *.log
116
- *.tmp
117
- *.bak
118
- """)
119
- print("📁 Created .gitignore file")
120
- return True
121
- except subprocess.CalledProcessError as e:
122
- print(f"❌ Failed to initialize Git repository: {e.stderr.decode().strip()}", file=sys.stderr)
123
- return False
124
-
125
- def create_initial_commit(commit_message="Initial commit"):
126
- """Create initial commit if no commits exist"""
127
- try:
128
- result = subprocess.run(["git", "rev-list", "--count", "HEAD"],
129
- capture_output=True, text=True)
130
- commit_count = int(result.stdout.strip()) if result.stdout.strip().isdigit() else 0
131
-
132
- if commit_count == 0:
133
- print("📦 Creating initial commit")
134
- subprocess.run(["git", "add", "."], check=True)
135
- subprocess.run(["git", "commit", "-m", commit_message], check=True)
136
- return True
137
- return False
138
- except subprocess.CalledProcessError as e:
139
- if "nothing to commit" in e.stderr.decode():
140
- print(f"❌ Failed to create initial commit: No files found to commit.", file=sys.stderr)
141
- print("➡️ Add some files to your project directory before creating a repository.", file=sys.stderr)
142
- else:
143
- print(f"❌ Failed to create initial commit: {e.stderr.decode().strip()}", file=sys.stderr)
144
- return False
145
-
146
- def create_with_gh_cli(repo_name, private=False, description="", commit_message="Initial commit"):
147
- """Create and push to new repository using GitHub CLI"""
148
- try:
149
- if not os.path.exists(".git"):
150
- if not initialize_git_repository():
151
- return False
152
-
153
- if not create_initial_commit(commit_message):
154
- if subprocess.run(["git", "status"], capture_output=True).returncode != 0:
155
- return False
156
- print("ℹ️ Using existing commits")
157
-
158
- private_flag = "--private" if private else "--public"
159
- cmd = ["gh", "repo", "create", repo_name, private_flag,
160
- "--source=.", "--remote=origin", "--push"]
161
-
162
- if description:
163
- cmd.extend(["--description", description])
164
-
165
- print("🚀 Creating repository and pushing code...")
166
- process = subprocess.run(cmd, check=True, capture_output=True, text=True)
167
-
168
- repo_url = process.stderr.strip()
169
- print(f"✅ Successfully created repository: {repo_url}")
170
- return True
171
-
172
- except subprocess.CalledProcessError as e:
173
- error_message = e.stderr.strip()
174
- if "already exists" in error_message:
175
- print(f"❌ Failed to create repository: {error_message}", file=sys.stderr)
176
- print("➡️ Please choose a different repository name.", file=sys.stderr)
177
- else:
178
- print(f"❌ Failed to create repository: {error_message}", file=sys.stderr)
179
- return False
180
- except Exception as e:
181
- print(f"❌ An unexpected error occurred: {str(e)}", file=sys.stderr)
182
- return False
183
-
184
- def standard_git_push(commit_message, branch, remote, force=False, tags=False):
185
- """Handle standard git push operations"""
186
- try:
187
- subprocess.run(["git", "add", "."], check=True)
188
-
189
- if commit_message:
190
- print(f"📦 Committing with message: '{commit_message}'")
191
- subprocess.run(["git", "commit", "-m", commit_message, "--allow-empty-message"], check=True)
192
- else:
193
- print("ℹ️ No commit message provided. Pushing only staged changes.")
194
-
195
- push_cmd = ["git", "push"]
196
- if force:
197
- push_cmd.append("--force-with-lease")
198
- print("⚠️ Using safe force push (--force-with-lease).")
199
- if tags:
200
- push_cmd.append("--tags")
201
- if remote and branch:
202
- push_cmd.extend([remote, branch])
203
-
204
- print(f"🚀 Executing: {' '.join(push_cmd)}")
205
- subprocess.run(push_cmd, check=True)
206
- print("✅ Successfully pushed changes.")
207
- return True
208
- except subprocess.CalledProcessError as e:
209
- error_output = e.stderr.decode().strip() if e.stderr else str(e)
210
- if "nothing to commit" in error_output:
211
- print("ℹ️ No changes to commit. Nothing to do.")
212
- return True
213
- print(f"❌ Push failed: {error_output}", file=sys.stderr)
214
- return False
215
-
216
- def run():
217
- parser = argparse.ArgumentParser(
218
- description="🚀 Supercharged Git push tool with GitHub repo creation",
219
- formatter_class=argparse.RawDescriptionHelpFormatter,
220
- epilog="""Examples:
221
- Standard push: gitpush_tool "My new feature"
222
- Create new repo: gitpush_tool "Initial commit" --new-repo my-awesome-project
223
- Private repository: gitpush_tool "Initial commit" --new-repo my-secret-project --private
224
- Force push (safe): gitpush_tool "Rebased feature" --force
225
- Initialize only: gitpush_tool --init
226
- """
227
- )
228
- parser.add_argument("commit", nargs="?", help="Commit message (optional if just pushing staged changes).")
229
- parser.add_argument("branch", nargs="?", default=None, help="Branch name (defaults to current branch).")
230
- parser.add_argument("remote", nargs="?", default="origin", help="Remote name (default: origin).")
231
- parser.add_argument("--force", action="store_true", help="Force push with --force-with-lease.")
232
- parser.add_argument("--tags", action="store_true", help="Push all tags.")
233
- parser.add_argument("--init", action="store_true", help="Initialize a new Git repository and exit.")
234
- parser.add_argument("--new-repo", metavar="REPO_NAME", help="Create a new GitHub repository with the given name.")
235
- parser.add_argument("--private", action="store_true", help="Make the new repository private.")
236
- parser.add_argument("--description", help="Description for the new repository.")
237
-
238
- args = parser.parse_args()
239
-
240
- target_branch = args.branch
241
- if not target_branch:
242
- try:
243
- branch_result = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], capture_output=True, text=True, check=True)
244
- target_branch = branch_result.stdout.strip()
245
- except subprocess.CalledProcessError:
246
- target_branch = "main"
247
-
248
- if args.new_repo:
249
- if not check_gh_installed():
250
- sys.exit(1)
251
-
252
- if not gh_authenticated():
253
- if not authenticate_with_gh():
254
- sys.exit(1)
255
-
256
- if not create_with_gh_cli(
257
- args.new_repo,
258
- private=args.private,
259
- description=args.description or "",
260
- commit_message=args.commit or "Initial commit"
261
- ):
262
- sys.exit(1)
263
-
264
- elif args.init:
265
- if initialize_git_repository():
266
- print("✅ Git repository initialized successfully.")
267
-
268
- else:
269
- if not standard_git_push(
270
- args.commit,
271
- target_branch,
272
- args.remote,
273
- args.force,
274
- args.tags
275
- ):
276
- sys.exit(1)
277
-
278
- if __name__ == "__main__":
279
- run()
File without changes
File without changes
File without changes
File without changes