semtag 0.1.1__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.
- semtag-0.1.1/.ansible/.lock +0 -0
- semtag-0.1.1/.github/workflows/publish.yml +49 -0
- semtag-0.1.1/.gitignore +64 -0
- semtag-0.1.1/LICENSE +21 -0
- semtag-0.1.1/PKG-INFO +89 -0
- semtag-0.1.1/README.md +64 -0
- semtag-0.1.1/SemanticVersion.py +91 -0
- semtag-0.1.1/pyproject.toml +43 -0
- semtag-0.1.1/requirements.txt +1 -0
- semtag-0.1.1/semtag.egg-info/PKG-INFO +89 -0
- semtag-0.1.1/semtag.egg-info/SOURCES.txt +15 -0
- semtag-0.1.1/semtag.egg-info/dependency_links.txt +1 -0
- semtag-0.1.1/semtag.egg-info/entry_points.txt +2 -0
- semtag-0.1.1/semtag.egg-info/requires.txt +1 -0
- semtag-0.1.1/semtag.egg-info/top_level.txt +2 -0
- semtag-0.1.1/semtag.py +156 -0
- semtag-0.1.1/setup.cfg +4 -0
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
- '[0-9]+.[0-9]+.[0-9]+'
|
|
8
|
+
- '[0-9]+.[0-9]+.[0-9]+-*'
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build-and-publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
id-token: write # Required for trusted publishing
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout code
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0 # Required for setuptools_scm to get git history
|
|
22
|
+
|
|
23
|
+
- name: Set up Python
|
|
24
|
+
uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: '3.x'
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: |
|
|
30
|
+
python -m pip install --upgrade pip
|
|
31
|
+
pip install build twine
|
|
32
|
+
|
|
33
|
+
- name: Build package
|
|
34
|
+
run: python -m build
|
|
35
|
+
|
|
36
|
+
- name: Check package
|
|
37
|
+
run: twine check dist/*
|
|
38
|
+
|
|
39
|
+
# Use the official PyPI publish action
|
|
40
|
+
- name: Publish to PyPI
|
|
41
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
42
|
+
|
|
43
|
+
# Use the GitHub Release action to creata release
|
|
44
|
+
- name: Create GitHub Release
|
|
45
|
+
uses: softprops/action-gh-release@v1
|
|
46
|
+
with:
|
|
47
|
+
generate_release_notes: true
|
|
48
|
+
files: |
|
|
49
|
+
dist/*
|
semtag-0.1.1/.gitignore
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
.Python
|
|
8
|
+
build/
|
|
9
|
+
develop-eggs/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
lib/
|
|
15
|
+
lib64/
|
|
16
|
+
parts/
|
|
17
|
+
sdist/
|
|
18
|
+
var/
|
|
19
|
+
wheels/
|
|
20
|
+
pip-wheel-metadata/
|
|
21
|
+
share/python-wheels/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
.installed.cfg
|
|
24
|
+
*.egg
|
|
25
|
+
MANIFEST
|
|
26
|
+
|
|
27
|
+
# PyInstaller
|
|
28
|
+
*.manifest
|
|
29
|
+
*.spec
|
|
30
|
+
|
|
31
|
+
# Unit test / coverage reports
|
|
32
|
+
htmlcov/
|
|
33
|
+
.tox/
|
|
34
|
+
.nox/
|
|
35
|
+
.coverage
|
|
36
|
+
.coverage.*
|
|
37
|
+
.cache
|
|
38
|
+
nosetests.xml
|
|
39
|
+
coverage.xml
|
|
40
|
+
*.cover
|
|
41
|
+
*.py,cover
|
|
42
|
+
.hypothesis/
|
|
43
|
+
.pytest_cache/
|
|
44
|
+
|
|
45
|
+
# Virtual environments
|
|
46
|
+
venv/
|
|
47
|
+
env/
|
|
48
|
+
ENV/
|
|
49
|
+
env.bak/
|
|
50
|
+
venv.bak/
|
|
51
|
+
|
|
52
|
+
# IDE
|
|
53
|
+
.vscode/
|
|
54
|
+
.idea/
|
|
55
|
+
*.swp
|
|
56
|
+
*.swo
|
|
57
|
+
*~
|
|
58
|
+
|
|
59
|
+
# OS
|
|
60
|
+
.DS_Store
|
|
61
|
+
Thumbs.db
|
|
62
|
+
|
|
63
|
+
# debug
|
|
64
|
+
/tmp/
|
semtag-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
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.
|
semtag-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: semtag
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A tool for managing semantic version tags in git repositories
|
|
5
|
+
Author-email: Mateusz Mikrut <mateusz.mikrut@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mateuszmikrut/semtag
|
|
8
|
+
Project-URL: Bug Reports, https://github.com/mateuszmikrut/semtag/issues
|
|
9
|
+
Project-URL: Source, https://github.com/mateuszmikrut/semtag
|
|
10
|
+
Keywords: git,tags,semantic-versioning,semver,version-control
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: gitpython>=3.1.0
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Semantic Version GIT Tagger
|
|
27
|
+
|
|
28
|
+
A pretty trivial python script to easely manage git tags with semantic versioning (semver.org)
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
semtag [options]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Options
|
|
37
|
+
|
|
38
|
+
- `-p, --patch` - Increment patch version (x.x.PATCH)
|
|
39
|
+
- `-m, --minor` - Increment minor version (x.MINOR.0)
|
|
40
|
+
- `-M, --major` - Increment major version (MAJOR.0.0)
|
|
41
|
+
- `-b, --by` - Increment by a specific number (default: 1)
|
|
42
|
+
- `-l, --label` - Add label to the version (e.g., -l rc1 creates 1.0.0-rc1)
|
|
43
|
+
- `-u, --push` - Push the new tag to remote repository
|
|
44
|
+
- `-n, --nofetch` - Do not fetch tags from remote prior creating new one
|
|
45
|
+
<!-- - `-f, --force` - Force operation even if not on main/master branch -->
|
|
46
|
+
- `-v, --verbose` - Increase verbosity (use -v, -vv, or -vvv for more detail)
|
|
47
|
+
|
|
48
|
+
### Examples
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Increment minor version (1.0.0 -> 1.1.0)
|
|
52
|
+
semtag -m
|
|
53
|
+
|
|
54
|
+
# Increment major version (1.0.0 -> 2.0.0)
|
|
55
|
+
semtag -M
|
|
56
|
+
|
|
57
|
+
# Increment patch version (1.0.0 -> 1.0.5)
|
|
58
|
+
semtag -p -b 5
|
|
59
|
+
|
|
60
|
+
# Increment patch and add label (1.0.0 -> 1.0.1-rc1), don't fetch and push new tag
|
|
61
|
+
semtag -n -u -p -l rc1
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
Using pip (preferred)
|
|
67
|
+
```bash
|
|
68
|
+
pip install semtag
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
From git
|
|
72
|
+
```bash
|
|
73
|
+
git clone https://github.com/mateuszmikrut/semtag.git
|
|
74
|
+
cd semtag
|
|
75
|
+
python -m venv venv
|
|
76
|
+
source ./venv/bin/activate
|
|
77
|
+
pip install -r requirements.txt
|
|
78
|
+
python ./semtag.py
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Supported Version Formats
|
|
82
|
+
|
|
83
|
+
The script supports semantic versioning with the following formats:
|
|
84
|
+
- `v1.0.0` (with 'v' prefix)
|
|
85
|
+
- `1.0.0` (without prefix)
|
|
86
|
+
- `1.0.0-rc1` (with prerelease label)
|
|
87
|
+
|
|
88
|
+
When incrementing versions, prerelease labels are automatically removed.
|
|
89
|
+
|
semtag-0.1.1/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Semantic Version GIT Tagger
|
|
2
|
+
|
|
3
|
+
A pretty trivial python script to easely manage git tags with semantic versioning (semver.org)
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
semtag [options]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Options
|
|
12
|
+
|
|
13
|
+
- `-p, --patch` - Increment patch version (x.x.PATCH)
|
|
14
|
+
- `-m, --minor` - Increment minor version (x.MINOR.0)
|
|
15
|
+
- `-M, --major` - Increment major version (MAJOR.0.0)
|
|
16
|
+
- `-b, --by` - Increment by a specific number (default: 1)
|
|
17
|
+
- `-l, --label` - Add label to the version (e.g., -l rc1 creates 1.0.0-rc1)
|
|
18
|
+
- `-u, --push` - Push the new tag to remote repository
|
|
19
|
+
- `-n, --nofetch` - Do not fetch tags from remote prior creating new one
|
|
20
|
+
<!-- - `-f, --force` - Force operation even if not on main/master branch -->
|
|
21
|
+
- `-v, --verbose` - Increase verbosity (use -v, -vv, or -vvv for more detail)
|
|
22
|
+
|
|
23
|
+
### Examples
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Increment minor version (1.0.0 -> 1.1.0)
|
|
27
|
+
semtag -m
|
|
28
|
+
|
|
29
|
+
# Increment major version (1.0.0 -> 2.0.0)
|
|
30
|
+
semtag -M
|
|
31
|
+
|
|
32
|
+
# Increment patch version (1.0.0 -> 1.0.5)
|
|
33
|
+
semtag -p -b 5
|
|
34
|
+
|
|
35
|
+
# Increment patch and add label (1.0.0 -> 1.0.1-rc1), don't fetch and push new tag
|
|
36
|
+
semtag -n -u -p -l rc1
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
Using pip (preferred)
|
|
42
|
+
```bash
|
|
43
|
+
pip install semtag
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
From git
|
|
47
|
+
```bash
|
|
48
|
+
git clone https://github.com/mateuszmikrut/semtag.git
|
|
49
|
+
cd semtag
|
|
50
|
+
python -m venv venv
|
|
51
|
+
source ./venv/bin/activate
|
|
52
|
+
pip install -r requirements.txt
|
|
53
|
+
python ./semtag.py
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Supported Version Formats
|
|
57
|
+
|
|
58
|
+
The script supports semantic versioning with the following formats:
|
|
59
|
+
- `v1.0.0` (with 'v' prefix)
|
|
60
|
+
- `1.0.0` (without prefix)
|
|
61
|
+
- `1.0.0-rc1` (with prerelease label)
|
|
62
|
+
|
|
63
|
+
When incrementing versions, prerelease labels are automatically removed.
|
|
64
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Semantic versioning module for parsing and bumping ver_list numbers
|
|
3
|
+
More about semantic versioning: https://semver.org/
|
|
4
|
+
"""
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
class SemanticVersion:
|
|
8
|
+
"""Handle semantic versioning parsing and incrementing"""
|
|
9
|
+
def __init__(self, version_string: str):
|
|
10
|
+
self.original = version_string
|
|
11
|
+
self.prefix = ""
|
|
12
|
+
self.major = 0
|
|
13
|
+
self.minor = 0
|
|
14
|
+
self.patch = 0
|
|
15
|
+
self.label = ""
|
|
16
|
+
self._parse(version_string)
|
|
17
|
+
|
|
18
|
+
def _parse(self, version_string: str):
|
|
19
|
+
"""Parse and validate semantic version string"""
|
|
20
|
+
# 'v' prefix handling
|
|
21
|
+
if version_string.startswith('v'):
|
|
22
|
+
self.prefix = 'v'
|
|
23
|
+
version_string = version_string[1:]
|
|
24
|
+
|
|
25
|
+
# regex from semver.org
|
|
26
|
+
pattern = r'^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
|
|
27
|
+
match = re.match(pattern, version_string)
|
|
28
|
+
|
|
29
|
+
if not match:
|
|
30
|
+
raise ValueError(f"Invalid semantic version format: {self.original}")
|
|
31
|
+
|
|
32
|
+
self.major = int(match.group('major'))
|
|
33
|
+
self.minor = int(match.group('minor'))
|
|
34
|
+
self.patch = int(match.group('patch'))
|
|
35
|
+
self.label = match.group('prerelease') or ""
|
|
36
|
+
|
|
37
|
+
def inc_major(self, by=1) -> 'SemanticVersion':
|
|
38
|
+
""" Increment major version and reset minor & patch"""
|
|
39
|
+
self.major += by
|
|
40
|
+
self.minor = 0
|
|
41
|
+
self.patch = 0
|
|
42
|
+
self.label = ""
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
def inc_minor(self, by=1) -> 'SemanticVersion':
|
|
46
|
+
""" Increment minor version and reset patch"""
|
|
47
|
+
self.minor += by
|
|
48
|
+
self.patch = 0
|
|
49
|
+
self.label = ""
|
|
50
|
+
return self
|
|
51
|
+
|
|
52
|
+
def inc_patch(self, by=1) -> 'SemanticVersion':
|
|
53
|
+
""" Increment patch - preserve major & minor"""
|
|
54
|
+
self.patch += by
|
|
55
|
+
self.label = ""
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
def add_label(self, label: str) -> 'SemanticVersion':
|
|
59
|
+
"""Add a label to the version"""
|
|
60
|
+
self.label = label
|
|
61
|
+
return self
|
|
62
|
+
|
|
63
|
+
def __str__(self) -> str:
|
|
64
|
+
"""Return concatinated string"""
|
|
65
|
+
version = f"{self.prefix}{self.major}.{self.minor}.{self.patch}"
|
|
66
|
+
if self.label:
|
|
67
|
+
version += f"-{self.label}"
|
|
68
|
+
return version
|
|
69
|
+
|
|
70
|
+
def __repr__(self) -> str:
|
|
71
|
+
"""Return representation of version"""
|
|
72
|
+
return f"SemanticVersion('{str(self)}')"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
### This is standalone function (could be @staticmethod as well) ###
|
|
76
|
+
def semsort(versions: list[str]) -> list[str]:
|
|
77
|
+
"""Sort semantic version tags by major, minor, patch """
|
|
78
|
+
ver_list = []
|
|
79
|
+
for v in versions:
|
|
80
|
+
try:
|
|
81
|
+
version = SemanticVersion(v)
|
|
82
|
+
ver_list.append((v, version))
|
|
83
|
+
except ValueError:
|
|
84
|
+
# Skip invalid semantic version tags
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
ver_list.sort(
|
|
88
|
+
key=lambda x: (x[1].major, x[1].minor, x[1].patch),
|
|
89
|
+
reverse=True
|
|
90
|
+
)
|
|
91
|
+
return [x for x, _ in ver_list]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "semtag"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "A tool for managing semantic version tags in git repositories"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Mateusz Mikrut", email = "mateusz.mikrut@gmail.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["git", "tags", "semantic-versioning", "semver", "version-control"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Topic :: Software Development :: Version Control :: Git",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.8",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"gitpython>=3.1.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/mateuszmikrut/semtag"
|
|
33
|
+
"Bug Reports" = "https://github.com/mateuszmikrut/semtag/issues"
|
|
34
|
+
Source = "https://github.com/mateuszmikrut/semtag"
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
semtag = "semtag:main"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools]
|
|
40
|
+
py-modules = ["semtag", "SemanticVersion"]
|
|
41
|
+
|
|
42
|
+
[tool.setuptools_scm]
|
|
43
|
+
version_scheme = "post-release"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gitpython>=3.1.0
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: semtag
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A tool for managing semantic version tags in git repositories
|
|
5
|
+
Author-email: Mateusz Mikrut <mateusz.mikrut@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mateuszmikrut/semtag
|
|
8
|
+
Project-URL: Bug Reports, https://github.com/mateuszmikrut/semtag/issues
|
|
9
|
+
Project-URL: Source, https://github.com/mateuszmikrut/semtag
|
|
10
|
+
Keywords: git,tags,semantic-versioning,semver,version-control
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: gitpython>=3.1.0
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Semantic Version GIT Tagger
|
|
27
|
+
|
|
28
|
+
A pretty trivial python script to easely manage git tags with semantic versioning (semver.org)
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
semtag [options]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Options
|
|
37
|
+
|
|
38
|
+
- `-p, --patch` - Increment patch version (x.x.PATCH)
|
|
39
|
+
- `-m, --minor` - Increment minor version (x.MINOR.0)
|
|
40
|
+
- `-M, --major` - Increment major version (MAJOR.0.0)
|
|
41
|
+
- `-b, --by` - Increment by a specific number (default: 1)
|
|
42
|
+
- `-l, --label` - Add label to the version (e.g., -l rc1 creates 1.0.0-rc1)
|
|
43
|
+
- `-u, --push` - Push the new tag to remote repository
|
|
44
|
+
- `-n, --nofetch` - Do not fetch tags from remote prior creating new one
|
|
45
|
+
<!-- - `-f, --force` - Force operation even if not on main/master branch -->
|
|
46
|
+
- `-v, --verbose` - Increase verbosity (use -v, -vv, or -vvv for more detail)
|
|
47
|
+
|
|
48
|
+
### Examples
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Increment minor version (1.0.0 -> 1.1.0)
|
|
52
|
+
semtag -m
|
|
53
|
+
|
|
54
|
+
# Increment major version (1.0.0 -> 2.0.0)
|
|
55
|
+
semtag -M
|
|
56
|
+
|
|
57
|
+
# Increment patch version (1.0.0 -> 1.0.5)
|
|
58
|
+
semtag -p -b 5
|
|
59
|
+
|
|
60
|
+
# Increment patch and add label (1.0.0 -> 1.0.1-rc1), don't fetch and push new tag
|
|
61
|
+
semtag -n -u -p -l rc1
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
Using pip (preferred)
|
|
67
|
+
```bash
|
|
68
|
+
pip install semtag
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
From git
|
|
72
|
+
```bash
|
|
73
|
+
git clone https://github.com/mateuszmikrut/semtag.git
|
|
74
|
+
cd semtag
|
|
75
|
+
python -m venv venv
|
|
76
|
+
source ./venv/bin/activate
|
|
77
|
+
pip install -r requirements.txt
|
|
78
|
+
python ./semtag.py
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Supported Version Formats
|
|
82
|
+
|
|
83
|
+
The script supports semantic versioning with the following formats:
|
|
84
|
+
- `v1.0.0` (with 'v' prefix)
|
|
85
|
+
- `1.0.0` (without prefix)
|
|
86
|
+
- `1.0.0-rc1` (with prerelease label)
|
|
87
|
+
|
|
88
|
+
When incrementing versions, prerelease labels are automatically removed.
|
|
89
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
.gitignore
|
|
2
|
+
LICENSE
|
|
3
|
+
README.md
|
|
4
|
+
SemanticVersion.py
|
|
5
|
+
pyproject.toml
|
|
6
|
+
requirements.txt
|
|
7
|
+
semtag.py
|
|
8
|
+
.ansible/.lock
|
|
9
|
+
.github/workflows/publish.yml
|
|
10
|
+
semtag.egg-info/PKG-INFO
|
|
11
|
+
semtag.egg-info/SOURCES.txt
|
|
12
|
+
semtag.egg-info/dependency_links.txt
|
|
13
|
+
semtag.egg-info/entry_points.txt
|
|
14
|
+
semtag.egg-info/requires.txt
|
|
15
|
+
semtag.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gitpython>=3.1.0
|
semtag-0.1.1/semtag.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
SemTag - A tool for managing semantic version tags in git repositories
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import logging
|
|
8
|
+
# import sys
|
|
9
|
+
from SemanticVersion import SemanticVersion, semsort
|
|
10
|
+
import git
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
def main():
|
|
16
|
+
""" Main function """
|
|
17
|
+
########################
|
|
18
|
+
### Argument Parsing ###
|
|
19
|
+
########################
|
|
20
|
+
parser = argparse.ArgumentParser(
|
|
21
|
+
description='SemTag - Manage semantic version tags in git repositories',
|
|
22
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
23
|
+
epilog="""
|
|
24
|
+
Examples:
|
|
25
|
+
%(prog)s -m # Increment minor version (1.0.0 -> 1.1.0)
|
|
26
|
+
%(prog)s -M # Increment major version (1.0.0 -> 2.0.0)
|
|
27
|
+
%(prog)s -p -b 5 # Increment patch version by 5 (1.0.0 -> 1.0.5)
|
|
28
|
+
%(prog)s -p -l rc1 # Increment patch and add label (1.0.0 -> 1.0.1-rc1)
|
|
29
|
+
"""
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity (-v for INFO, -vv for DEBUG)')
|
|
32
|
+
# parser.add_argument('-f', '--force', action='store_true', default=False, help='Force the operation even if not on main/master branch')
|
|
33
|
+
parser.add_argument('-b', '--by', action='store', type=int, default=1, help='Increment by a specific number')
|
|
34
|
+
|
|
35
|
+
version_group = parser.add_mutually_exclusive_group(required=True)
|
|
36
|
+
version_group.add_argument('-p', '--patch', action='store_true', help='Increment patch version (x.x.PATCH)')
|
|
37
|
+
version_group.add_argument('-m', '--minor', action='store_true', help='Increment minor version (x.MINOR.0)')
|
|
38
|
+
version_group.add_argument('-M', '--major', action='store_true', help='Increment major version (MAJOR.0.0)')
|
|
39
|
+
|
|
40
|
+
parser.add_argument('-l', '--label', type=str, default=None, help='Add label to the version (e.g., -l rc1 creates 1.0.0-rc1)')
|
|
41
|
+
parser.add_argument('-u', '--push', action='store_true', help='Push the new tag to remote repository', default=False)
|
|
42
|
+
parser.add_argument('-n', '--no-fetch', action='store_true', help='Do not fetch tags from remote before operation', default=False)
|
|
43
|
+
args = parser.parse_args()
|
|
44
|
+
|
|
45
|
+
### Logging based on verbosity ###
|
|
46
|
+
if args.verbose == 0:
|
|
47
|
+
log_level = logging.WARNING
|
|
48
|
+
elif args.verbose == 1:
|
|
49
|
+
log_level = logging.INFO
|
|
50
|
+
else:
|
|
51
|
+
log_level = logging.DEBUG
|
|
52
|
+
|
|
53
|
+
logging.basicConfig(
|
|
54
|
+
level=log_level,
|
|
55
|
+
format='%(message)s' # Keep it simple (no timestamps, severity etc.)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
##################
|
|
59
|
+
### Main Logic ###
|
|
60
|
+
##################
|
|
61
|
+
logger.debug(f"Arguments: {args}")
|
|
62
|
+
pwd = Path('.').resolve()
|
|
63
|
+
|
|
64
|
+
### Check if this is a git workspace ###
|
|
65
|
+
try:
|
|
66
|
+
repo = git.Repo(pwd, search_parent_directories=True)
|
|
67
|
+
logger.debug(f"Found git repository at {repo.working_dir}")
|
|
68
|
+
except git.InvalidGitRepositoryError:
|
|
69
|
+
logger.error(f"{pwd} is not a git repository")
|
|
70
|
+
exit(1)
|
|
71
|
+
except git.GitCommandError as e:
|
|
72
|
+
logger.error(f"Git command failed: {e}")
|
|
73
|
+
exit(99)
|
|
74
|
+
|
|
75
|
+
reponame = Path(repo.working_dir).name
|
|
76
|
+
logger.debug(f"Repository name : {reponame}")
|
|
77
|
+
|
|
78
|
+
## Check if on main/master branch (warning only)
|
|
79
|
+
# try:
|
|
80
|
+
# if repo.active_branch.name not in ['main', 'master']:
|
|
81
|
+
# logger.warning(f"You are on branch '{repo.active_branch.name}', not on main/master")
|
|
82
|
+
# logger.debug(f"On {repo.active_branch.name} branch")
|
|
83
|
+
# except TypeError:
|
|
84
|
+
# logger.warning("HEAD is detached, not on any branch")
|
|
85
|
+
# exit(2)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# Fetch tags from remote to avoid duplicates
|
|
89
|
+
if not args.no_fetch:
|
|
90
|
+
try:
|
|
91
|
+
logger.debug("Fetching tags from remote...")
|
|
92
|
+
repo.remotes.origin.fetch(tags=True)
|
|
93
|
+
logger.debug("Tags fetched successfully")
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.warning(f"Error fetching tags: {e}")
|
|
96
|
+
|
|
97
|
+
tags = semsort([tag.name for tag in repo.tags])
|
|
98
|
+
|
|
99
|
+
if tags:
|
|
100
|
+
latest_tag = tags[0]
|
|
101
|
+
logger.debug(f"Latest semantic version tag: {latest_tag}")
|
|
102
|
+
else:
|
|
103
|
+
# No tags found, start with 0.0.0
|
|
104
|
+
logger.debug("No semantic version tags found. Starting with 0.0.0")
|
|
105
|
+
latest_tag = '0.0.0'
|
|
106
|
+
|
|
107
|
+
# Initialize obj
|
|
108
|
+
current_version = SemanticVersion(latest_tag)
|
|
109
|
+
|
|
110
|
+
### Increment version ###
|
|
111
|
+
if args.major:
|
|
112
|
+
logger.debug("Incrementing major version")
|
|
113
|
+
current_version.inc_major(by=args.by)
|
|
114
|
+
elif args.minor:
|
|
115
|
+
logger.debug("Incrementing minor version")
|
|
116
|
+
current_version.inc_minor(by=args.by)
|
|
117
|
+
elif args.patch:
|
|
118
|
+
logger.debug("Incrementing patch version")
|
|
119
|
+
current_version.inc_patch(by=args.by)
|
|
120
|
+
|
|
121
|
+
### Label ###
|
|
122
|
+
if args.label:
|
|
123
|
+
logger.debug(f"Adding label: {args.label}")
|
|
124
|
+
current_version.add_label(args.label)
|
|
125
|
+
|
|
126
|
+
new_tag = str(current_version)
|
|
127
|
+
logger.info(f"Generate tag: {new_tag}")
|
|
128
|
+
|
|
129
|
+
### Create and push
|
|
130
|
+
try:
|
|
131
|
+
#TODO: add tag message option later
|
|
132
|
+
#repo.create_tag(new_tag, message=f"Release {new_tag}")
|
|
133
|
+
repo.create_tag(new_tag)
|
|
134
|
+
logger.info(f"Successfully created tag: {new_tag}")
|
|
135
|
+
|
|
136
|
+
# Push if requested
|
|
137
|
+
if args.push:
|
|
138
|
+
logger.info(f"Pushing tag '{new_tag}' to remote...")
|
|
139
|
+
repo.remote('origin').push(new_tag) # It only pushes the new tag not all from local repo
|
|
140
|
+
logger.info(f"Successfully pushed new tag: {new_tag}")
|
|
141
|
+
else:
|
|
142
|
+
logger.debug("Not pushing tag to remote")
|
|
143
|
+
except git.GitCommandError as e:
|
|
144
|
+
logger.error(f"Error creating/pushing tag: {e}")
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.error(f"Error: {e}")
|
|
147
|
+
|
|
148
|
+
# Print the new tag to stout as confirmation if not verbose
|
|
149
|
+
if args.verbose == 0:
|
|
150
|
+
GREEN = '\033[92m'
|
|
151
|
+
YELLOW = '\033[93m'
|
|
152
|
+
RESET = '\033[0m'
|
|
153
|
+
print(f"{GREEN}{reponame}{RESET} -> {YELLOW}{new_tag}{RESET}")
|
|
154
|
+
|
|
155
|
+
if __name__ == "__main__":
|
|
156
|
+
main()
|
semtag-0.1.1/setup.cfg
ADDED