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.
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/PKG-INFO +7 -1
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/README.md +6 -0
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/README.ru.md +1 -0
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/pyproject.toml +2 -2
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/src/smart_commit/cli.py +1 -1
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/src/smart_commit/services/git.py +32 -3
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/src/smart_commit/services/security.py +9 -4
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/.gitignore +0 -0
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/LICENSE +0 -0
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/assets/demonstration.gif +0 -0
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/src/smart_commit/config.py +0 -0
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/src/smart_commit/exceptions.py +0 -0
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/src/smart_commit/logger.py +0 -0
- {smart_commit_tool-1.0.0 → smart_commit_tool-1.1.0}/src/smart_commit/services/validator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: smart-commit-tool
|
|
3
|
-
Version: 1.
|
|
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
|
+
[](https://pypi.org/project/smart-commit-tool/)
|
|
16
|
+
[](https://pypi.org/project/smart-commit-tool/)
|
|
17
|
+
[](https://opensource.org/licenses/MIT)
|
|
18
|
+
[](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
|
+

|
|
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
|
+
[](https://pypi.org/project/smart-commit-tool/)
|
|
6
|
+
[](https://pypi.org/project/smart-commit-tool/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](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
|
+

|
|
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
|
+

|
|
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.
|
|
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/
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
50
|
-
if
|
|
51
|
-
|
|
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
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|