bump-py-version 2.1.0__py3-none-any.whl
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.
- bump_py_version/__init__.py +1 -0
- bump_py_version/cli.py +245 -0
- bump_py_version-2.1.0.dist-info/METADATA +94 -0
- bump_py_version-2.1.0.dist-info/RECORD +8 -0
- bump_py_version-2.1.0.dist-info/WHEEL +5 -0
- bump_py_version-2.1.0.dist-info/entry_points.txt +2 -0
- bump_py_version-2.1.0.dist-info/licenses/LICENSE +21 -0
- bump_py_version-2.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.0"
|
bump_py_version/cli.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import argparse
|
|
3
|
+
import shlex
|
|
4
|
+
import shutil
|
|
5
|
+
import tomlkit
|
|
6
|
+
import subprocess
|
|
7
|
+
from bump_py_version import __version__
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse_version_tag(tag):
|
|
11
|
+
"""
|
|
12
|
+
Remove leading 'v' if it exists
|
|
13
|
+
"""
|
|
14
|
+
if tag.startswith("v"):
|
|
15
|
+
return tag[1:]
|
|
16
|
+
return tag
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def alter_pyproject(doc, version):
|
|
20
|
+
"""
|
|
21
|
+
Changes the project version and the poetry version
|
|
22
|
+
in the pyproject.toml file, preserving formatting.
|
|
23
|
+
"""
|
|
24
|
+
version = parse_version_tag(version)
|
|
25
|
+
|
|
26
|
+
if "project" in doc:
|
|
27
|
+
doc["project"]["version"] = version
|
|
28
|
+
|
|
29
|
+
with open("pyproject.toml", "w", encoding="utf-8") as f:
|
|
30
|
+
f.write(tomlkit.dumps(doc))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def alter_init(path, version):
|
|
34
|
+
"""
|
|
35
|
+
Changes the version in the __init__.py file
|
|
36
|
+
"""
|
|
37
|
+
version = parse_version_tag(version)
|
|
38
|
+
with open(path, "r") as f:
|
|
39
|
+
lines = f.readlines()
|
|
40
|
+
with open(path, "w") as f:
|
|
41
|
+
for line in lines:
|
|
42
|
+
if line.startswith("__version__ ="):
|
|
43
|
+
f.write(f'__version__ = "{version}"\n')
|
|
44
|
+
else:
|
|
45
|
+
f.write(line)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def alter_text_file(file, str_search, replace):
|
|
49
|
+
"""
|
|
50
|
+
Changes the version in a text file by replacing
|
|
51
|
+
the line after the line matching str_search.
|
|
52
|
+
"""
|
|
53
|
+
dynamic_next_line = False
|
|
54
|
+
with open(file, "r") as f:
|
|
55
|
+
lines = f.readlines()
|
|
56
|
+
with open(file, "w") as f:
|
|
57
|
+
for line in lines:
|
|
58
|
+
if dynamic_next_line:
|
|
59
|
+
f.write(replace)
|
|
60
|
+
dynamic_next_line = False
|
|
61
|
+
elif line.startswith(str_search):
|
|
62
|
+
f.write(line)
|
|
63
|
+
dynamic_next_line = True
|
|
64
|
+
else:
|
|
65
|
+
f.write(line)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def alter_version(version):
|
|
69
|
+
try:
|
|
70
|
+
with open("pyproject.toml", "r", encoding="utf-8") as f:
|
|
71
|
+
content = f.read()
|
|
72
|
+
doc = tomlkit.parse(content)
|
|
73
|
+
except FileNotFoundError:
|
|
74
|
+
doc = None
|
|
75
|
+
|
|
76
|
+
# Alter pyproject.toml
|
|
77
|
+
if doc:
|
|
78
|
+
alter_pyproject(doc, version)
|
|
79
|
+
|
|
80
|
+
# Alter __init__.py
|
|
81
|
+
try:
|
|
82
|
+
version_file = doc["tool"]["bump_version"]["version_file"]
|
|
83
|
+
alter_init(version_file, version)
|
|
84
|
+
except KeyError:
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
# Alter text files
|
|
88
|
+
try:
|
|
89
|
+
replace_patterns = doc["tool"]["bump_version"]["replace_patterns"]
|
|
90
|
+
for _, pattern in replace_patterns.items():
|
|
91
|
+
pattern["replace"] = pattern["replace"].replace("{version}", version)
|
|
92
|
+
alter_text_file(
|
|
93
|
+
pattern["file"],
|
|
94
|
+
pattern["search"],
|
|
95
|
+
pattern["replace"],
|
|
96
|
+
)
|
|
97
|
+
except KeyError:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def run_command_check_untracked():
|
|
102
|
+
"""
|
|
103
|
+
Checks if there are untracked files.
|
|
104
|
+
"""
|
|
105
|
+
status_message = """There are untracked files.
|
|
106
|
+
Use `git status` to see the files.
|
|
107
|
+
Please remove or commit the files before running the command."""
|
|
108
|
+
|
|
109
|
+
result = subprocess.run(
|
|
110
|
+
"git ls-files --others --exclude-standard",
|
|
111
|
+
check=True,
|
|
112
|
+
shell=True,
|
|
113
|
+
stdout=subprocess.PIPE,
|
|
114
|
+
stderr=subprocess.PIPE,
|
|
115
|
+
text=True,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if result.stdout.strip():
|
|
119
|
+
print(status_message)
|
|
120
|
+
sys.exit(1)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def run_command_check_uncommited():
|
|
124
|
+
"""
|
|
125
|
+
Checks if there are uncommited changes.
|
|
126
|
+
"""
|
|
127
|
+
status_message = (
|
|
128
|
+
"There are uncommited changes. " "Use `git status` to see the changes.\n" "Please commit the changes before running the command."
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
subprocess.run(
|
|
133
|
+
"git diff-index --quiet HEAD --",
|
|
134
|
+
check=True,
|
|
135
|
+
shell=True,
|
|
136
|
+
stdout=subprocess.PIPE,
|
|
137
|
+
stderr=subprocess.PIPE,
|
|
138
|
+
text=True,
|
|
139
|
+
)
|
|
140
|
+
except Exception:
|
|
141
|
+
print(status_message)
|
|
142
|
+
sys.exit(1)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def run_command(command):
|
|
146
|
+
try:
|
|
147
|
+
subprocess.run(command, check=True, shell=True)
|
|
148
|
+
except subprocess.CalledProcessError as e:
|
|
149
|
+
error_message = "Command execution failed: " + str(e)
|
|
150
|
+
print(error_message)
|
|
151
|
+
sys.exit(1)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def check_uv_lock():
|
|
155
|
+
"""Read the opt-in setting and check prerequisites before editing files."""
|
|
156
|
+
try:
|
|
157
|
+
with open("pyproject.toml", "r", encoding="utf-8") as f:
|
|
158
|
+
doc = tomlkit.parse(f.read())
|
|
159
|
+
except FileNotFoundError:
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
enabled = doc.get("tool", {}).get("bump_version", {}).get("uv_lock", False)
|
|
163
|
+
if not isinstance(enabled, bool):
|
|
164
|
+
print("tool.bump_version.uv_lock must be true or false.")
|
|
165
|
+
sys.exit(1)
|
|
166
|
+
if not enabled:
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
result = subprocess.run(["git", "check-ignore", "--quiet", "uv.lock"])
|
|
170
|
+
if result.returncode == 0:
|
|
171
|
+
print(
|
|
172
|
+
"uv.lock is ignored by Git. Remove uv.lock from .gitignore "
|
|
173
|
+
"(or the applicable Git ignore rules) before using uv_lock = true."
|
|
174
|
+
)
|
|
175
|
+
sys.exit(1)
|
|
176
|
+
if result.returncode != 1:
|
|
177
|
+
print("Could not check whether uv.lock is ignored by Git.")
|
|
178
|
+
sys.exit(1)
|
|
179
|
+
if shutil.which("uv") is None:
|
|
180
|
+
print("uv_lock = true requires uv to be installed and available on PATH.")
|
|
181
|
+
sys.exit(1)
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def bump_version(version, message=None):
|
|
186
|
+
uv_lock = check_uv_lock()
|
|
187
|
+
|
|
188
|
+
# Check if there are files that are not tracked. If there are, exit
|
|
189
|
+
run_command_check_untracked()
|
|
190
|
+
|
|
191
|
+
# Check if there are uncommited changes
|
|
192
|
+
run_command_check_uncommited()
|
|
193
|
+
|
|
194
|
+
# Alter the version in the files
|
|
195
|
+
alter_version(version)
|
|
196
|
+
|
|
197
|
+
if uv_lock:
|
|
198
|
+
run_command("uv lock")
|
|
199
|
+
|
|
200
|
+
# Add the changes
|
|
201
|
+
run_command("git add .")
|
|
202
|
+
|
|
203
|
+
# Commit the changes
|
|
204
|
+
run_command(f'git commit -m "bump version to {version}"')
|
|
205
|
+
|
|
206
|
+
# Push the changes
|
|
207
|
+
run_command("git push")
|
|
208
|
+
|
|
209
|
+
# Create a tag
|
|
210
|
+
tag_message = message if message else f"bump version to {version}"
|
|
211
|
+
run_command(f"git tag -a {shlex.quote(version)} -m {shlex.quote(tag_message)}")
|
|
212
|
+
|
|
213
|
+
# Push the tag
|
|
214
|
+
run_command("git push --tags")
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def get_version():
|
|
218
|
+
"""Reads version from pyproject.toml at runtime, only when needed."""
|
|
219
|
+
with open("pyproject.toml", "r", encoding="utf-8") as f:
|
|
220
|
+
content = f.read()
|
|
221
|
+
doc = tomlkit.parse(content)
|
|
222
|
+
return doc["project"]["version"]
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def cli():
|
|
226
|
+
parser = argparse.ArgumentParser(
|
|
227
|
+
prog="bump-py-version",
|
|
228
|
+
description=f"Bump the version of a git-enabled python package. Version ({__version__}).",
|
|
229
|
+
epilog="""Example:
|
|
230
|
+
|
|
231
|
+
bump-py-version v1.2.3
|
|
232
|
+
""",
|
|
233
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
234
|
+
)
|
|
235
|
+
parser.add_argument("version", help="Version to bump to, e.g. v1.2.3 or 1.2.3")
|
|
236
|
+
parser.add_argument(
|
|
237
|
+
"--message",
|
|
238
|
+
help="Custom git tag message. Overrides the default tag message.",
|
|
239
|
+
)
|
|
240
|
+
args = parser.parse_args()
|
|
241
|
+
bump_version(args.version, message=args.message)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
if __name__ == "__main__":
|
|
245
|
+
cli()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bump-py-version
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: Bump a python version in python files. Create a new tag and push it to git repository.
|
|
5
|
+
Author-email: Dennis Iversen <dennis.iversen@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/diversen/bump-py-version
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Topic :: Utilities
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: tomlkit==0.13.3
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# bump-py-version
|
|
22
|
+
|
|
23
|
+
This is a simple and opinionated python `tool` to bump the of a python package.
|
|
24
|
+
|
|
25
|
+
1. The script will check for any changes in the current `git` repository and abort if there are any.
|
|
26
|
+
|
|
27
|
+
2. If there are no changes, it will check for a `version` field in `pyproject.toml`. If the `project.version` exist it is altered to the specified version. Other options are specified below.
|
|
28
|
+
|
|
29
|
+
3. The script commits and pushes the changes.
|
|
30
|
+
4. Then a new tag is created using the new `version` specified to the script.
|
|
31
|
+
5. Finally the script pushes the new tag to the remote repository.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
Install latest version:
|
|
36
|
+
|
|
37
|
+
<!-- LATEST-VERSION-UV -->
|
|
38
|
+
uv tool install git+https://github.com/diversen/bump-py-version@v2.1.0
|
|
39
|
+
|
|
40
|
+
## Configuration
|
|
41
|
+
|
|
42
|
+
You may also bump the python version `__version__` in e.g. `__init__.py` file (or similar) using a `pyproject.toml` setting like the following:
|
|
43
|
+
|
|
44
|
+
```toml
|
|
45
|
+
[tool.bump_version]
|
|
46
|
+
version_file = "bump_py_version/__init__.py"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
You may configure the script to alter text files (e.g. `README.md`) by setting the section `tool.bump_version.replace_patterns` in the `pyproject.toml` file. Example:
|
|
50
|
+
|
|
51
|
+
```toml
|
|
52
|
+
[tool.bump_version.replace_patterns.pipx]
|
|
53
|
+
file = "README.md"
|
|
54
|
+
search = "<!-- LATEST-VERSION-UV -->"
|
|
55
|
+
replace = "\tuv tool install git+https://github.com/diversen/bump-py-version@{version}\n"
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The above will cause the line below the `search` string to be replaced with the `replace` string. Then it is easy to show the latest version of the package in a `README.md` file.
|
|
60
|
+
|
|
61
|
+
### Updating uv.lock
|
|
62
|
+
|
|
63
|
+
To update `uv.lock` as part of each version bump, enable:
|
|
64
|
+
|
|
65
|
+
```toml
|
|
66
|
+
[tool.bump_version]
|
|
67
|
+
uv_lock = true
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This runs `uv lock` after updating the version files and before staging changes,
|
|
71
|
+
so the lockfile is included in the release commit and tag. It does not request
|
|
72
|
+
dependency upgrades. If the lockfile does not exist, `uv lock` creates it.
|
|
73
|
+
|
|
74
|
+
`uv` must be installed and available on `PATH`. The command checks these
|
|
75
|
+
prerequisites before modifying files and rejects an ignored `uv.lock`, explaining
|
|
76
|
+
that it must be removed from `.gitignore` or the applicable Git ignore rules.
|
|
77
|
+
An existing, untracked lockfile must be committed before bumping, just like any
|
|
78
|
+
other non-ignored untracked file.
|
|
79
|
+
|
|
80
|
+
If `uv lock` fails, the command stops before committing, pushing, or tagging;
|
|
81
|
+
local edits remain available for inspection. The setting defaults to `false`,
|
|
82
|
+
which preserves the existing behavior without running `uv`.
|
|
83
|
+
|
|
84
|
+
## Usage example
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
bump-py-version v1.2.3
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
# License
|
|
93
|
+
|
|
94
|
+
MIT © [Dennis Iversen](https://github.com/diversen)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
bump_py_version/__init__.py,sha256=Xybt2skBZamGMNlLuOX1IG-h4uIxqUDGAO8MIGWrJac,22
|
|
2
|
+
bump_py_version/cli.py,sha256=olHbFQQvZGRppY2jjVci7JxLyqKY-N4etipRdeVHD1Q,6774
|
|
3
|
+
bump_py_version-2.1.0.dist-info/licenses/LICENSE,sha256=RhJa9n3MJadQpolKbuGOiundPNs_9pqw2rNV4GaONmw,1071
|
|
4
|
+
bump_py_version-2.1.0.dist-info/METADATA,sha256=4VAgfAo7tSoLYMUngdY8QFr377UR_NAESGGDeKVrHGk,3327
|
|
5
|
+
bump_py_version-2.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
6
|
+
bump_py_version-2.1.0.dist-info/entry_points.txt,sha256=GDJqphGw3wBeNi7wmy3lA9CAuw9VvAMLERK36CVS_D0,60
|
|
7
|
+
bump_py_version-2.1.0.dist-info/top_level.txt,sha256=T6wmloAit2RKV2sN--qTdH2dJPlv40t2Juea_1Q5GcU,16
|
|
8
|
+
bump_py_version-2.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Dennis Iversen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bump_py_version
|