gitpush-tool 0.2.8__tar.gz → 0.2.10__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.
- {gitpush_tool-0.2.8/gitpush_tool.egg-info → gitpush_tool-0.2.10}/PKG-INFO +1 -1
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10}/README.md +1 -17
- gitpush_tool-0.2.10/gitpush_tool/__init__.py +1 -0
- gitpush_tool-0.2.10/gitpush_tool/cli.py +279 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10/gitpush_tool.egg-info}/PKG-INFO +1 -1
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10}/setup.py +1 -1
- gitpush_tool-0.2.8/gitpush_tool/__init__.py +0 -1
- gitpush_tool-0.2.8/gitpush_tool/cli.py +0 -287
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10}/LICENSE +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10}/MANIFEST.in +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10}/gitpush_tool/__doc__.py +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10}/gitpush_tool.egg-info/SOURCES.txt +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10}/gitpush_tool.egg-info/dependency_links.txt +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10}/gitpush_tool.egg-info/entry_points.txt +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10}/gitpush_tool.egg-info/top_level.txt +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10}/pyproject.toml +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.10}/setup.cfg +0 -0
|
@@ -20,22 +20,6 @@ A supercharged Git CLI tool that simplifies repository creation and pushing with
|
|
|
20
20
|
pip install gitpush-tool
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
### Prerequisites
|
|
24
|
-
|
|
25
|
-
- GitHub CLI (gh)
|
|
26
|
-
- Git
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
# Install GitHub CLI
|
|
30
|
-
# macOS
|
|
31
|
-
brew install gh
|
|
32
|
-
|
|
33
|
-
# Windows
|
|
34
|
-
winget install --id GitHub.cli
|
|
35
|
-
|
|
36
|
-
# Linux (Debian/Ubuntu)
|
|
37
|
-
sudo apt install gh
|
|
38
|
-
```
|
|
39
23
|
|
|
40
24
|
## Usage 🛠️
|
|
41
25
|
|
|
@@ -179,4 +163,4 @@ Contributions welcome! Please follow these steps:
|
|
|
179
163
|
|
|
180
164
|
MIT - See LICENSE for details.
|
|
181
165
|
|
|
182
|
-
<center>✨ <strong>Happy Coding!</strong> ✨</center>
|
|
166
|
+
<center>✨ <strong>Happy Coding!</strong> ✨</center>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.10"
|
|
@@ -0,0 +1,279 @@
|
|
|
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()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.8"
|
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import argparse
|
|
3
|
-
import sys
|
|
4
|
-
import subprocess
|
|
5
|
-
import platform
|
|
6
|
-
import urllib.request
|
|
7
|
-
import tempfile
|
|
8
|
-
import shutil
|
|
9
|
-
|
|
10
|
-
def check_gh_installed():
|
|
11
|
-
"""Check if GitHub CLI is installed, attempt installation if not"""
|
|
12
|
-
if shutil.which("gh"):
|
|
13
|
-
return True
|
|
14
|
-
|
|
15
|
-
print("📦 GitHub CLI not found. Attempting installation...")
|
|
16
|
-
system = platform.system()
|
|
17
|
-
|
|
18
|
-
try:
|
|
19
|
-
if system == "Windows":
|
|
20
|
-
return install_gh_cli_windows()
|
|
21
|
-
elif system == "Darwin":
|
|
22
|
-
subprocess.run(["brew", "install", "gh"], check=True)
|
|
23
|
-
elif system == "Linux":
|
|
24
|
-
subprocess.run(["sudo", "apt", "install", "-y", "gh"], check=True)
|
|
25
|
-
else:
|
|
26
|
-
print("❌ Unsupported OS.")
|
|
27
|
-
return False
|
|
28
|
-
|
|
29
|
-
return shutil.which("gh") is not None
|
|
30
|
-
except Exception as e:
|
|
31
|
-
print(f"❌ Failed to install GitHub CLI: {e}")
|
|
32
|
-
return False
|
|
33
|
-
|
|
34
|
-
import os
|
|
35
|
-
import subprocess
|
|
36
|
-
import shutil
|
|
37
|
-
import tempfile
|
|
38
|
-
import urllib.request
|
|
39
|
-
import json
|
|
40
|
-
|
|
41
|
-
def install_gh_cli_windows():
|
|
42
|
-
"""Install GitHub CLI on Windows using winget or direct download"""
|
|
43
|
-
# Try winget first
|
|
44
|
-
if shutil.which("winget"):
|
|
45
|
-
try:
|
|
46
|
-
print("📦 Attempting to install GitHub CLI with winget...")
|
|
47
|
-
subprocess.run(["winget", "install", "--id", "GitHub.cli", "--silent"], check=True)
|
|
48
|
-
print("✅ GitHub CLI installed successfully via winget.")
|
|
49
|
-
return True
|
|
50
|
-
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
51
|
-
print("⚠️ Winget installation failed. Falling back to direct download.")
|
|
52
|
-
|
|
53
|
-
# Fallback to direct download
|
|
54
|
-
try:
|
|
55
|
-
print("⬇️ Finding the latest GitHub CLI release for Windows...")
|
|
56
|
-
api_url = "https://api.github.com/repos/cli/cli/releases/latest"
|
|
57
|
-
with urllib.request.urlopen(api_url) as response:
|
|
58
|
-
data = json.loads(response.read().decode())
|
|
59
|
-
|
|
60
|
-
# Find the correct MSI asset
|
|
61
|
-
msi_url = None
|
|
62
|
-
for asset in data.get("assets", []):
|
|
63
|
-
if asset.get("name", "").endswith("_windows_amd64.msi"):
|
|
64
|
-
msi_url = asset.get("browser_download_url")
|
|
65
|
-
break
|
|
66
|
-
|
|
67
|
-
if not msi_url:
|
|
68
|
-
print("❌ Could not find a downloadable MSI file for the latest release.")
|
|
69
|
-
return False
|
|
70
|
-
|
|
71
|
-
msi_path = os.path.join(tempfile.gettempdir(), "gh_installer.msi")
|
|
72
|
-
|
|
73
|
-
print(f"⬇️ Downloading GitHub CLI from: {msi_url}")
|
|
74
|
-
urllib.request.urlretrieve(msi_url, msi_path)
|
|
75
|
-
|
|
76
|
-
print("🛠️ Installing GitHub CLI...")
|
|
77
|
-
# Use msiexec for silent installation
|
|
78
|
-
subprocess.run(["msiexec", "/i", msi_path, "/quiet", "/norestart"], check=True)
|
|
79
|
-
|
|
80
|
-
# Clean up the downloaded file
|
|
81
|
-
os.remove(msi_path)
|
|
82
|
-
print("✅ GitHub CLI installed successfully.")
|
|
83
|
-
return True
|
|
84
|
-
except Exception as e:
|
|
85
|
-
print(f"❌ Direct installation failed: {e}")
|
|
86
|
-
return False
|
|
87
|
-
|
|
88
|
-
def gh_authenticated():
|
|
89
|
-
"""Check if user is authenticated with GitHub CLI"""
|
|
90
|
-
try:
|
|
91
|
-
result = subprocess.run(["gh", "auth", "status"], capture_output=True, text=True)
|
|
92
|
-
return result.returncode == 0
|
|
93
|
-
except:
|
|
94
|
-
return False
|
|
95
|
-
|
|
96
|
-
def authenticate_with_gh():
|
|
97
|
-
"""Authenticate user with GitHub CLI"""
|
|
98
|
-
print("\n🔑 GitHub authentication required")
|
|
99
|
-
print("We'll use the GitHub CLI (gh) for authentication")
|
|
100
|
-
print("This will open your browser for secure login")
|
|
101
|
-
|
|
102
|
-
try:
|
|
103
|
-
subprocess.run(["gh", "auth", "login", "--web", "-h", "github.com"], check=True)
|
|
104
|
-
return True
|
|
105
|
-
except subprocess.CalledProcessError:
|
|
106
|
-
print("❌ Authentication failed")
|
|
107
|
-
return False
|
|
108
|
-
|
|
109
|
-
def initialize_git_repository():
|
|
110
|
-
"""Initialize git repository if not already initialized"""
|
|
111
|
-
if os.path.exists(".git"):
|
|
112
|
-
return False
|
|
113
|
-
|
|
114
|
-
print("🛠 Initializing git repository")
|
|
115
|
-
try:
|
|
116
|
-
subprocess.run(["git", "init"], check=True)
|
|
117
|
-
subprocess.run(["git", "branch", "-M", "main"], check=True)
|
|
118
|
-
|
|
119
|
-
if not os.path.exists(".gitignore"):
|
|
120
|
-
with open(".gitignore", "w") as f:
|
|
121
|
-
f.write("""# Python
|
|
122
|
-
__pycache__/
|
|
123
|
-
*.py[cod]
|
|
124
|
-
*.so
|
|
125
|
-
.Python
|
|
126
|
-
env/
|
|
127
|
-
venv/
|
|
128
|
-
.env
|
|
129
|
-
|
|
130
|
-
# IDE
|
|
131
|
-
.vscode/
|
|
132
|
-
.idea/
|
|
133
|
-
*.swp
|
|
134
|
-
*.swo
|
|
135
|
-
|
|
136
|
-
# System
|
|
137
|
-
.DS_Store
|
|
138
|
-
Thumbs.db
|
|
139
|
-
|
|
140
|
-
# Project specific
|
|
141
|
-
*.log
|
|
142
|
-
*.tmp
|
|
143
|
-
*.bak
|
|
144
|
-
""")
|
|
145
|
-
print("📁 Created .gitignore file")
|
|
146
|
-
return True
|
|
147
|
-
except subprocess.CalledProcessError as e:
|
|
148
|
-
print(f"❌ Failed to initialize Git repository: {e}")
|
|
149
|
-
return False
|
|
150
|
-
|
|
151
|
-
def create_initial_commit(commit_message="Initial commit"):
|
|
152
|
-
"""Create initial commit if no commits exist"""
|
|
153
|
-
try:
|
|
154
|
-
result = subprocess.run(["git", "rev-list", "--count", "HEAD"],
|
|
155
|
-
capture_output=True, text=True)
|
|
156
|
-
commit_count = int(result.stdout.strip()) if result.stdout.strip().isdigit() else 0
|
|
157
|
-
|
|
158
|
-
if commit_count == 0:
|
|
159
|
-
print("📦 Creating initial commit")
|
|
160
|
-
subprocess.run(["git", "add", "."], check=True)
|
|
161
|
-
subprocess.run(["git", "commit", "-m", commit_message], check=True)
|
|
162
|
-
return True
|
|
163
|
-
return False
|
|
164
|
-
except subprocess.CalledProcessError as e:
|
|
165
|
-
print(f"❌ Failed to create initial commit: {e}")
|
|
166
|
-
return False
|
|
167
|
-
|
|
168
|
-
def create_with_gh_cli(repo_name, private=False, description="", commit_message="Initial commit"):
|
|
169
|
-
"""Create and push to new repository using GitHub CLI"""
|
|
170
|
-
try:
|
|
171
|
-
if not os.path.exists(".git") and not initialize_git_repository():
|
|
172
|
-
return False
|
|
173
|
-
|
|
174
|
-
if not create_initial_commit(commit_message):
|
|
175
|
-
print("ℹ️ Using existing commits")
|
|
176
|
-
|
|
177
|
-
private_flag = "--private" if private else "--public"
|
|
178
|
-
cmd = ["gh", "repo", "create", repo_name, private_flag,
|
|
179
|
-
"--source=.", "--remote=origin", "--push"]
|
|
180
|
-
|
|
181
|
-
if description:
|
|
182
|
-
cmd.extend(["--description", description])
|
|
183
|
-
|
|
184
|
-
print("🚀 Creating repository and pushing code...")
|
|
185
|
-
subprocess.run(cmd, check=True)
|
|
186
|
-
|
|
187
|
-
url_result = subprocess.run(
|
|
188
|
-
["gh", "repo", "view", "--json", "url", "--jq", ".url"],
|
|
189
|
-
capture_output=True, text=True, check=True
|
|
190
|
-
)
|
|
191
|
-
repo_url = url_result.stdout.strip()
|
|
192
|
-
print(f"✅ Successfully created repository: {repo_url}")
|
|
193
|
-
return True
|
|
194
|
-
|
|
195
|
-
except subprocess.CalledProcessError as e:
|
|
196
|
-
print(f"❌ Failed to create repository: {e.stderr if e.stderr else 'Unknown error'}")
|
|
197
|
-
return False
|
|
198
|
-
except Exception as e:
|
|
199
|
-
print(f"❌ Unexpected error: {str(e)}")
|
|
200
|
-
return False
|
|
201
|
-
|
|
202
|
-
def standard_git_push(commit_message, branch, remote, force=False, tags=False):
|
|
203
|
-
"""Handle standard git push operations"""
|
|
204
|
-
try:
|
|
205
|
-
subprocess.run(["git", "add", "."], check=True)
|
|
206
|
-
|
|
207
|
-
if commit_message:
|
|
208
|
-
print(f"📦 Committing: '{commit_message}'")
|
|
209
|
-
subprocess.run(["git", "commit", "-m", commit_message], check=True)
|
|
210
|
-
else:
|
|
211
|
-
print("ℹ️ No commit message provided - skipping commit")
|
|
212
|
-
|
|
213
|
-
push_cmd = ["git", "push"]
|
|
214
|
-
if force:
|
|
215
|
-
push_cmd.append("--force-with-lease")
|
|
216
|
-
if tags:
|
|
217
|
-
push_cmd.append("--tags")
|
|
218
|
-
if remote and branch:
|
|
219
|
-
push_cmd.extend([remote, branch])
|
|
220
|
-
|
|
221
|
-
print(f"🚀 Executing: {' '.join(push_cmd)}")
|
|
222
|
-
subprocess.run(push_cmd, check=True)
|
|
223
|
-
print("✅ Successfully pushed changes")
|
|
224
|
-
return True
|
|
225
|
-
except subprocess.CalledProcessError as e:
|
|
226
|
-
print(f"❌ Push failed: {e}")
|
|
227
|
-
return False
|
|
228
|
-
|
|
229
|
-
def run():
|
|
230
|
-
parser = argparse.ArgumentParser(
|
|
231
|
-
description="🚀 Supercharged Git push tool with GitHub repo creation",
|
|
232
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
233
|
-
epilog="""Examples:
|
|
234
|
-
Standard push: gitpush_tool "Commit message"
|
|
235
|
-
Create new repo: gitpush_tool "Initial commit" --new-repo project-name
|
|
236
|
-
Private repository: gitpush_tool --new-repo private-project --private
|
|
237
|
-
Force push: gitpush_tool "Fix critical bug" --force
|
|
238
|
-
"""
|
|
239
|
-
)
|
|
240
|
-
parser.add_argument("commit", nargs="?", help="Commit message")
|
|
241
|
-
parser.add_argument("branch", nargs="?", default="main", help="Branch name (default: main)")
|
|
242
|
-
parser.add_argument("remote", nargs="?", default="origin", help="Remote name (default: origin)")
|
|
243
|
-
parser.add_argument("--force", action="store_true", help="Force push with --force-with-lease")
|
|
244
|
-
parser.add_argument("--tags", action="store_true", help="Push tags")
|
|
245
|
-
parser.add_argument("--init", action="store_true", help="Initialize git repo")
|
|
246
|
-
parser.add_argument("--new-repo", metavar="NAME", help="Create new GitHub repository")
|
|
247
|
-
parser.add_argument("--private", action="store_true", help="Make repository private")
|
|
248
|
-
parser.add_argument("--description", help="Repository description")
|
|
249
|
-
|
|
250
|
-
args = parser.parse_args()
|
|
251
|
-
|
|
252
|
-
if args.new_repo:
|
|
253
|
-
print(f"🆕 Creating repository: {args.new_repo}")
|
|
254
|
-
|
|
255
|
-
if not check_gh_installed():
|
|
256
|
-
print("❌ GitHub CLI (gh) is not installed")
|
|
257
|
-
print("Please install it first:")
|
|
258
|
-
print(" Mac (Homebrew): brew install gh")
|
|
259
|
-
print(" Windows (Winget): winget install --id GitHub.cli")
|
|
260
|
-
print(" Linux: See https://github.com/cli/cli#installation")
|
|
261
|
-
sys.exit(1)
|
|
262
|
-
|
|
263
|
-
if not gh_authenticated() and not authenticate_with_gh():
|
|
264
|
-
sys.exit(1)
|
|
265
|
-
|
|
266
|
-
if not create_with_gh_cli(
|
|
267
|
-
args.new_repo,
|
|
268
|
-
private=args.private,
|
|
269
|
-
description=args.description or "",
|
|
270
|
-
commit_message=args.commit or "Initial commit"
|
|
271
|
-
):
|
|
272
|
-
sys.exit(1)
|
|
273
|
-
elif args.init:
|
|
274
|
-
if initialize_git_repository():
|
|
275
|
-
create_initial_commit(args.commit or "Initial commit")
|
|
276
|
-
else:
|
|
277
|
-
if not standard_git_push(
|
|
278
|
-
args.commit,
|
|
279
|
-
args.branch,
|
|
280
|
-
args.remote,
|
|
281
|
-
args.force,
|
|
282
|
-
args.tags
|
|
283
|
-
):
|
|
284
|
-
sys.exit(1)
|
|
285
|
-
|
|
286
|
-
if __name__ == "__main__":
|
|
287
|
-
run()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|