tinysemver 2.1.1__tar.gz → 3.0.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.
- {tinysemver-2.1.1/tinysemver.egg-info → tinysemver-3.0.0}/PKG-INFO +48 -26
- {tinysemver-2.1.1 → tinysemver-3.0.0}/README.md +28 -17
- {tinysemver-2.1.1 → tinysemver-3.0.0}/pyproject.toml +23 -9
- tinysemver-3.0.0/tinysemver/__init__.py +75 -0
- {tinysemver-2.1.1 → tinysemver-3.0.0}/tinysemver/tinysemver.py +87 -224
- {tinysemver-2.1.1 → tinysemver-3.0.0/tinysemver.egg-info}/PKG-INFO +48 -26
- tinysemver-3.0.0/tinysemver.egg-info/requires.txt +14 -0
- tinysemver-2.1.1/tinysemver/__init__.py +0 -0
- tinysemver-2.1.1/tinysemver.egg-info/requires.txt +0 -2
- {tinysemver-2.1.1 → tinysemver-3.0.0}/LICENSE +0 -0
- {tinysemver-2.1.1 → tinysemver-3.0.0}/setup.cfg +0 -0
- {tinysemver-2.1.1 → tinysemver-3.0.0}/tinysemver.egg-info/SOURCES.txt +0 -0
- {tinysemver-2.1.1 → tinysemver-3.0.0}/tinysemver.egg-info/dependency_links.txt +0 -0
- {tinysemver-2.1.1 → tinysemver-3.0.0}/tinysemver.egg-info/entry_points.txt +0 -0
- {tinysemver-2.1.1 → tinysemver-3.0.0}/tinysemver.egg-info/top_level.txt +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: tinysemver
|
|
3
|
-
Version:
|
|
4
|
-
Summary: Semantic-Versioning
|
|
5
|
-
Author-email: Ash Vardanian <1983160+ashvardanian@users.noreply.github.com
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Semantic-Versioning... that doesn't need 300,000 lines of JS
|
|
5
|
+
Author-email: Ash Vardanian <1983160+ashvardanian@users.noreply.github.com>
|
|
6
6
|
License: Apache License
|
|
7
7
|
Version 2.0, January 2004
|
|
8
8
|
http://www.apache.org/licenses/
|
|
@@ -206,10 +206,10 @@ License: Apache License
|
|
|
206
206
|
limitations under the License.
|
|
207
207
|
|
|
208
208
|
Project-URL: Homepage, https://pypi.org/project/tinysemver/
|
|
209
|
-
Project-URL: Documentation, https://github.com/ashvardanian/
|
|
210
|
-
Project-URL: Repository, https://github.com/ashvardanian/
|
|
211
|
-
Project-URL: Issues, https://github.com/ashvardanian/
|
|
212
|
-
Project-URL: Changelog, https://github.com/ashvardanian/
|
|
209
|
+
Project-URL: Documentation, https://github.com/ashvardanian/TinySemVer/blob/main/README.md
|
|
210
|
+
Project-URL: Repository, https://github.com/ashvardanian/TinySemVer.git
|
|
211
|
+
Project-URL: Issues, https://github.com/ashvardanian/TinySemVer/issues
|
|
212
|
+
Project-URL: Changelog, https://github.com/ashvardanian/TinySemVer/blob/main/CHANGELOG.md
|
|
213
213
|
Classifier: Development Status :: 5 - Production/Stable
|
|
214
214
|
Classifier: Intended Audience :: Developers
|
|
215
215
|
Classifier: Topic :: Software Development :: Build Tools
|
|
@@ -223,12 +223,23 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
223
223
|
Classifier: Programming Language :: Python :: 3.11
|
|
224
224
|
Description-Content-Type: text/markdown
|
|
225
225
|
License-File: LICENSE
|
|
226
|
-
Requires-Dist: openai>=1.54.4
|
|
227
226
|
Requires-Dist: rich>=13.8.0
|
|
227
|
+
Provides-Extra: test
|
|
228
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
229
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
230
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "test"
|
|
231
|
+
Provides-Extra: dev
|
|
232
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
233
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
234
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
235
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
236
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
237
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
238
|
+
Dynamic: license-file
|
|
228
239
|
|
|
229
240
|

|
|
230
241
|
|
|
231
|
-
__TinySemVer__ is a minimalistic [Semantic Versioning](https://semver.org/) package for projects following [Conventional Commits](https://www.conventionalcommits.org/) in a single short Python file
|
|
242
|
+
__TinySemVer__ is a minimalistic [Semantic Versioning](https://semver.org/) package for projects following [Conventional Commits](https://www.conventionalcommits.org/) in a single short Python file.
|
|
232
243
|
In plain English, if your commit messages look like `feat: add new feature` or `fix: bugfix`, this package will automate releasing new "GIT tags" based on the commit messages.
|
|
233
244
|
Here is how to integrate it into your project CI:
|
|
234
245
|
|
|
@@ -306,19 +317,6 @@ Alternatively, you can just ask for `--help`:
|
|
|
306
317
|
$ tinysemver --help
|
|
307
318
|
```
|
|
308
319
|
|
|
309
|
-
## AI and Rock-n-Roll
|
|
310
|
-
|
|
311
|
-
TinySemVer can leverage a language model to validate the commits and generate clean and infromative release notes.
|
|
312
|
-
|
|
313
|
-
```sh
|
|
314
|
-
$ tinysemver --verbose \
|
|
315
|
-
--github-repository 'ashvardanian/tinysemver' \
|
|
316
|
-
--openai-base-url 'https://api.groq.com/openai/v1' \
|
|
317
|
-
--openai-api-key 'GET_YOURSELF_A_KEY' \
|
|
318
|
-
--openai-model 'llama-3.2-11b-text-preview' \
|
|
319
|
-
--dry-run
|
|
320
|
-
```
|
|
321
|
-
|
|
322
320
|
## Usage Details for the GitHub CI Action
|
|
323
321
|
|
|
324
322
|
TinySemVer can be easily integrated into your GitHub Actions CI pipeline.
|
|
@@ -337,12 +335,12 @@ jobs:
|
|
|
337
335
|
|
|
338
336
|
steps:
|
|
339
337
|
- name: Checkout
|
|
340
|
-
uses: actions/checkout@
|
|
338
|
+
uses: actions/checkout@v5
|
|
341
339
|
with:
|
|
342
340
|
persist-credentials: false # Only if main branch if protected
|
|
343
341
|
|
|
344
342
|
- name: Run TinySemVer
|
|
345
|
-
uses: ashvardanian/tinysemver@
|
|
343
|
+
uses: ashvardanian/tinysemver@v3 # Automatically receives non-breaking updates
|
|
346
344
|
with:
|
|
347
345
|
major-verbs: 'breaking,break,major'
|
|
348
346
|
minor-verbs: 'feature,minor,add,new'
|
|
@@ -363,7 +361,7 @@ jobs:
|
|
|
363
361
|
runs-on: ubuntu-latest
|
|
364
362
|
|
|
365
363
|
steps:
|
|
366
|
-
- uses: actions/checkout@
|
|
364
|
+
- uses: actions/checkout@v5
|
|
367
365
|
with:
|
|
368
366
|
ref: main # Take the most recent updated version
|
|
369
367
|
```
|
|
@@ -382,6 +380,30 @@ If you need to update the version in multiple files, pass a multiline string wit
|
|
|
382
380
|
|
|
383
381
|
For examples, consider checking StringZilla, USearch, and other libraries using TinySemVer.
|
|
384
382
|
|
|
383
|
+
Since version 3.0.0, TinySemVer can optionally maintain moving version tags similar to `actions/checkout@v4`:
|
|
384
|
+
|
|
385
|
+
```yaml
|
|
386
|
+
uses: ashvardanian/tinysemver@v3 # Automatically gets non-breaking updates
|
|
387
|
+
uses: ashvardanian/tinysemver@v3.0 # Automatically gets patch updates
|
|
388
|
+
uses: ashvardanian/tinysemver@v3.0.0 # Pinned, never changes
|
|
389
|
+
uses: ashvardanian/tinysemver@main # Bleeding edge
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
This feature is disabled by default because it requires force-push permissions.
|
|
393
|
+
To enable it, add `push-moving-tags: 'true'` and ensure your token has force-push access:
|
|
394
|
+
|
|
395
|
+
```yaml
|
|
396
|
+
- name: Run TinySemVer
|
|
397
|
+
uses: ashvardanian/tinysemver@v3
|
|
398
|
+
with:
|
|
399
|
+
push: 'true'
|
|
400
|
+
push-moving-tags: 'true'
|
|
401
|
+
github-token: ${{ secrets.TINYSEMVER_TOKEN }}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
When enabled, TinySemVer creates three tags: `v3` (major), `v3.0` (minor), and `v3.0.0` (specific).
|
|
405
|
+
The default `GITHUB_TOKEN` typically lacks force-push permissions for protected branches.
|
|
406
|
+
|
|
385
407
|
### Security Considerations
|
|
386
408
|
|
|
387
409
|
If your default branch is protected with a "pull request before merging" rule:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
-
__TinySemVer__ is a minimalistic [Semantic Versioning](https://semver.org/) package for projects following [Conventional Commits](https://www.conventionalcommits.org/) in a single short Python file
|
|
3
|
+
__TinySemVer__ is a minimalistic [Semantic Versioning](https://semver.org/) package for projects following [Conventional Commits](https://www.conventionalcommits.org/) in a single short Python file.
|
|
4
4
|
In plain English, if your commit messages look like `feat: add new feature` or `fix: bugfix`, this package will automate releasing new "GIT tags" based on the commit messages.
|
|
5
5
|
Here is how to integrate it into your project CI:
|
|
6
6
|
|
|
@@ -78,19 +78,6 @@ Alternatively, you can just ask for `--help`:
|
|
|
78
78
|
$ tinysemver --help
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
## AI and Rock-n-Roll
|
|
82
|
-
|
|
83
|
-
TinySemVer can leverage a language model to validate the commits and generate clean and infromative release notes.
|
|
84
|
-
|
|
85
|
-
```sh
|
|
86
|
-
$ tinysemver --verbose \
|
|
87
|
-
--github-repository 'ashvardanian/tinysemver' \
|
|
88
|
-
--openai-base-url 'https://api.groq.com/openai/v1' \
|
|
89
|
-
--openai-api-key 'GET_YOURSELF_A_KEY' \
|
|
90
|
-
--openai-model 'llama-3.2-11b-text-preview' \
|
|
91
|
-
--dry-run
|
|
92
|
-
```
|
|
93
|
-
|
|
94
81
|
## Usage Details for the GitHub CI Action
|
|
95
82
|
|
|
96
83
|
TinySemVer can be easily integrated into your GitHub Actions CI pipeline.
|
|
@@ -109,12 +96,12 @@ jobs:
|
|
|
109
96
|
|
|
110
97
|
steps:
|
|
111
98
|
- name: Checkout
|
|
112
|
-
uses: actions/checkout@
|
|
99
|
+
uses: actions/checkout@v5
|
|
113
100
|
with:
|
|
114
101
|
persist-credentials: false # Only if main branch if protected
|
|
115
102
|
|
|
116
103
|
- name: Run TinySemVer
|
|
117
|
-
uses: ashvardanian/tinysemver@
|
|
104
|
+
uses: ashvardanian/tinysemver@v3 # Automatically receives non-breaking updates
|
|
118
105
|
with:
|
|
119
106
|
major-verbs: 'breaking,break,major'
|
|
120
107
|
minor-verbs: 'feature,minor,add,new'
|
|
@@ -135,7 +122,7 @@ jobs:
|
|
|
135
122
|
runs-on: ubuntu-latest
|
|
136
123
|
|
|
137
124
|
steps:
|
|
138
|
-
- uses: actions/checkout@
|
|
125
|
+
- uses: actions/checkout@v5
|
|
139
126
|
with:
|
|
140
127
|
ref: main # Take the most recent updated version
|
|
141
128
|
```
|
|
@@ -154,6 +141,30 @@ If you need to update the version in multiple files, pass a multiline string wit
|
|
|
154
141
|
|
|
155
142
|
For examples, consider checking StringZilla, USearch, and other libraries using TinySemVer.
|
|
156
143
|
|
|
144
|
+
Since version 3.0.0, TinySemVer can optionally maintain moving version tags similar to `actions/checkout@v4`:
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
uses: ashvardanian/tinysemver@v3 # Automatically gets non-breaking updates
|
|
148
|
+
uses: ashvardanian/tinysemver@v3.0 # Automatically gets patch updates
|
|
149
|
+
uses: ashvardanian/tinysemver@v3.0.0 # Pinned, never changes
|
|
150
|
+
uses: ashvardanian/tinysemver@main # Bleeding edge
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
This feature is disabled by default because it requires force-push permissions.
|
|
154
|
+
To enable it, add `push-moving-tags: 'true'` and ensure your token has force-push access:
|
|
155
|
+
|
|
156
|
+
```yaml
|
|
157
|
+
- name: Run TinySemVer
|
|
158
|
+
uses: ashvardanian/tinysemver@v3
|
|
159
|
+
with:
|
|
160
|
+
push: 'true'
|
|
161
|
+
push-moving-tags: 'true'
|
|
162
|
+
github-token: ${{ secrets.TINYSEMVER_TOKEN }}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
When enabled, TinySemVer creates three tags: `v3` (major), `v3.0` (minor), and `v3.0.0` (specific).
|
|
166
|
+
The default `GITHUB_TOKEN` typically lacks force-push permissions for protected branches.
|
|
167
|
+
|
|
157
168
|
### Security Considerations
|
|
158
169
|
|
|
159
170
|
If your default branch is protected with a "pull request before merging" rule:
|
|
@@ -4,13 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tinysemver"
|
|
7
|
-
version = "
|
|
8
|
-
description = "Semantic-Versioning
|
|
7
|
+
version = "3.0.0"
|
|
8
|
+
description = "Semantic-Versioning... that doesn't need 300,000 lines of JS"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
11
11
|
authors = [
|
|
12
|
-
{ name = "Ash Vardanian", email = "1983160+ashvardanian@users.noreply.github.com" }
|
|
13
|
-
{ name = "Guillaume de Rouville", email = "31691250+grouville@users.noreply.github.com" },
|
|
12
|
+
{ name = "Ash Vardanian", email = "1983160+ashvardanian@users.noreply.github.com" }
|
|
14
13
|
]
|
|
15
14
|
classifiers = [
|
|
16
15
|
"Development Status :: 5 - Production/Stable",
|
|
@@ -25,17 +24,32 @@ classifiers = [
|
|
|
25
24
|
"Programming Language :: Python :: 3.10",
|
|
26
25
|
"Programming Language :: Python :: 3.11",
|
|
27
26
|
]
|
|
28
|
-
dependencies = ["
|
|
27
|
+
dependencies = ["rich >= 13.8.0"]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
test = [
|
|
31
|
+
"pytest >= 7.0.0",
|
|
32
|
+
"pytest-cov >= 4.0.0",
|
|
33
|
+
"pytest-mock >= 3.10.0",
|
|
34
|
+
]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest >= 7.0.0",
|
|
37
|
+
"pytest-cov >= 4.0.0",
|
|
38
|
+
"pytest-mock >= 3.10.0",
|
|
39
|
+
"ruff >= 0.1.0",
|
|
40
|
+
"mypy >= 1.0.0",
|
|
41
|
+
"black >= 23.0.0",
|
|
42
|
+
]
|
|
29
43
|
|
|
30
44
|
[project.scripts]
|
|
31
45
|
tinysemver = "tinysemver.tinysemver:main"
|
|
32
46
|
|
|
33
47
|
[project.urls]
|
|
34
48
|
Homepage = "https://pypi.org/project/tinysemver/"
|
|
35
|
-
Documentation = "https://github.com/ashvardanian/
|
|
36
|
-
Repository = "https://github.com/ashvardanian/
|
|
37
|
-
Issues = "https://github.com/ashvardanian/
|
|
38
|
-
Changelog = "https://github.com/ashvardanian/
|
|
49
|
+
Documentation = "https://github.com/ashvardanian/TinySemVer/blob/main/README.md"
|
|
50
|
+
Repository = "https://github.com/ashvardanian/TinySemVer.git"
|
|
51
|
+
Issues = "https://github.com/ashvardanian/TinySemVer/issues"
|
|
52
|
+
Changelog = "https://github.com/ashvardanian/TinySemVer/blob/main/CHANGELOG.md"
|
|
39
53
|
|
|
40
54
|
[tool.setuptools]
|
|
41
55
|
package-dir = { "" = "." }
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""TinySemVer - A tiny but mighty Semantic Versioning tool.
|
|
2
|
+
|
|
3
|
+
This package provides automated semantic versioning for Git repositories
|
|
4
|
+
following Conventional Commits. It can generate changelogs and release notes
|
|
5
|
+
with or without LLM assistance.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from tinysemver import bump
|
|
9
|
+
>>> new_version = bump(
|
|
10
|
+
... path=".",
|
|
11
|
+
... dry_run=True,
|
|
12
|
+
... verbose=True,
|
|
13
|
+
... version_file="VERSION",
|
|
14
|
+
... changelog_file="CHANGELOG.md"
|
|
15
|
+
... )
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from tinysemver.tinysemver import (
|
|
19
|
+
# Main function
|
|
20
|
+
bump,
|
|
21
|
+
# Version utilities
|
|
22
|
+
parse_version,
|
|
23
|
+
bump_version,
|
|
24
|
+
# Commit utilities
|
|
25
|
+
commit_starts_with_verb,
|
|
26
|
+
normalize_verbs,
|
|
27
|
+
group_commits,
|
|
28
|
+
convert_commits_to_message,
|
|
29
|
+
# Git utilities
|
|
30
|
+
get_last_tag,
|
|
31
|
+
get_commits_since_tag,
|
|
32
|
+
get_diff_for_commit,
|
|
33
|
+
# File utilities
|
|
34
|
+
patch_with_regex,
|
|
35
|
+
# Tag creation
|
|
36
|
+
create_tag,
|
|
37
|
+
# Types
|
|
38
|
+
SemVer,
|
|
39
|
+
BumpType,
|
|
40
|
+
PathLike,
|
|
41
|
+
Commit,
|
|
42
|
+
ChangeDiff,
|
|
43
|
+
# Exceptions
|
|
44
|
+
NoNewCommitsError,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
__version__ = "2.1.1"
|
|
48
|
+
__all__ = [
|
|
49
|
+
# Main function
|
|
50
|
+
"bump",
|
|
51
|
+
# Version utilities
|
|
52
|
+
"parse_version",
|
|
53
|
+
"bump_version",
|
|
54
|
+
# Commit utilities
|
|
55
|
+
"commit_starts_with_verb",
|
|
56
|
+
"normalize_verbs",
|
|
57
|
+
"group_commits",
|
|
58
|
+
"convert_commits_to_message",
|
|
59
|
+
# Git utilities
|
|
60
|
+
"get_last_tag",
|
|
61
|
+
"get_commits_since_tag",
|
|
62
|
+
"get_diff_for_commit",
|
|
63
|
+
# File utilities
|
|
64
|
+
"patch_with_regex",
|
|
65
|
+
# Tag creation
|
|
66
|
+
"create_tag",
|
|
67
|
+
# Types
|
|
68
|
+
"SemVer",
|
|
69
|
+
"BumpType",
|
|
70
|
+
"PathLike",
|
|
71
|
+
"Commit",
|
|
72
|
+
"ChangeDiff",
|
|
73
|
+
# Exceptions
|
|
74
|
+
"NoNewCommitsError",
|
|
75
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
-
"""TinySemVer is a tiny
|
|
3
|
+
"""TinySemVer is a tiny minimalistic Semantic Versioning tool.
|
|
4
4
|
|
|
5
5
|
This module traces the commit history of a Git repository after the last tag, and based
|
|
6
6
|
on the commit messages, it determines the type of version bump (major, minor, or patch).
|
|
@@ -67,39 +67,10 @@ class NoNewCommitsError(Exception):
|
|
|
67
67
|
pass
|
|
68
68
|
|
|
69
69
|
|
|
70
|
-
class MayContainVulnerability(Warning):
|
|
71
|
-
"""Raised when the commit may contain a vulnerability."""
|
|
72
|
-
|
|
73
|
-
pass
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class MayContainLogicalBugs(Warning):
|
|
77
|
-
"""Raised when the commit may contain logical bugs."""
|
|
78
|
-
|
|
79
|
-
pass
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class MayContainBreakingChange(Warning):
|
|
83
|
-
"""Raised when the commit may contain a breaking change."""
|
|
84
|
-
|
|
85
|
-
pass
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
class MayLackDocumentation(Warning):
|
|
89
|
-
"""Raised when the commit may lack documentation updates."""
|
|
90
|
-
|
|
91
|
-
pass
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
class UnknownCommitWarning(Warning):
|
|
95
|
-
"""Something in the commit doesn't add up."""
|
|
96
|
-
|
|
97
|
-
pass
|
|
98
70
|
|
|
99
71
|
|
|
100
72
|
_console = None # Initialize global console variable
|
|
101
73
|
_console_is_rich = True # Initialize global console type variable
|
|
102
|
-
_openai_client = None # Initialize global OpenAI client variable
|
|
103
74
|
|
|
104
75
|
|
|
105
76
|
def print_to_console(message: str) -> None:
|
|
@@ -120,16 +91,6 @@ def print_to_console(message: str) -> None:
|
|
|
120
91
|
print(message)
|
|
121
92
|
|
|
122
93
|
|
|
123
|
-
def get_open_ai_client(base_url: str, api_key: str):
|
|
124
|
-
# Create a global variable for the client
|
|
125
|
-
global _openai_client
|
|
126
|
-
if not _openai_client:
|
|
127
|
-
from openai import OpenAI
|
|
128
|
-
|
|
129
|
-
_openai_client = OpenAI(base_url=base_url, api_key=api_key)
|
|
130
|
-
return _openai_client
|
|
131
|
-
|
|
132
|
-
|
|
133
94
|
def get_last_tag(repository_path: PathLike) -> str:
|
|
134
95
|
"""Retrieve the last Git tag name from the repository."""
|
|
135
96
|
result = subprocess.run(
|
|
@@ -262,6 +223,7 @@ def create_tag(
|
|
|
262
223
|
github_repository: Optional[str] = None,
|
|
263
224
|
push: bool = False,
|
|
264
225
|
create_release: bool = False,
|
|
226
|
+
push_moving_tags: bool = False,
|
|
265
227
|
major_commits: List[Commit] = None,
|
|
266
228
|
minor_commits: List[Commit] = None,
|
|
267
229
|
patch_commits: List[Commit] = None,
|
|
@@ -274,7 +236,8 @@ def create_tag(
|
|
|
274
236
|
env["GIT_COMMITTER_EMAIL"] = user_email
|
|
275
237
|
env["GIT_AUTHOR_NAME"] = user_name
|
|
276
238
|
env["GIT_AUTHOR_EMAIL"] = user_email
|
|
277
|
-
|
|
239
|
+
if github_token:
|
|
240
|
+
env["GITHUB_TOKEN"] = github_token
|
|
278
241
|
|
|
279
242
|
message = f"Release: {tag} [skip ci]"
|
|
280
243
|
message += convert_commits_to_message(major_commits or [], minor_commits or [], patch_commits or [])
|
|
@@ -293,6 +256,29 @@ def create_tag(
|
|
|
293
256
|
|
|
294
257
|
subprocess.run(["git", "tag", "-a", tag, "-m", message, new_commit_sha], cwd=repository_path, env=env)
|
|
295
258
|
print_to_console(f"[bold green]Created new tag:[/bold green] {tag}")
|
|
259
|
+
|
|
260
|
+
# Optionally create moving major and minor version tags for GitHub Actions compatibility
|
|
261
|
+
# This allows users to reference @v2 or @v2.1 instead of @v2.1.1
|
|
262
|
+
# Note: This requires force-push permissions
|
|
263
|
+
if push_moving_tags:
|
|
264
|
+
major_tag = f"v{version[0]}"
|
|
265
|
+
minor_tag = f"v{version[0]}.{version[1]}"
|
|
266
|
+
|
|
267
|
+
# Force-update major version tag (e.g., v2 -> v2.1.1)
|
|
268
|
+
subprocess.run(
|
|
269
|
+
["git", "tag", "-fa", major_tag, "-m", f"Update {major_tag} to {tag}", new_commit_sha],
|
|
270
|
+
cwd=repository_path,
|
|
271
|
+
env=env,
|
|
272
|
+
)
|
|
273
|
+
print_to_console(f"[bold green]Updated major version tag:[/bold green] {major_tag} -> {tag}")
|
|
274
|
+
|
|
275
|
+
# Force-update minor version tag (e.g., v2.1 -> v2.1.1)
|
|
276
|
+
subprocess.run(
|
|
277
|
+
["git", "tag", "-fa", minor_tag, "-m", f"Update {minor_tag} to {tag}", new_commit_sha],
|
|
278
|
+
cwd=repository_path,
|
|
279
|
+
env=env,
|
|
280
|
+
)
|
|
281
|
+
print_to_console(f"[bold green]Updated minor version tag:[/bold green] {minor_tag} -> {tag}")
|
|
296
282
|
if push:
|
|
297
283
|
url = None
|
|
298
284
|
if github_token and github_repository:
|
|
@@ -315,12 +301,41 @@ def create_tag(
|
|
|
315
301
|
f"Failed to push the new commits to the remote repository: '{url}' with error: {push_result.stderr.decode('utf-8')}"
|
|
316
302
|
)
|
|
317
303
|
|
|
318
|
-
|
|
304
|
+
# Push the specific version tag (e.g., v2.1.1)
|
|
305
|
+
push_result = subprocess.run(["git", "push", url, tag], cwd=repository_path, capture_output=True, env=env)
|
|
319
306
|
if push_result.returncode != 0:
|
|
320
307
|
raise RuntimeError(
|
|
321
|
-
f"Failed to push the
|
|
308
|
+
f"Failed to push the tag '{tag}' to the remote repository: '{url}' with error: {push_result.stderr.decode('utf-8')}"
|
|
322
309
|
)
|
|
323
|
-
|
|
310
|
+
|
|
311
|
+
print_to_console(f"[bold green]Pushed tag:[/bold green] {tag}")
|
|
312
|
+
|
|
313
|
+
# Optionally force-push the moving major and minor version tags
|
|
314
|
+
if push_moving_tags:
|
|
315
|
+
major_tag = f"v{version[0]}"
|
|
316
|
+
minor_tag = f"v{version[0]}.{version[1]}"
|
|
317
|
+
|
|
318
|
+
# Force-push the major version tag (e.g., v2)
|
|
319
|
+
push_result = subprocess.run(
|
|
320
|
+
["git", "push", url, major_tag, "--force"], cwd=repository_path, capture_output=True, env=env
|
|
321
|
+
)
|
|
322
|
+
if push_result.returncode != 0:
|
|
323
|
+
raise RuntimeError(
|
|
324
|
+
f"Failed to push major tag '{major_tag}' to the remote repository: '{url}' with error: {push_result.stderr.decode('utf-8')}"
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# Force-push the minor version tag (e.g., v2.1)
|
|
328
|
+
push_result = subprocess.run(
|
|
329
|
+
["git", "push", url, minor_tag, "--force"], cwd=repository_path, capture_output=True, env=env
|
|
330
|
+
)
|
|
331
|
+
if push_result.returncode != 0:
|
|
332
|
+
raise RuntimeError(
|
|
333
|
+
f"Failed to push minor tag '{minor_tag}' to the remote repository: '{url}' with error: {push_result.stderr.decode('utf-8')}"
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
print_to_console(f"[bold green]Pushed moving tags:[/bold green]")
|
|
337
|
+
print_to_console(f"[bold green] - Major:[/bold green] {major_tag}")
|
|
338
|
+
print_to_console(f"[bold green] - Minor:[/bold green] {minor_tag}")
|
|
324
339
|
|
|
325
340
|
# Create a release using GitHub CLI if available
|
|
326
341
|
if create_release and github_repository:
|
|
@@ -370,6 +385,7 @@ def patch_with_regex(
|
|
|
370
385
|
) -> None:
|
|
371
386
|
"""Update a file by replacing the first matched group of every RegEx match with a new version."""
|
|
372
387
|
|
|
388
|
+
assert os.path.exists(file_path), f"File missing: {file_path}"
|
|
373
389
|
with open(file_path, "r") as file:
|
|
374
390
|
old_content = file.read()
|
|
375
391
|
|
|
@@ -388,7 +404,7 @@ def patch_with_regex(
|
|
|
388
404
|
# Compile the regex pattern with multiline support
|
|
389
405
|
regex_pattern = re.compile(regex_pattern, re.MULTILINE)
|
|
390
406
|
matches = list(re.finditer(regex_pattern, old_content))
|
|
391
|
-
new_content = re.sub(regex_pattern, replace_first_group, old_content, 1)
|
|
407
|
+
new_content = re.sub(regex_pattern, replace_first_group, old_content, count=1)
|
|
392
408
|
|
|
393
409
|
non_empty_matches = [m for m in matches if len(m.group(0).strip())]
|
|
394
410
|
assert len(non_empty_matches) > 0, f"No matches found in: {file_path}"
|
|
@@ -396,7 +412,7 @@ def patch_with_regex(
|
|
|
396
412
|
for match in non_empty_matches:
|
|
397
413
|
match_line = old_content.count("\n", 0, match.pos) + 1
|
|
398
414
|
old_slice = match.group(0)
|
|
399
|
-
new_slice = re.sub(regex_pattern, replace_first_group, old_slice, 1)
|
|
415
|
+
new_slice = re.sub(regex_pattern, replace_first_group, old_slice, count=1)
|
|
400
416
|
|
|
401
417
|
if verbose:
|
|
402
418
|
print_to_console(f"[bold cyan]Will update file:[/bold cyan] {file_path}:{match_line}")
|
|
@@ -410,119 +426,12 @@ def patch_with_regex(
|
|
|
410
426
|
print_to_console(f"[bold green]File updated successfully:[/bold green] {file_path}")
|
|
411
427
|
|
|
412
428
|
|
|
413
|
-
def validate_commit_with_llms(
|
|
414
|
-
base_url: str,
|
|
415
|
-
api_key: str,
|
|
416
|
-
model: Optional[str],
|
|
417
|
-
commit: Commit,
|
|
418
|
-
change: ChangeDiff,
|
|
419
|
-
) -> Optional[Warning]:
|
|
420
|
-
prompt = f"""
|
|
421
|
-
You are a professional coding assistant helping me to validate a commit message and its changes.
|
|
422
|
-
|
|
423
|
-
1. Check if the changed code is likely to introduce logical bugs.
|
|
424
|
-
2. Check if the commit message is likely to contain a vulnerability.
|
|
425
|
-
3. Check if it contains a breaking change that is likely to affect users.
|
|
426
|
-
4. Check if the documentation wasn't updated for the changes made.
|
|
427
|
-
|
|
428
|
-
Don't let the commit message fool you, just look at the changes made in the code.
|
|
429
|
-
Reply by choosing any number from 0 to 4 and describe the issue, if anything is found.
|
|
430
|
-
|
|
431
|
-
0. No issues found.
|
|
432
|
-
1. The commit may introduce logical bugs, such as the overflow in ...
|
|
433
|
-
2. The commit may contain a vulnerability, such as the SQL injection in ...
|
|
434
|
-
3. The commit may contain a breaking change, such as the removal of ...
|
|
435
|
-
4. The commit may lack documentation updates for the changes made in function ...
|
|
436
|
-
"""
|
|
437
|
-
message = f"""
|
|
438
|
-
#{commit.hash}: {commit.message}
|
|
439
|
-
|
|
440
|
-
Changes:
|
|
441
|
-
|
|
442
|
-
{change}
|
|
443
|
-
"""
|
|
444
|
-
client = get_open_ai_client(base_url=base_url, api_key=api_key)
|
|
445
|
-
response = client.chat.completions.create(
|
|
446
|
-
messages=[
|
|
447
|
-
{"role": "system", "content": prompt},
|
|
448
|
-
{"role": "user", "content": message},
|
|
449
|
-
],
|
|
450
|
-
max_tokens=256,
|
|
451
|
-
model=model,
|
|
452
|
-
stream=False,
|
|
453
|
-
)
|
|
454
|
-
response_text = response.choices[0].message.content
|
|
455
|
-
if response_text.startswith("0."):
|
|
456
|
-
return None
|
|
457
|
-
if response_text.startswith("1."):
|
|
458
|
-
return MayContainLogicalBugs(response_text)
|
|
459
|
-
if response_text.startswith("2."):
|
|
460
|
-
return MayContainVulnerability(response_text)
|
|
461
|
-
if response_text.startswith("3."):
|
|
462
|
-
return MayContainBreakingChange(response_text)
|
|
463
|
-
if response_text.startswith("4."):
|
|
464
|
-
return MayLackDocumentation(response_text)
|
|
465
|
-
return UnknownCommitWarning(response_text)
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
def aggregate_release_notes_with_llms(
|
|
469
|
-
base_url: str,
|
|
470
|
-
api_key: str,
|
|
471
|
-
model: Optional[str],
|
|
472
|
-
github_repository: str,
|
|
473
|
-
commits: List[Commit],
|
|
474
|
-
changes: List[ChangeDiff],
|
|
475
|
-
) -> str:
|
|
476
|
-
|
|
477
|
-
commits_with_hashes = "\n".join(f"- {commit.hash}: {commit.message}" for commit in commits)
|
|
478
|
-
prompt = f"""
|
|
479
|
-
You are a release notes generator for an advanced software project.
|
|
480
|
-
|
|
481
|
-
Aggregate the release notes for the upcoming version based on the commits and their
|
|
482
|
-
changes replying in a GitHub-flavored Markdown format.
|
|
483
|
-
|
|
484
|
-
- Mention the new features, improvements, and bug fixes.
|
|
485
|
-
- Warn about potential breaking changes and vulnerabilities.
|
|
486
|
-
- Tag people and teams responsible for the changes.
|
|
487
|
-
- For the most important commits provide a link to the issue or pull request and
|
|
488
|
-
a URL to that commit, like [commit](https://github.com/{github_repository}/commit/...).
|
|
489
|
-
- Don't guess anything, only use the information from the commits and their changes.
|
|
490
|
-
- Use inline math notation, like $\\beta=1$ or math blocks like the following, when needed:
|
|
491
|
-
|
|
492
|
-
```math
|
|
493
|
-
\\S_i(A, B, \\alpha, \\beta) = \\alpha \\cdot A_i + \\beta \\cdot B_i
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
- Use alerting quotes, like: [!CAUTION] or [!TIP], when needed.
|
|
497
|
-
|
|
498
|
-
Most importantly, be concise and informative.
|
|
499
|
-
"""
|
|
500
|
-
header_message = f"""Commits:
|
|
501
|
-
|
|
502
|
-
{commits_with_hashes}
|
|
503
|
-
"""
|
|
504
|
-
changes_messages = [f"#{commit.hash}: {commit.message}\n{change}" for commit, change in zip(commits, changes)]
|
|
505
|
-
|
|
506
|
-
client = get_open_ai_client(base_url=base_url, api_key=api_key)
|
|
507
|
-
response = client.chat.completions.create(
|
|
508
|
-
messages=[
|
|
509
|
-
{"role": "system", "content": prompt},
|
|
510
|
-
{"role": "user", "content": header_message},
|
|
511
|
-
*[{"role": "user", "content": message} for message in changes_messages],
|
|
512
|
-
],
|
|
513
|
-
max_tokens=2048,
|
|
514
|
-
model=model,
|
|
515
|
-
stream=False,
|
|
516
|
-
)
|
|
517
|
-
return response.choices[0].message.content
|
|
518
|
-
|
|
519
|
-
|
|
520
429
|
def bump(
|
|
521
430
|
*, # enforce keyword-only arguments
|
|
522
431
|
dry_run: bool = False,
|
|
523
432
|
verbose: bool = False,
|
|
524
433
|
major_verbs: List[str] = ["major", "breaking", "break"],
|
|
525
|
-
minor_verbs: List[str] = ["minor", "feature", "add", "new"],
|
|
434
|
+
minor_verbs: List[str] = ["minor", "feature", "feat", "add", "new"],
|
|
526
435
|
patch_verbs: List[str] = ["patch", "fix", "bug", "improve", "docs", "make"],
|
|
527
436
|
path: Optional[PathLike] = None, # takes current directory as default
|
|
528
437
|
changelog_file: Optional[PathLike] = None, # relative or absolute path to the changelog file
|
|
@@ -536,11 +445,9 @@ def bump(
|
|
|
536
445
|
push: bool = True,
|
|
537
446
|
github_token: Optional[str] = None,
|
|
538
447
|
github_repository: Optional[str] = None,
|
|
539
|
-
openai_base_url: str = "https://api.openai.com",
|
|
540
|
-
openai_api_key: Optional[str] = None,
|
|
541
|
-
openai_model: Optional[str] = None,
|
|
542
448
|
default_branch: str = "main",
|
|
543
449
|
create_release: bool = False,
|
|
450
|
+
push_moving_tags: bool = False,
|
|
544
451
|
) -> SemVer:
|
|
545
452
|
"""Primary function used to update the files (version, changelog, etc.), create a new Git tag, push it to a GitHub repository.
|
|
546
453
|
It can be used for dry-runs to preview the changes without actually creating a new tag.
|
|
@@ -564,9 +471,6 @@ def bump(
|
|
|
564
471
|
push (bool): If True, pushes the changes to the remote GitHub repository. Defaults to True.
|
|
565
472
|
github_token (Optional[str]): The GitHub token for authentication. If None, it attempts to use the GH_TOKEN environment variable. Defaults to None.
|
|
566
473
|
github_repository (Optional[str]): The GitHub repository in 'owner/repo' format. If None, it attempts to use the GH_REPOSITORY environment variable. Defaults to None.
|
|
567
|
-
openai_base_url (str): The OpenAI API base URL. Defaults to "https://api.openai.com".
|
|
568
|
-
openai_api_key (Optional[str]): The OpenAI API key. Defaults to None.
|
|
569
|
-
openai_model (Optional[str]): The OpenAI model to use for text generation. Defaults to None.
|
|
570
474
|
default_branch (str): The default branch to push the changes to. Defaults to "main".
|
|
571
475
|
|
|
572
476
|
Returns:
|
|
@@ -576,12 +480,16 @@ def bump(
|
|
|
576
480
|
repository_path = os.path.abspath(path) if path else os.getcwd()
|
|
577
481
|
assert os.path.isdir(os.path.join(repository_path, ".git")), f"Not a Git repository: {repository_path}"
|
|
578
482
|
|
|
579
|
-
def normalize_path(file_path
|
|
580
|
-
if not file_path
|
|
483
|
+
def normalize_path(file_path) -> str:
|
|
484
|
+
if not file_path:
|
|
581
485
|
return None
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
486
|
+
# Convert `pathlib.Path` to `str`
|
|
487
|
+
file_path_str = str(file_path) if hasattr(file_path, '__fspath__') or isinstance(file_path, os.PathLike) else file_path
|
|
488
|
+
if not file_path_str or len(file_path_str) == 0:
|
|
489
|
+
return None
|
|
490
|
+
if os.path.isabs(file_path_str):
|
|
491
|
+
return file_path_str
|
|
492
|
+
return os.path.join(repository_path, file_path_str)
|
|
585
493
|
|
|
586
494
|
changelog_file = normalize_path(changelog_file)
|
|
587
495
|
version_file = normalize_path(version_file)
|
|
@@ -615,7 +523,7 @@ def bump(
|
|
|
615
523
|
), f"Changelog file is missing or isn't a regular file: {changelog_file}"
|
|
616
524
|
|
|
617
525
|
major_verbs = normalize_verbs(major_verbs, ["major", "breaking", "break"])
|
|
618
|
-
minor_verbs = normalize_verbs(minor_verbs, ["minor", "feature", "add", "new"])
|
|
526
|
+
minor_verbs = normalize_verbs(minor_verbs, ["minor", "feature", "feat", "add", "new"])
|
|
619
527
|
patch_verbs = normalize_verbs(patch_verbs, ["patch", "fix", "bug", "improve", "docs", "make"])
|
|
620
528
|
|
|
621
529
|
last_tag = get_last_tag(repository_path)
|
|
@@ -682,43 +590,6 @@ def bump(
|
|
|
682
590
|
for file_path, regex_pattern in update_patch_version_in:
|
|
683
591
|
patch_with_regex(file_path, regex_pattern, str(new_version[2]), dry_run=dry_run, verbose=verbose)
|
|
684
592
|
|
|
685
|
-
# Now log the potential issues with the commits
|
|
686
|
-
if openai_api_key:
|
|
687
|
-
warnings_commits = []
|
|
688
|
-
warnings = []
|
|
689
|
-
changes = [get_diff_for_commit(repository_path, commit.hash) for commit in commits]
|
|
690
|
-
for commit, change in zip(commits, changes):
|
|
691
|
-
try:
|
|
692
|
-
warning = validate_commit_with_llms(
|
|
693
|
-
base_url=openai_base_url,
|
|
694
|
-
api_key=openai_api_key,
|
|
695
|
-
model=openai_model,
|
|
696
|
-
commit=commit,
|
|
697
|
-
change=change,
|
|
698
|
-
)
|
|
699
|
-
if warning:
|
|
700
|
-
warnings_commits.append(commit)
|
|
701
|
-
warnings.append(warning)
|
|
702
|
-
except Exception as e:
|
|
703
|
-
print_to_console(f"Failed to validate commit: {commit.hash} with error: {str(e)}")
|
|
704
|
-
traceback.print_exc()
|
|
705
|
-
|
|
706
|
-
if len(warnings):
|
|
707
|
-
print_to_console("## Potential issues")
|
|
708
|
-
for commit, warning in zip(warnings_commits, warnings):
|
|
709
|
-
print_to_console(f"- Commit #{commit.hash}: {warning}")
|
|
710
|
-
|
|
711
|
-
release_notes = aggregate_release_notes_with_llms(
|
|
712
|
-
base_url=openai_base_url,
|
|
713
|
-
api_key=openai_api_key,
|
|
714
|
-
model=openai_model,
|
|
715
|
-
github_repository=github_repository,
|
|
716
|
-
commits=commits,
|
|
717
|
-
changes=changes,
|
|
718
|
-
)
|
|
719
|
-
print_to_console("## Generated release notes:")
|
|
720
|
-
print_to_console(release_notes)
|
|
721
|
-
|
|
722
593
|
if not dry_run:
|
|
723
594
|
create_tag(
|
|
724
595
|
repository_path=repository_path,
|
|
@@ -730,11 +601,14 @@ def bump(
|
|
|
730
601
|
github_repository=github_repository,
|
|
731
602
|
push=push,
|
|
732
603
|
create_release=create_release,
|
|
604
|
+
push_moving_tags=push_moving_tags,
|
|
733
605
|
major_commits=major_commits,
|
|
734
606
|
minor_commits=minor_commits,
|
|
735
607
|
patch_commits=patch_commits,
|
|
736
608
|
)
|
|
737
609
|
|
|
610
|
+
return new_version
|
|
611
|
+
|
|
738
612
|
|
|
739
613
|
def main():
|
|
740
614
|
"""
|
|
@@ -836,25 +710,18 @@ def main():
|
|
|
836
710
|
"--github-repository",
|
|
837
711
|
help="GitHub repository in the 'owner/repo' format, if not set will use GH_REPOSITORY env var",
|
|
838
712
|
)
|
|
839
|
-
parser.add_argument(
|
|
840
|
-
"--openai-base-url",
|
|
841
|
-
default="https://api.openai.com",
|
|
842
|
-
help="OpenAI API base URL",
|
|
843
|
-
)
|
|
844
|
-
parser.add_argument(
|
|
845
|
-
"--openai-api-key",
|
|
846
|
-
help="OpenAI API key for text generation",
|
|
847
|
-
)
|
|
848
|
-
parser.add_argument(
|
|
849
|
-
"--openai-model",
|
|
850
|
-
help="OpenAI model to use for text generation (e.g., 'text-davinci-003' or 'text-davinci-002')",
|
|
851
|
-
)
|
|
852
713
|
parser.add_argument(
|
|
853
714
|
"--create-release",
|
|
854
715
|
action="store_true",
|
|
855
716
|
default=False,
|
|
856
717
|
help="Create a GitHub release using the GitHub CLI",
|
|
857
718
|
)
|
|
719
|
+
parser.add_argument(
|
|
720
|
+
"--push-moving-tags",
|
|
721
|
+
action="store_true",
|
|
722
|
+
default=False,
|
|
723
|
+
help="Push moving major and minor version tags (requires force-push permissions)",
|
|
724
|
+
)
|
|
858
725
|
args = parser.parse_args()
|
|
859
726
|
else:
|
|
860
727
|
|
|
@@ -866,7 +733,7 @@ def main():
|
|
|
866
733
|
args.verbose = os.environ.get("TINYSEMVER_VERBOSE", "").lower() == "true"
|
|
867
734
|
args.push = os.environ.get("TINYSEMVER_PUSH", "").lower() == "true"
|
|
868
735
|
args.major_verbs = os.environ.get("TINYSEMVER_MAJOR_VERBS") or "breaking,break,major"
|
|
869
|
-
args.minor_verbs = os.environ.get("TINYSEMVER_MINOR_VERBS") or "feature,minor,add,new"
|
|
736
|
+
args.minor_verbs = os.environ.get("TINYSEMVER_MINOR_VERBS") or "feature,feat,minor,add,new"
|
|
870
737
|
args.patch_verbs = os.environ.get("TINYSEMVER_PATCH_VERBS") or "fix,patch,bug,improve,docs,make"
|
|
871
738
|
args.default_branch = os.environ.get("TINYSEMVER_DEFAULT_BRANCH") or "main"
|
|
872
739
|
args.changelog_file = os.environ.get("TINYSEMVER_CHANGELOG_FILE")
|
|
@@ -896,10 +763,8 @@ def main():
|
|
|
896
763
|
args.git_user_email = os.environ.get("TINYSEMVER_GIT_USER_EMAIL", "tinysemver@ashvardanian.com")
|
|
897
764
|
args.github_token = os.environ.get("GITHUB_TOKEN")
|
|
898
765
|
args.github_repository = os.environ.get("GITHUB_REPOSITORY")
|
|
899
|
-
args.openai_base_url = os.environ.get("OPENAI_BASE_URL", "https://api.openai.com")
|
|
900
|
-
args.openai_api_key = os.environ.get("OPENAI_API_KEY")
|
|
901
|
-
args.openai_model = os.environ.get("OPENAI_MODEL")
|
|
902
766
|
args.create_release = os.environ.get("TINYSEMVER_CREATE_RELEASE", "").lower() == "true"
|
|
767
|
+
args.push_moving_tags = os.environ.get("TINYSEMVER_PUSH_MOVING_TAGS", "").lower() == "true"
|
|
903
768
|
|
|
904
769
|
# It's common for a CI pipeline to have multiple broken settings or missing files.
|
|
905
770
|
# For the best user experience, we want to catch all errors and print them at once,
|
|
@@ -927,11 +792,9 @@ def main():
|
|
|
927
792
|
git_user_email=args.git_user_email,
|
|
928
793
|
github_token=args.github_token,
|
|
929
794
|
github_repository=args.github_repository,
|
|
930
|
-
openai_base_url=args.openai_base_url,
|
|
931
|
-
openai_api_key=args.openai_api_key,
|
|
932
|
-
openai_model=args.openai_model,
|
|
933
795
|
push=args.push,
|
|
934
796
|
create_release=args.create_release,
|
|
797
|
+
push_moving_tags=args.push_moving_tags,
|
|
935
798
|
)
|
|
936
799
|
except NoNewCommitsError as e:
|
|
937
800
|
print(f"! {e}")
|
|
@@ -940,7 +803,7 @@ def main():
|
|
|
940
803
|
print(f"! {e}")
|
|
941
804
|
exit(1)
|
|
942
805
|
except Exception as e:
|
|
943
|
-
traceback.print_exc(
|
|
806
|
+
traceback.print_exc()
|
|
944
807
|
exit(1)
|
|
945
808
|
|
|
946
809
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: tinysemver
|
|
3
|
-
Version:
|
|
4
|
-
Summary: Semantic-Versioning
|
|
5
|
-
Author-email: Ash Vardanian <1983160+ashvardanian@users.noreply.github.com
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Semantic-Versioning... that doesn't need 300,000 lines of JS
|
|
5
|
+
Author-email: Ash Vardanian <1983160+ashvardanian@users.noreply.github.com>
|
|
6
6
|
License: Apache License
|
|
7
7
|
Version 2.0, January 2004
|
|
8
8
|
http://www.apache.org/licenses/
|
|
@@ -206,10 +206,10 @@ License: Apache License
|
|
|
206
206
|
limitations under the License.
|
|
207
207
|
|
|
208
208
|
Project-URL: Homepage, https://pypi.org/project/tinysemver/
|
|
209
|
-
Project-URL: Documentation, https://github.com/ashvardanian/
|
|
210
|
-
Project-URL: Repository, https://github.com/ashvardanian/
|
|
211
|
-
Project-URL: Issues, https://github.com/ashvardanian/
|
|
212
|
-
Project-URL: Changelog, https://github.com/ashvardanian/
|
|
209
|
+
Project-URL: Documentation, https://github.com/ashvardanian/TinySemVer/blob/main/README.md
|
|
210
|
+
Project-URL: Repository, https://github.com/ashvardanian/TinySemVer.git
|
|
211
|
+
Project-URL: Issues, https://github.com/ashvardanian/TinySemVer/issues
|
|
212
|
+
Project-URL: Changelog, https://github.com/ashvardanian/TinySemVer/blob/main/CHANGELOG.md
|
|
213
213
|
Classifier: Development Status :: 5 - Production/Stable
|
|
214
214
|
Classifier: Intended Audience :: Developers
|
|
215
215
|
Classifier: Topic :: Software Development :: Build Tools
|
|
@@ -223,12 +223,23 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
223
223
|
Classifier: Programming Language :: Python :: 3.11
|
|
224
224
|
Description-Content-Type: text/markdown
|
|
225
225
|
License-File: LICENSE
|
|
226
|
-
Requires-Dist: openai>=1.54.4
|
|
227
226
|
Requires-Dist: rich>=13.8.0
|
|
227
|
+
Provides-Extra: test
|
|
228
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
229
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
230
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "test"
|
|
231
|
+
Provides-Extra: dev
|
|
232
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
233
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
234
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
235
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
236
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
237
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
238
|
+
Dynamic: license-file
|
|
228
239
|
|
|
229
240
|

|
|
230
241
|
|
|
231
|
-
__TinySemVer__ is a minimalistic [Semantic Versioning](https://semver.org/) package for projects following [Conventional Commits](https://www.conventionalcommits.org/) in a single short Python file
|
|
242
|
+
__TinySemVer__ is a minimalistic [Semantic Versioning](https://semver.org/) package for projects following [Conventional Commits](https://www.conventionalcommits.org/) in a single short Python file.
|
|
232
243
|
In plain English, if your commit messages look like `feat: add new feature` or `fix: bugfix`, this package will automate releasing new "GIT tags" based on the commit messages.
|
|
233
244
|
Here is how to integrate it into your project CI:
|
|
234
245
|
|
|
@@ -306,19 +317,6 @@ Alternatively, you can just ask for `--help`:
|
|
|
306
317
|
$ tinysemver --help
|
|
307
318
|
```
|
|
308
319
|
|
|
309
|
-
## AI and Rock-n-Roll
|
|
310
|
-
|
|
311
|
-
TinySemVer can leverage a language model to validate the commits and generate clean and infromative release notes.
|
|
312
|
-
|
|
313
|
-
```sh
|
|
314
|
-
$ tinysemver --verbose \
|
|
315
|
-
--github-repository 'ashvardanian/tinysemver' \
|
|
316
|
-
--openai-base-url 'https://api.groq.com/openai/v1' \
|
|
317
|
-
--openai-api-key 'GET_YOURSELF_A_KEY' \
|
|
318
|
-
--openai-model 'llama-3.2-11b-text-preview' \
|
|
319
|
-
--dry-run
|
|
320
|
-
```
|
|
321
|
-
|
|
322
320
|
## Usage Details for the GitHub CI Action
|
|
323
321
|
|
|
324
322
|
TinySemVer can be easily integrated into your GitHub Actions CI pipeline.
|
|
@@ -337,12 +335,12 @@ jobs:
|
|
|
337
335
|
|
|
338
336
|
steps:
|
|
339
337
|
- name: Checkout
|
|
340
|
-
uses: actions/checkout@
|
|
338
|
+
uses: actions/checkout@v5
|
|
341
339
|
with:
|
|
342
340
|
persist-credentials: false # Only if main branch if protected
|
|
343
341
|
|
|
344
342
|
- name: Run TinySemVer
|
|
345
|
-
uses: ashvardanian/tinysemver@
|
|
343
|
+
uses: ashvardanian/tinysemver@v3 # Automatically receives non-breaking updates
|
|
346
344
|
with:
|
|
347
345
|
major-verbs: 'breaking,break,major'
|
|
348
346
|
minor-verbs: 'feature,minor,add,new'
|
|
@@ -363,7 +361,7 @@ jobs:
|
|
|
363
361
|
runs-on: ubuntu-latest
|
|
364
362
|
|
|
365
363
|
steps:
|
|
366
|
-
- uses: actions/checkout@
|
|
364
|
+
- uses: actions/checkout@v5
|
|
367
365
|
with:
|
|
368
366
|
ref: main # Take the most recent updated version
|
|
369
367
|
```
|
|
@@ -382,6 +380,30 @@ If you need to update the version in multiple files, pass a multiline string wit
|
|
|
382
380
|
|
|
383
381
|
For examples, consider checking StringZilla, USearch, and other libraries using TinySemVer.
|
|
384
382
|
|
|
383
|
+
Since version 3.0.0, TinySemVer can optionally maintain moving version tags similar to `actions/checkout@v4`:
|
|
384
|
+
|
|
385
|
+
```yaml
|
|
386
|
+
uses: ashvardanian/tinysemver@v3 # Automatically gets non-breaking updates
|
|
387
|
+
uses: ashvardanian/tinysemver@v3.0 # Automatically gets patch updates
|
|
388
|
+
uses: ashvardanian/tinysemver@v3.0.0 # Pinned, never changes
|
|
389
|
+
uses: ashvardanian/tinysemver@main # Bleeding edge
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
This feature is disabled by default because it requires force-push permissions.
|
|
393
|
+
To enable it, add `push-moving-tags: 'true'` and ensure your token has force-push access:
|
|
394
|
+
|
|
395
|
+
```yaml
|
|
396
|
+
- name: Run TinySemVer
|
|
397
|
+
uses: ashvardanian/tinysemver@v3
|
|
398
|
+
with:
|
|
399
|
+
push: 'true'
|
|
400
|
+
push-moving-tags: 'true'
|
|
401
|
+
github-token: ${{ secrets.TINYSEMVER_TOKEN }}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
When enabled, TinySemVer creates three tags: `v3` (major), `v3.0` (minor), and `v3.0.0` (specific).
|
|
405
|
+
The default `GITHUB_TOKEN` typically lacks force-push permissions for protected branches.
|
|
406
|
+
|
|
385
407
|
### Security Considerations
|
|
386
408
|
|
|
387
409
|
If your default branch is protected with a "pull request before merging" rule:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|