smart-commit-tool 2.0.1__tar.gz → 2.0.3__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-2.0.3/.github/workflows/smart_commit_ci.yml +68 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/PKG-INFO +1 -1
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/pyproject.toml +1 -1
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/src/smart_commit/constants.py +5 -6
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/src/smart_commit/services/git.py +32 -13
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/.gitignore +0 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/LICENSE +0 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/README.md +0 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/README.ru.md +0 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/assets/demonstration.gif +0 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/src/smart_commit/cli.py +0 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/src/smart_commit/config.py +0 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/src/smart_commit/exceptions.py +0 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/src/smart_commit/logger.py +0 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/src/smart_commit/services/ci.py +0 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/src/smart_commit/services/security.py +0 -0
- {smart_commit_tool-2.0.1 → smart_commit_tool-2.0.3}/src/smart_commit/services/validator.py +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Smart Commit CI Pipeline
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ "main", "master", "prod", "release" ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ "main", "master", "prod", "release" ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
validate:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
# Runs the pipeline on multiple Python versions automatically
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout Repository
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
cache: 'pip' # Speeds up consecutive runs
|
|
27
|
+
|
|
28
|
+
- name: Install Dependencies (Adaptive Smart Installer)
|
|
29
|
+
run: |
|
|
30
|
+
python -m pip install --upgrade pip
|
|
31
|
+
|
|
32
|
+
echo "🔍 Detecting package manager..."
|
|
33
|
+
|
|
34
|
+
if [ -f "poetry.lock" ] || [ -f "pyproject.toml" ] && grep -q "tool.poetry" "pyproject.toml"; then
|
|
35
|
+
echo "🚀 Poetry detected!"
|
|
36
|
+
pip install poetry
|
|
37
|
+
# Force poetry to install globally so custom commands work without 'poetry run'
|
|
38
|
+
poetry config virtualenvs.create false
|
|
39
|
+
poetry install --no-interaction --no-ansi
|
|
40
|
+
|
|
41
|
+
elif [ -f "Pipfile.lock" ] || [ -f "Pipfile" ]; then
|
|
42
|
+
echo "🚀 Pipenv detected!"
|
|
43
|
+
pip install pipenv
|
|
44
|
+
pipenv install --system --dev
|
|
45
|
+
|
|
46
|
+
elif [ -f "pdm.lock" ] || [ -f "pyproject.toml" ] && grep -q "tool.pdm" "pyproject.toml"; then
|
|
47
|
+
echo "🚀 PDM detected!"
|
|
48
|
+
pip install pdm
|
|
49
|
+
pdm config python.use_venv false
|
|
50
|
+
pdm install
|
|
51
|
+
|
|
52
|
+
elif [ -f "requirements.txt" ]; then
|
|
53
|
+
echo "🚀 Standard requirements.txt detected!"
|
|
54
|
+
pip install -r requirements.txt
|
|
55
|
+
# Install package itself if setup.py exists
|
|
56
|
+
if [ -f "setup.py" ]; then pip install -e .; fi
|
|
57
|
+
|
|
58
|
+
elif [ -f "pyproject.toml" ]; then
|
|
59
|
+
echo "🚀 Standard PEP 621 pyproject.toml detected (Hatch, Flit, Setuptools)!"
|
|
60
|
+
# Attempt to install with dev dependencies if available, otherwise regular install
|
|
61
|
+
pip install -e .[dev] || pip install -e .
|
|
62
|
+
|
|
63
|
+
else
|
|
64
|
+
echo "⚠️ No known package manager found. Skipping dependency installation."
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
- name: Run 'ruff check .'
|
|
68
|
+
run: ruff check .
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "smart-commit-tool"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.3"
|
|
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"
|
|
@@ -56,7 +56,6 @@ jobs:
|
|
|
56
56
|
strategy:
|
|
57
57
|
fail-fast: false
|
|
58
58
|
matrix:
|
|
59
|
-
# Runs the pipeline on multiple Python versions automatically
|
|
60
59
|
python-version: ["3.10", "3.11", "3.12"]
|
|
61
60
|
|
|
62
61
|
steps:
|
|
@@ -67,18 +66,20 @@ jobs:
|
|
|
67
66
|
uses: actions/setup-python@v5
|
|
68
67
|
with:
|
|
69
68
|
python-version: ${{ matrix.python-version }}
|
|
70
|
-
cache: 'pip'
|
|
69
|
+
cache: 'pip'
|
|
71
70
|
|
|
72
71
|
- name: Install Dependencies (Adaptive Smart Installer)
|
|
73
72
|
run: |
|
|
74
73
|
python -m pip install --upgrade pip
|
|
75
74
|
|
|
75
|
+
# Ensure local bin is in PATH for all subsequent steps
|
|
76
|
+
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
|
77
|
+
|
|
76
78
|
echo "🔍 Detecting package manager..."
|
|
77
79
|
|
|
78
80
|
if [ -f "poetry.lock" ] || [ -f "pyproject.toml" ] && grep -q "tool.poetry" "pyproject.toml"; then
|
|
79
81
|
echo "🚀 Poetry detected!"
|
|
80
82
|
pip install poetry
|
|
81
|
-
# Force poetry to install globally so custom commands work without 'poetry run'
|
|
82
83
|
poetry config virtualenvs.create false
|
|
83
84
|
poetry install --no-interaction --no-ansi
|
|
84
85
|
|
|
@@ -96,12 +97,10 @@ jobs:
|
|
|
96
97
|
elif [ -f "requirements.txt" ]; then
|
|
97
98
|
echo "🚀 Standard requirements.txt detected!"
|
|
98
99
|
pip install -r requirements.txt
|
|
99
|
-
# Install package itself if setup.py exists
|
|
100
100
|
if [ -f "setup.py" ]; then pip install -e .; fi
|
|
101
101
|
|
|
102
102
|
elif [ -f "pyproject.toml" ]; then
|
|
103
|
-
echo "🚀 Standard PEP 621 pyproject.toml detected
|
|
104
|
-
# Attempt to install with dev dependencies if available, otherwise regular install
|
|
103
|
+
echo "🚀 Standard PEP 621 pyproject.toml detected!"
|
|
105
104
|
pip install -e .[dev] || pip install -e .
|
|
106
105
|
|
|
107
106
|
else
|
|
@@ -7,21 +7,32 @@ from ..logger import Console, logger
|
|
|
7
7
|
class GitService:
|
|
8
8
|
@classmethod
|
|
9
9
|
def _run(cls, cmd: list[str], silent: bool = True) -> subprocess.CompletedProcess:
|
|
10
|
-
"""Internal helper for executing Git commands."""
|
|
10
|
+
"""Internal helper for executing Git commands with combined output."""
|
|
11
11
|
logger.debug(f"Git CMD: {' '.join(cmd)}")
|
|
12
|
-
return subprocess.run(cmd, capture_output=
|
|
12
|
+
return subprocess.run(cmd, capture_output=True, text=True)
|
|
13
13
|
|
|
14
14
|
@classmethod
|
|
15
15
|
def ensure_repo(cls):
|
|
16
|
-
"""Checks for repository initialization
|
|
17
|
-
|
|
16
|
+
"""Checks for repository initialization, Detached HEAD, and ongoing merges/rebases."""
|
|
17
|
+
git_dir = Path(".git")
|
|
18
|
+
if not git_dir.exists():
|
|
18
19
|
Console.warning("Git not initialized. Initializing now...")
|
|
19
20
|
cls._run(["git", "init"])
|
|
21
|
+
is_rebase = (git_dir / "rebase-apply").exists() or (
|
|
22
|
+
git_dir / "rebase-merge"
|
|
23
|
+
).exists()
|
|
24
|
+
is_merge = (git_dir / "MERGE_HEAD").exists()
|
|
25
|
+
|
|
26
|
+
if is_rebase or is_merge:
|
|
27
|
+
raise GitOperationError(
|
|
28
|
+
"🛑 Git is currently in the middle of a REBASE or MERGE.\n"
|
|
29
|
+
"Please resolve conflicts manually or run 'git rebase --abort', then try again."
|
|
30
|
+
)
|
|
20
31
|
|
|
21
32
|
res = cls._run(["git", "branch", "--show-current"])
|
|
22
33
|
if not res.stdout.strip():
|
|
23
34
|
raise GitOperationError(
|
|
24
|
-
"You are in a DETACHED HEAD state.
|
|
35
|
+
"You are in a DETACHED HEAD state. Please switch to a branch (e.g., 'git checkout main')."
|
|
25
36
|
)
|
|
26
37
|
|
|
27
38
|
@classmethod
|
|
@@ -76,33 +87,41 @@ class GitService:
|
|
|
76
87
|
|
|
77
88
|
@classmethod
|
|
78
89
|
def commit(cls, message: str):
|
|
79
|
-
"""Commits changes
|
|
90
|
+
"""Commits changes. Ignores 'nothing to commit' errors to allow push-only flows."""
|
|
80
91
|
res = cls._run(["git", "commit", "-m", message])
|
|
92
|
+
|
|
81
93
|
if res.returncode != 0:
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
output = (res.stdout + res.stderr).lower()
|
|
95
|
+
if "nothing to commit" in output or "working tree clean" in output:
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
raise GitOperationError(
|
|
99
|
+
f"Commit failed. Git says:\n{res.stderr or res.stdout}"
|
|
100
|
+
)
|
|
84
101
|
|
|
85
102
|
@classmethod
|
|
86
103
|
def push_with_retry(cls, branch: str):
|
|
87
|
-
"""Pushes to remote, attempting a pull --rebase if rejected."""
|
|
104
|
+
"""Pushes to remote, attempting a pull --rebase if rejected or diverged."""
|
|
88
105
|
Console.info(f"Pushing to branch: {branch}...")
|
|
89
106
|
res = cls._run(["git", "push", "-u", "origin", branch])
|
|
90
107
|
|
|
91
108
|
if res.returncode == 0:
|
|
92
109
|
Console.success(f"Code successfully pushed to origin/{branch}!")
|
|
93
110
|
return
|
|
111
|
+
Console.warning("Push rejected or branches diverged. Remote changes detected.")
|
|
112
|
+
Console.info("Synchronizing via pull --rebase...")
|
|
94
113
|
|
|
95
|
-
Console.warning("Push rejected. Remote changes detected.")
|
|
96
|
-
Console.info("Synchronizing (pull --rebase)...")
|
|
97
114
|
pull_res = cls._run(["git", "pull", "origin", branch, "--rebase"])
|
|
98
115
|
|
|
99
116
|
if pull_res.returncode != 0:
|
|
100
117
|
error_msg = pull_res.stderr.strip() or pull_res.stdout.strip()
|
|
118
|
+
cls._run(["git", "rebase", "--abort"])
|
|
101
119
|
raise GitOperationError(
|
|
102
|
-
f"🛑 Rebase conflict detected
|
|
120
|
+
f"🛑 Rebase conflict! Conflicts were detected with remote files.\n"
|
|
121
|
+
f"Sync aborted. Please resolve manually. Git says:\n{error_msg}"
|
|
103
122
|
)
|
|
104
123
|
|
|
105
|
-
Console.success("Sync successful. Retrying push...")
|
|
124
|
+
Console.success("Sync successful. Retrying final push...")
|
|
106
125
|
push_retry = cls._run(["git", "push", "-u", "origin", branch])
|
|
107
126
|
|
|
108
127
|
if push_retry.returncode != 0:
|
|
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
|
|
File without changes
|
|
File without changes
|