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