gitpush-tool 0.2.8__tar.gz → 0.2.9__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.9}/PKG-INFO +1 -1
- gitpush_tool-0.2.9/gitpush_tool/__init__.py +1 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/gitpush_tool/cli.py +122 -92
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9/gitpush_tool.egg-info}/PKG-INFO +1 -1
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/setup.py +1 -1
- gitpush_tool-0.2.8/gitpush_tool/__init__.py +0 -1
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/LICENSE +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/MANIFEST.in +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/README.md +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/gitpush_tool/__doc__.py +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/gitpush_tool.egg-info/SOURCES.txt +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/gitpush_tool.egg-info/dependency_links.txt +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/gitpush_tool.egg-info/entry_points.txt +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/gitpush_tool.egg-info/top_level.txt +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/pyproject.toml +0 -0
- {gitpush_tool-0.2.8 → gitpush_tool-0.2.9}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.9"
|
|
@@ -2,40 +2,10 @@ import os
|
|
|
2
2
|
import argparse
|
|
3
3
|
import sys
|
|
4
4
|
import subprocess
|
|
5
|
+
import shutil
|
|
5
6
|
import platform
|
|
6
7
|
import urllib.request
|
|
7
8
|
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
9
|
import json
|
|
40
10
|
|
|
41
11
|
def install_gh_cli_windows():
|
|
@@ -44,7 +14,7 @@ def install_gh_cli_windows():
|
|
|
44
14
|
if shutil.which("winget"):
|
|
45
15
|
try:
|
|
46
16
|
print("📦 Attempting to install GitHub CLI with winget...")
|
|
47
|
-
subprocess.run(["winget", "install", "--id", "GitHub.cli", "--silent"], check=True)
|
|
17
|
+
subprocess.run(["winget", "install", "--id", "GitHub.cli", "--source", "winget", "--silent"], check=True, capture_output=True)
|
|
48
18
|
print("✅ GitHub CLI installed successfully via winget.")
|
|
49
19
|
return True
|
|
50
20
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
@@ -57,15 +27,10 @@ def install_gh_cli_windows():
|
|
|
57
27
|
with urllib.request.urlopen(api_url) as response:
|
|
58
28
|
data = json.loads(response.read().decode())
|
|
59
29
|
|
|
60
|
-
|
|
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
|
|
30
|
+
msi_url = next((asset["browser_download_url"] for asset in data.get("assets", []) if asset.get("name", "").endswith("_windows_amd64.msi")), None)
|
|
66
31
|
|
|
67
32
|
if not msi_url:
|
|
68
|
-
print("❌ Could not find a downloadable MSI file for the latest release.")
|
|
33
|
+
print("❌ Could not find a downloadable MSI file for the latest release.", file=sys.stderr)
|
|
69
34
|
return False
|
|
70
35
|
|
|
71
36
|
msi_path = os.path.join(tempfile.gettempdir(), "gh_installer.msi")
|
|
@@ -74,36 +39,83 @@ def install_gh_cli_windows():
|
|
|
74
39
|
urllib.request.urlretrieve(msi_url, msi_path)
|
|
75
40
|
|
|
76
41
|
print("🛠️ Installing GitHub CLI...")
|
|
77
|
-
# Use msiexec for silent installation
|
|
78
42
|
subprocess.run(["msiexec", "/i", msi_path, "/quiet", "/norestart"], check=True)
|
|
79
43
|
|
|
80
|
-
# Clean up the downloaded file
|
|
81
44
|
os.remove(msi_path)
|
|
82
45
|
print("✅ GitHub CLI installed successfully.")
|
|
83
46
|
return True
|
|
84
47
|
except Exception as e:
|
|
85
|
-
print(f"❌ Direct installation failed: {e}")
|
|
48
|
+
print(f"❌ Direct installation failed: {e}", file=sys.stderr)
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
def check_gh_installed():
|
|
52
|
+
"""Check if GitHub CLI is installed. If not, prompt the user to install it."""
|
|
53
|
+
if shutil.which("gh"):
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
print("❓ GitHub CLI (gh) is required for the '--new-repo' feature but was not found.", file=sys.stderr)
|
|
57
|
+
try:
|
|
58
|
+
answer = input(" Would you like this tool to attempt an automatic installation? (y/n): ").lower().strip()
|
|
59
|
+
except (EOFError, KeyboardInterrupt):
|
|
60
|
+
print("\nInstallation cancelled.", file=sys.stderr)
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
if answer != 'y':
|
|
64
|
+
print("➡️ Installation skipped. Please install 'gh' manually from https://cli.github.com/", file=sys.stderr)
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
print("\n📦 Attempting to install GitHub CLI...")
|
|
68
|
+
system = platform.system()
|
|
69
|
+
installed = False
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
if system == "Windows":
|
|
73
|
+
installed = install_gh_cli_windows()
|
|
74
|
+
elif system == "Darwin":
|
|
75
|
+
print(" Running: brew install gh")
|
|
76
|
+
subprocess.run(["brew", "install", "gh"], check=True)
|
|
77
|
+
installed = True
|
|
78
|
+
elif system == "Linux":
|
|
79
|
+
print(" Running: sudo apt update && sudo apt install -y gh")
|
|
80
|
+
subprocess.run(["sudo", "apt", "update"], check=True)
|
|
81
|
+
subprocess.run(["sudo", "apt", "install", "-y", "gh"], check=True)
|
|
82
|
+
installed = True
|
|
83
|
+
else:
|
|
84
|
+
print(f"❌ Automatic installation is not supported for your OS ({system}).", file=sys.stderr)
|
|
85
|
+
print(" Please install 'gh' manually from https://cli.github.com/", file=sys.stderr)
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
except (subprocess.CalledProcessError, FileNotFoundError) as e:
|
|
89
|
+
print(f"❌ Installation failed: {e}", file=sys.stderr)
|
|
90
|
+
print(" Please try installing 'gh' manually from https://cli.github.com/", file=sys.stderr)
|
|
86
91
|
return False
|
|
87
92
|
|
|
93
|
+
if installed:
|
|
94
|
+
print("\n✅ GitHub CLI was installed successfully!", file=sys.stderr)
|
|
95
|
+
print("‼️ IMPORTANT: You must open a NEW terminal for the changes to take effect.", file=sys.stderr)
|
|
96
|
+
print(" Please re-run your command in a new terminal window.", file=sys.stderr)
|
|
97
|
+
sys.exit(0) # Exit gracefully so the user can follow instructions
|
|
98
|
+
|
|
99
|
+
return False
|
|
100
|
+
|
|
88
101
|
def gh_authenticated():
|
|
89
102
|
"""Check if user is authenticated with GitHub CLI"""
|
|
90
103
|
try:
|
|
91
|
-
result = subprocess.run(["gh", "auth", "status"], capture_output=True, text=True)
|
|
92
|
-
return
|
|
93
|
-
except:
|
|
104
|
+
result = subprocess.run(["gh", "auth", "status"], capture_output=True, text=True, check=True)
|
|
105
|
+
return "Logged in to github.com" in result.stderr
|
|
106
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
94
107
|
return False
|
|
95
108
|
|
|
96
109
|
def authenticate_with_gh():
|
|
97
110
|
"""Authenticate user with GitHub CLI"""
|
|
98
|
-
print("\n🔑 GitHub authentication required")
|
|
99
|
-
print("
|
|
100
|
-
print("This will open your browser for secure login")
|
|
111
|
+
print("\n🔑 GitHub authentication required.")
|
|
112
|
+
print("The tool will use the GitHub CLI (gh) to open a browser for secure login.")
|
|
101
113
|
|
|
102
114
|
try:
|
|
103
115
|
subprocess.run(["gh", "auth", "login", "--web", "-h", "github.com"], check=True)
|
|
104
116
|
return True
|
|
105
117
|
except subprocess.CalledProcessError:
|
|
106
|
-
print("❌ Authentication failed")
|
|
118
|
+
print("❌ Authentication failed. Please try running 'gh auth login' manually.", file=sys.stderr)
|
|
107
119
|
return False
|
|
108
120
|
|
|
109
121
|
def initialize_git_repository():
|
|
@@ -113,8 +125,8 @@ def initialize_git_repository():
|
|
|
113
125
|
|
|
114
126
|
print("🛠 Initializing git repository")
|
|
115
127
|
try:
|
|
116
|
-
subprocess.run(["git", "init"], check=True)
|
|
117
|
-
subprocess.run(["git", "branch", "-M", "main"], check=True)
|
|
128
|
+
subprocess.run(["git", "init"], check=True, capture_output=True)
|
|
129
|
+
subprocess.run(["git", "branch", "-M", "main"], check=True, capture_output=True)
|
|
118
130
|
|
|
119
131
|
if not os.path.exists(".gitignore"):
|
|
120
132
|
with open(".gitignore", "w") as f:
|
|
@@ -145,7 +157,7 @@ Thumbs.db
|
|
|
145
157
|
print("📁 Created .gitignore file")
|
|
146
158
|
return True
|
|
147
159
|
except subprocess.CalledProcessError as e:
|
|
148
|
-
print(f"❌ Failed to initialize Git repository: {e}")
|
|
160
|
+
print(f"❌ Failed to initialize Git repository: {e.stderr.decode().strip()}", file=sys.stderr)
|
|
149
161
|
return False
|
|
150
162
|
|
|
151
163
|
def create_initial_commit(commit_message="Initial commit"):
|
|
@@ -162,16 +174,23 @@ def create_initial_commit(commit_message="Initial commit"):
|
|
|
162
174
|
return True
|
|
163
175
|
return False
|
|
164
176
|
except subprocess.CalledProcessError as e:
|
|
165
|
-
|
|
177
|
+
if "nothing to commit" in e.stderr.decode():
|
|
178
|
+
print(f"❌ Failed to create initial commit: No files found to commit.", file=sys.stderr)
|
|
179
|
+
print("➡️ Add some files to your project directory before creating a repository.", file=sys.stderr)
|
|
180
|
+
else:
|
|
181
|
+
print(f"❌ Failed to create initial commit: {e.stderr.decode().strip()}", file=sys.stderr)
|
|
166
182
|
return False
|
|
167
183
|
|
|
168
184
|
def create_with_gh_cli(repo_name, private=False, description="", commit_message="Initial commit"):
|
|
169
185
|
"""Create and push to new repository using GitHub CLI"""
|
|
170
186
|
try:
|
|
171
|
-
if not os.path.exists(".git")
|
|
172
|
-
|
|
187
|
+
if not os.path.exists(".git"):
|
|
188
|
+
if not initialize_git_repository():
|
|
189
|
+
return False
|
|
173
190
|
|
|
174
191
|
if not create_initial_commit(commit_message):
|
|
192
|
+
if subprocess.run(["git", "status"], capture_output=True).returncode != 0:
|
|
193
|
+
return False
|
|
175
194
|
print("ℹ️ Using existing commits")
|
|
176
195
|
|
|
177
196
|
private_flag = "--private" if private else "--public"
|
|
@@ -182,21 +201,22 @@ def create_with_gh_cli(repo_name, private=False, description="", commit_message=
|
|
|
182
201
|
cmd.extend(["--description", description])
|
|
183
202
|
|
|
184
203
|
print("🚀 Creating repository and pushing code...")
|
|
185
|
-
subprocess.run(cmd, check=True)
|
|
204
|
+
process = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
|
186
205
|
|
|
187
|
-
|
|
188
|
-
["gh", "repo", "view", "--json", "url", "--jq", ".url"],
|
|
189
|
-
capture_output=True, text=True, check=True
|
|
190
|
-
)
|
|
191
|
-
repo_url = url_result.stdout.strip()
|
|
206
|
+
repo_url = process.stderr.strip()
|
|
192
207
|
print(f"✅ Successfully created repository: {repo_url}")
|
|
193
208
|
return True
|
|
194
209
|
|
|
195
210
|
except subprocess.CalledProcessError as e:
|
|
196
|
-
|
|
211
|
+
error_message = e.stderr.strip()
|
|
212
|
+
if "already exists" in error_message:
|
|
213
|
+
print(f"❌ Failed to create repository: {error_message}", file=sys.stderr)
|
|
214
|
+
print("➡️ Please choose a different repository name.", file=sys.stderr)
|
|
215
|
+
else:
|
|
216
|
+
print(f"❌ Failed to create repository: {error_message}", file=sys.stderr)
|
|
197
217
|
return False
|
|
198
218
|
except Exception as e:
|
|
199
|
-
print(f"❌
|
|
219
|
+
print(f"❌ An unexpected error occurred: {str(e)}", file=sys.stderr)
|
|
200
220
|
return False
|
|
201
221
|
|
|
202
222
|
def standard_git_push(commit_message, branch, remote, force=False, tags=False):
|
|
@@ -205,14 +225,15 @@ def standard_git_push(commit_message, branch, remote, force=False, tags=False):
|
|
|
205
225
|
subprocess.run(["git", "add", "."], check=True)
|
|
206
226
|
|
|
207
227
|
if commit_message:
|
|
208
|
-
print(f"📦 Committing: '{commit_message}'")
|
|
209
|
-
subprocess.run(["git", "commit", "-m", commit_message], check=True)
|
|
228
|
+
print(f"📦 Committing with message: '{commit_message}'")
|
|
229
|
+
subprocess.run(["git", "commit", "-m", commit_message, "--allow-empty-message"], check=True)
|
|
210
230
|
else:
|
|
211
|
-
print("ℹ️ No commit message provided
|
|
231
|
+
print("ℹ️ No commit message provided. Pushing only staged changes.")
|
|
212
232
|
|
|
213
233
|
push_cmd = ["git", "push"]
|
|
214
234
|
if force:
|
|
215
235
|
push_cmd.append("--force-with-lease")
|
|
236
|
+
print("⚠️ Using safe force push (--force-with-lease).")
|
|
216
237
|
if tags:
|
|
217
238
|
push_cmd.append("--tags")
|
|
218
239
|
if remote and branch:
|
|
@@ -220,10 +241,14 @@ def standard_git_push(commit_message, branch, remote, force=False, tags=False):
|
|
|
220
241
|
|
|
221
242
|
print(f"🚀 Executing: {' '.join(push_cmd)}")
|
|
222
243
|
subprocess.run(push_cmd, check=True)
|
|
223
|
-
print("✅ Successfully pushed changes")
|
|
244
|
+
print("✅ Successfully pushed changes.")
|
|
224
245
|
return True
|
|
225
246
|
except subprocess.CalledProcessError as e:
|
|
226
|
-
|
|
247
|
+
error_output = e.stderr.decode().strip() if e.stderr else str(e)
|
|
248
|
+
if "nothing to commit" in error_output:
|
|
249
|
+
print("ℹ️ No changes to commit. Nothing to do.")
|
|
250
|
+
return True
|
|
251
|
+
print(f"❌ Push failed: {error_output}", file=sys.stderr)
|
|
227
252
|
return False
|
|
228
253
|
|
|
229
254
|
def run():
|
|
@@ -231,37 +256,40 @@ def run():
|
|
|
231
256
|
description="🚀 Supercharged Git push tool with GitHub repo creation",
|
|
232
257
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
233
258
|
epilog="""Examples:
|
|
234
|
-
Standard push: gitpush_tool "
|
|
235
|
-
Create new repo: gitpush_tool "Initial commit" --new-repo project
|
|
236
|
-
Private repository: gitpush_tool --new-repo
|
|
237
|
-
Force push:
|
|
259
|
+
Standard push: gitpush_tool "My new feature"
|
|
260
|
+
Create new repo: gitpush_tool "Initial commit" --new-repo my-awesome-project
|
|
261
|
+
Private repository: gitpush_tool "Initial commit" --new-repo my-secret-project --private
|
|
262
|
+
Force push (safe): gitpush_tool "Rebased feature" --force
|
|
263
|
+
Initialize only: gitpush_tool --init
|
|
238
264
|
"""
|
|
239
265
|
)
|
|
240
|
-
parser.add_argument("commit", nargs="?", help="Commit message")
|
|
241
|
-
parser.add_argument("branch", nargs="?", default=
|
|
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
|
|
246
|
-
parser.add_argument("--new-repo", metavar="
|
|
247
|
-
parser.add_argument("--private", action="store_true", help="Make repository private")
|
|
248
|
-
parser.add_argument("--description", help="
|
|
266
|
+
parser.add_argument("commit", nargs="?", help="Commit message (optional if just pushing staged changes).")
|
|
267
|
+
parser.add_argument("branch", nargs="?", default=None, help="Branch name (defaults to current branch).")
|
|
268
|
+
parser.add_argument("remote", nargs="?", default="origin", help="Remote name (default: origin).")
|
|
269
|
+
parser.add_argument("--force", action="store_true", help="Force push with --force-with-lease.")
|
|
270
|
+
parser.add_argument("--tags", action="store_true", help="Push all tags.")
|
|
271
|
+
parser.add_argument("--init", action="store_true", help="Initialize a new Git repository and exit.")
|
|
272
|
+
parser.add_argument("--new-repo", metavar="REPO_NAME", help="Create a new GitHub repository with the given name.")
|
|
273
|
+
parser.add_argument("--private", action="store_true", help="Make the new repository private.")
|
|
274
|
+
parser.add_argument("--description", help="Description for the new repository.")
|
|
249
275
|
|
|
250
276
|
args = parser.parse_args()
|
|
277
|
+
|
|
278
|
+
target_branch = args.branch
|
|
279
|
+
if not target_branch:
|
|
280
|
+
try:
|
|
281
|
+
branch_result = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], capture_output=True, text=True, check=True)
|
|
282
|
+
target_branch = branch_result.stdout.strip()
|
|
283
|
+
except subprocess.CalledProcessError:
|
|
284
|
+
target_branch = "main"
|
|
251
285
|
|
|
252
286
|
if args.new_repo:
|
|
253
|
-
print(f"🆕 Creating repository: {args.new_repo}")
|
|
254
|
-
|
|
255
287
|
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
288
|
sys.exit(1)
|
|
262
289
|
|
|
263
|
-
if not gh_authenticated()
|
|
264
|
-
|
|
290
|
+
if not gh_authenticated():
|
|
291
|
+
if not authenticate_with_gh():
|
|
292
|
+
sys.exit(1)
|
|
265
293
|
|
|
266
294
|
if not create_with_gh_cli(
|
|
267
295
|
args.new_repo,
|
|
@@ -270,13 +298,15 @@ def run():
|
|
|
270
298
|
commit_message=args.commit or "Initial commit"
|
|
271
299
|
):
|
|
272
300
|
sys.exit(1)
|
|
301
|
+
|
|
273
302
|
elif args.init:
|
|
274
303
|
if initialize_git_repository():
|
|
275
|
-
|
|
304
|
+
print("✅ Git repository initialized successfully.")
|
|
305
|
+
|
|
276
306
|
else:
|
|
277
307
|
if not standard_git_push(
|
|
278
308
|
args.commit,
|
|
279
|
-
|
|
309
|
+
target_branch,
|
|
280
310
|
args.remote,
|
|
281
311
|
args.force,
|
|
282
312
|
args.tags
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.8"
|
|
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
|
|
File without changes
|