smart-commit-tool 1.0.0__tar.gz → 1.1.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: smart-commit-tool
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Automated pre-push workflow manager with built-in code quality enforcement and smart branch protection.
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
@@ -12,6 +12,11 @@ Description-Content-Type: text/markdown
12
12
 
13
13
  [English](README.md) | [Русский](README.ru.md)
14
14
 
15
+ [![PyPI version](https://img.shields.io/pypi/v/smart-commit-tool?color=blue&logo=pypi&logoColor=white)](https://pypi.org/project/smart-commit-tool/)
16
+ [![Python Version](https://img.shields.io/badge/python-3.8+-blue?logo=python&logoColor=white)](https://pypi.org/project/smart-commit-tool/)
17
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
18
+ [![GitHub stars](https://img.shields.io/github/stars/mokinprokin/smart_commit?style=social)](https://github.com/mokinprokin/smart_commit/stargazers)
19
+
15
20
  **Smart Commit** is a universal CLI tool designed to streamline and secure your Git workflow. It orchestrates linting, testing, committing, and pushing into a single, bulletproof operation.
16
21
 
17
22
  **The Golden Rule:** If your tests fail, your code doesn't ship.
@@ -86,6 +91,7 @@ smart-commit
86
91
  4. **Optimized Push**: Automatically handles `add` and `commit`. If the remote branch has moved forward, it helps you `rebase` to ensure a linear, clean history.
87
92
 
88
93
  ---
94
+ ![Демонстрация smart-commit](assets/demonstration.gif)
89
95
 
90
96
  ## 📄 License
91
97
  Distributed under the MIT License. Feel free to use, modify, and share!
@@ -2,6 +2,11 @@
2
2
 
3
3
  [English](README.md) | [Русский](README.ru.md)
4
4
 
5
+ [![PyPI version](https://img.shields.io/pypi/v/smart-commit-tool?color=blue&logo=pypi&logoColor=white)](https://pypi.org/project/smart-commit-tool/)
6
+ [![Python Version](https://img.shields.io/badge/python-3.8+-blue?logo=python&logoColor=white)](https://pypi.org/project/smart-commit-tool/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![GitHub stars](https://img.shields.io/github/stars/mokinprokin/smart_commit?style=social)](https://github.com/mokinprokin/smart_commit/stargazers)
9
+
5
10
  **Smart Commit** is a universal CLI tool designed to streamline and secure your Git workflow. It orchestrates linting, testing, committing, and pushing into a single, bulletproof operation.
6
11
 
7
12
  **The Golden Rule:** If your tests fail, your code doesn't ship.
@@ -76,6 +81,7 @@ smart-commit
76
81
  4. **Optimized Push**: Automatically handles `add` and `commit`. If the remote branch has moved forward, it helps you `rebase` to ensure a linear, clean history.
77
82
 
78
83
  ---
84
+ ![Демонстрация smart-commit](assets/demonstration.gif)
79
85
 
80
86
  ## 📄 License
81
87
  Distributed under the MIT License. Feel free to use, modify, and share!
@@ -75,6 +75,7 @@ smart-commit
75
75
  4. **Умный Push**: Инструмент сам сделает `add`, `commit` и отправит код. Если на сервере появились новые изменения, он предложит сделать `rebase`, чтобы история оставалась чистой.
76
76
 
77
77
  ---
78
+ ![Демонстрация smart-commit](assets/demonstration.gif)
78
79
 
79
80
  ## 📄 Лицензия
80
81
  Распространяется под лицензией **MIT**. Пользуйтесь, меняйте и делитесь с друзьями!
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "smart-commit-tool"
7
- version = "1.0.0"
7
+ version = "1.1.0"
8
8
  description = "Automated pre-push workflow manager with built-in code quality enforcement and smart branch protection."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -17,7 +17,7 @@ dependencies = [
17
17
  smart-commit = "smart_commit.cli:main"
18
18
 
19
19
  [tool.smart_commit]
20
- repository_url = "https://github.com/mokinprokin/smart_commit.git"
20
+ repository_url = "https://github.com/mokinprokin/SmartCommit.git"
21
21
  commands = ["ruff check ."]
22
22
  protected_branches = ["main", "master", "prod", "release"]
23
23
 
@@ -27,7 +27,7 @@ def main():
27
27
 
28
28
  current_branch = GitService.get_current_branch()
29
29
  branch = args.branch or current_branch
30
-
30
+ GitService.switch_branch(branch)
31
31
  if branch in protected_branches:
32
32
  Console.warning(
33
33
  f"You are pushing directly to a protected branch: '{branch}'."
@@ -28,6 +28,29 @@ class GitService:
28
28
  def get_current_branch(cls) -> str:
29
29
  return cls._run(["git", "branch", "--show-current"]).stdout.strip()
30
30
 
31
+ @classmethod
32
+ def switch_branch(cls, branch: str):
33
+ """Safely switches to the target branch or creates it."""
34
+ current = cls.get_current_branch()
35
+ if current != branch:
36
+ Console.info(f"🌿 Switching branch: {current} ➔ {branch}")
37
+ has_commits = cls._run(["git", "rev-parse", "HEAD"]).returncode == 0
38
+
39
+ if not has_commits:
40
+ res = cls._run(["git", "branch", "-M", branch])
41
+ else:
42
+ res = cls._run(["git", "checkout", "-B", branch])
43
+
44
+ if res.returncode != 0:
45
+ error_msg = res.stderr.strip() or res.stdout.strip()
46
+ raise GitOperationError(f"Failed to switch branch: {error_msg}")
47
+
48
+ @classmethod
49
+ def has_any_changes(cls) -> bool:
50
+ """Checks if there are any staged, unstaged, or untracked changes."""
51
+ res = cls._run(["git", "status", "--porcelain"])
52
+ return bool(res.stdout.strip())
53
+
31
54
  @classmethod
32
55
  def has_staged_changes(cls) -> bool:
33
56
  """Checks if there are files already in the staging area."""
@@ -53,12 +76,15 @@ class GitService:
53
76
 
54
77
  @classmethod
55
78
  def commit(cls, message: str):
79
+ """Commits changes and extracts Git output if it fails."""
56
80
  res = cls._run(["git", "commit", "-m", message])
57
81
  if res.returncode != 0:
58
- raise GitOperationError("Commit failed. Ensure you have changes to commit.")
82
+ error_msg = res.stderr.strip() or res.stdout.strip()
83
+ raise GitOperationError(f"Commit failed. Git says:\n{error_msg}")
59
84
 
60
85
  @classmethod
61
86
  def push_with_retry(cls, branch: str):
87
+ """Pushes to remote, attempting a pull --rebase if rejected."""
62
88
  Console.info(f"Pushing to branch: {branch}...")
63
89
  res = cls._run(["git", "push", "-u", "origin", branch])
64
90
 
@@ -71,11 +97,14 @@ class GitService:
71
97
  pull_res = cls._run(["git", "pull", "origin", branch, "--rebase"])
72
98
 
73
99
  if pull_res.returncode != 0:
100
+ error_msg = pull_res.stderr.strip() or pull_res.stdout.strip()
74
101
  raise GitOperationError(
75
- "🛑 Rebase conflict detected! Please resolve manually and run again."
102
+ f"🛑 Rebase conflict detected!\nGit says: {error_msg}"
76
103
  )
77
104
 
78
105
  Console.success("Sync successful. Retrying push...")
79
106
  push_retry = cls._run(["git", "push", "-u", "origin", branch])
107
+
80
108
  if push_retry.returncode != 0:
81
- raise GitOperationError("Final push failed after rebase.")
109
+ error_msg = push_retry.stderr.strip() or push_retry.stdout.strip()
110
+ raise GitOperationError(f"Final push failed after rebase:\n{error_msg}")
@@ -13,6 +13,7 @@ __pycache__/
13
13
  venv/
14
14
  env/
15
15
  .env*
16
+ *.env
16
17
  !.env.example
17
18
  .vscode/
18
19
  .idea/
@@ -39,16 +40,20 @@ node_modules/
39
40
  @classmethod
40
41
  def check_env_leaks(cls):
41
42
  """
42
- Scans for .env* files and uses Git to check if they are ignored.
43
+ Scans for .env files (e.g. .env.local, .test.env, prod.env)
44
+ and uses Git to check if they are ignored.
43
45
  Prevents accidental leaks of secrets.
44
46
  """
45
47
  Console.info("🛡️ Scanning for potential secret leaks (.env files)...")
46
48
 
49
+ raw_files = set(Path(".").rglob(".env*")) | set(Path(".").rglob("*.env"))
50
+
47
51
  env_files = [
48
52
  f
49
- for f in Path(".").rglob(".env*")
50
- if not any(
51
- part in ["venv", ".venv", "env", "node_modules", ".git"]
53
+ for f in raw_files
54
+ if f.is_file()
55
+ and not any(
56
+ part in ["venv", ".venv", "env", "node_modules", ".git", "__pycache__"]
52
57
  for part in f.parts
53
58
  )
54
59
  ]