Compare commits

...

3 Commits

Author SHA1 Message Date
cclecle
63b3f25b33 fix quality warning + commit messages unittest 2023-03-19 10:03:50 +00:00
cclecle
cefa964a3a add commit class 2023-03-19 09:52:42 +00:00
cclecle
38cde9d700 reword 2023-03-18 21:13:11 +00:00
5 changed files with 330 additions and 24 deletions

1
Jenkinsfile vendored
View File

@@ -181,7 +181,6 @@ pipeline {
sh("rm -Rf ~/_gitrepo || true")
//sh(". ~/BUILD_ENV/bin/activate && python -m copier --vcs-ref HEAD --prereleases --defaults ./ ~/_gitrepo")
sh(script: """#!/bin/sh -
|. ~/BUILD_ENV/bin/activate
|exec python - << '__EOWRAPPER__'

View File

@@ -5,7 +5,10 @@
# pyGitVersionHelper
A tiny library to help versioning management of git python project
_A tiny library to help versioning management of git python projects_
Because a good developer is a lazy developer and version management in CI/CD can be very time consuming.
Checkout [Latest Documentation](https://chacha.ddns.net/mkdocs-web/chacha/pygitversionhelper/{{branch}}/latest/).
@@ -13,7 +16,7 @@ Checkout [Latest Documentation](https://chacha.ddns.net/mkdocs-web/chacha/pygitv
- list tags
- get last tag
- get last version
- get current version (bump)
- get current version (bumped)
- convert / switch from SemVer to PEP440 (both ways)
- automatic version format detection (SemVer by default)

View File

@@ -20,13 +20,13 @@ Add this line on the top of your python script:
#from pygitversionhelper import gitversionhelper
(optionnal)If you need to catch exception from this module:
[optionnal] If you need to catch exception from this module:
#from pygitversionhelper import gitversionhelperException
## Basic API
All the API commands are static and so it is not needed to create instantiate any object.
All the API commands are static so it is not needed to create instantiate any object.
They are all executed in the current active directory.
@@ -44,12 +44,12 @@ To check if a repository is dirty:
### sublib: tag
List all tags (default to taggerdate order):
List all tags [default to taggerdate order]:
for tag in gitversionhelper.tag.getTags():
print(f"found tag: {tag}")
List all tags (using git refname order):
List all tags [using git refname order]:
for tag in gitversionhelper.tag.getTags("v:refname"):
print(f"found tag: {tag}")
@@ -58,7 +58,7 @@ Get the last tag:
print(f"most recent repository tag: {gitversionhelper.tag.getLastTag()}")
Get the last tag (only on same branch):
Get the last tag [only on same branch]:
print(f"most recent repository tag: {gitversionhelper.tag.getLastTag(same_branch=True)}")
@@ -67,26 +67,26 @@ Get the distance from HEAD to last tag:
print(f"number of commit since last tag: {gitversionhelper.tag.getDistanceFromTag()}")
Get the distance from HEAD to last tag (only on same branch):
Get the distance from HEAD to last tag [only on same branch]:
print(f"number of commit since last tag: {gitversionhelper.tag.getDistanceFromTag(same_branch=True)}")
### sublib: version
Get the last found version in the repository (return MetaVersion object):
Get the last found version in the repository [return MetaVersion object]:
print(f"most recent repository version: {gitversionhelper.tag.getLastVersion()}")
Get the last found version in the repository (return formated string):
Get the last found version in the repository [return formated string]:
print(f"most recent repository version: {gitversionhelper.tag.getLastVersion(formated_output=True)}")
Others kwargs available to this function:
- version_std: string to force a version standard for rendering ("PEP440" or "SemVer")
- version_std: string to force a version standard for rendering ["PEP440" or "SemVer"]
- same_branch: boolean to force searching on same branch
- ignore_unknown_tags: boolean to allow unknown tag to be ignored
Get the current version of the repository, including bumped one if the last one is not tagged (return MetaVersion object):
Get the current version of the repository, automatically bump it if the last one is not tagged [returns MetaVersion object]:
print(f"most recent repository version: {gitversionhelper.tag.getCurrentVersion()}")
@@ -103,7 +103,7 @@ A version object can also be manually formated:
gitversionhelper.version.doFormatVersion(_version)
kwargs available to those function:
- output_format: string to choose a rendering format ("Auto","PEP440" or "SemVer")
- output_format: string to choose a rendering format ["Auto","PEP440" or "SemVer"]
## Limitations

View File

@@ -5,7 +5,6 @@
#
# You should have received a copy of the license along with this
# work. If not, see <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
"""
This project try to help doing handy operations with git when
dealing with project versioning and tags on python project -
@@ -13,6 +12,7 @@ at leat for project using PEP440 or SemVer standards.
One requirement is to keep it compact and to not cover too much fancy features.
This is the reason why it is one single file with nested classes.
=> Design is on-purpose poorly expandable to keep the scope light.
This library is maid for repository using tag as version.
@@ -23,8 +23,7 @@ This module is the main project file, containing all the code.
Read the read me for more information.
Check the unittest s for usage samples.
Note:
Other Parameters are **kwargs
Note: _Other Parameters_ are **kwargs
"""
from __future__ import annotations
@@ -37,7 +36,7 @@ import logging
from packaging.version import VERSION_PATTERN as packaging_VERSION_PATTERN
def _exec(cmd: str, root: str | os.PathLike | None = None) -> list[str]:
def _exec(cmd: str, root: str | os.PathLike | None = None, raw:bool = False) -> list[str]:
"""
helper function to handle system cmd execution
Args:
@@ -47,13 +46,16 @@ def _exec(cmd: str, root: str | os.PathLike | None = None) -> list[str]:
a list of command's return lines
"""
p = subprocess.run(cmd.split(), text=True, cwd=root, capture_output=True, check=False, timeout=2)
p = subprocess.run(cmd, text=True, cwd=root, capture_output=True, check=False, timeout=2,shell=True)
if re.search("not a git repository",p.stderr):
raise gitversionhelper.repository.notAGitRepository()
if re.search("fatal:",p.stderr): #pragma: nocover
raise gitversionhelper.unknownGITFatalError(p.stderr)
if int(p.returncode) < 0: #pragma: nocover
raise gitversionhelper.unknownGITError(p.stderr)
if raw:
return p.stdout
lines = p.stdout.splitlines()
return [line.rstrip() for line in lines if line.rstrip()]
@@ -108,6 +110,103 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
True if it is dirty
"""
return bool(_exec("git status --short"))
class commit:
"""
class containing methods focusing on commits
"""
__OptDict = {"same_branch": "same_branch",
"merged_output":"merged_output"}
class commitException(gitversionhelperException):
"""
generic commit exception
"""
class commitNotFound(commitException):
"""
tag not found exception
"""
@classmethod
def getMessagesSinceTag(cls,tag:str,**kwargs) -> str:
"""
retrieve a commits message history from repository
from LastCommit to the given tag
Keyword Arguments:
merged_output: output one single merged string
same_branch(bool): force searching only in the same branch
Returns:
the commit message
"""
current_commit_id=cls.getLast(**kwargs)
tag_commit_id=cls.getFromTag(tag,**kwargs)
if ((cls.__OptDict["same_branch"] in kwargs) and (kwargs[cls.__OptDict["same_branch"]] is True)):
commits = _exec(f"git rev-list --first-parent --ancestry-path {tag_commit_id}..{current_commit_id}")
else:
commits = _exec(f"git rev-list --ancestry-path {tag_commit_id}..{current_commit_id}")
result=[]
for commit in commits:
result.append(cls.getMessage(commit,**kwargs))
if ((cls.__OptDict["merged_output"] in kwargs) and (kwargs[cls.__OptDict["merged_output"]] is True)):
print("JOIN")
return os.linesep.join(result)
return result
@classmethod
def getMessage(cls, hash:str) -> str:
"""
retrieve a commit message from repository
Args:
hash: id of the commit
Returns:
the commit message
"""
try:
res=_exec(f"git log -z --pretty=tformat:%B%-C() -n 1 {hash}",None,True).rstrip('\x00')
except gitversionhelper.unknownGITFatalError as _e:
raise cls.commitNotFound("no commit found in commit history") from _e
return res.replace('\r\n','\n').replace('\n','\r\n')
@classmethod
def getFromTag(cls,tag:str) -> str:
"""
retrieve a commit from repository associated to a tag
Args:
tag: tag of the commit
Returns:
the commit Id
"""
try:
res=_exec(f"git rev-list -n 1 {tag}")
except gitversionhelper.unknownGITFatalError as _e:
raise cls.commitNotFound("no commit found in commit history") from _e
if len(res)==0:
raise cls.commitNotFound("no commit found in commit history")
return res[0]
@classmethod
def getLast(cls,**kwargs) -> str:
"""
retrieve last commit from repository
Keyword Arguments:
same_branch(bool): force searching only in the same branch
Returns:
the commit Id
"""
if ((cls.__OptDict["same_branch"] in kwargs) and (kwargs[cls.__OptDict["same_branch"]] is True)):
try:
res = _exec("git rev-parse HEAD")
except gitversionhelper.unknownGITFatalError as _e:
raise cls.commitNotFound("no commit found in commit history") from _e
else:
res = _exec("git for-each-ref --sort=-committerdate refs/heads/ --count 1 --format=%(objectname)")
if len(res)==0:
raise cls.commitNotFound("no commit found in commit history")
return res[0]
class tag:
"""
@@ -132,7 +231,7 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
"""
@classmethod
def getTags(cls,sort:str = "taggerdate",**kwargs) -> list[str]:
def getTags(cls,sort:str = "taggerdate",**kwargs) -> list[str|None]:
"""
retrieve all tags from a repository
Args:
@@ -457,8 +556,8 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
VersionStd = "PEP440"
if not bFound :
raise gitversionhelper.version.noValidVersion("no valid version found in tags")
raise gitversionhelper.version.noValidVersion("no valid version found in tags")
if pre_count > 0 and post_count > 0:
raise cls.PreAndPostVersionUnsupported("can not parse a version with both pre" \
" and post release number.")

View File

@@ -13,6 +13,8 @@ import os
import pathlib
import re
import copy
import time
import subprocess
print(__name__)
print(__package__)
@@ -507,8 +509,7 @@ class Test_gitversionhelper(unittest.TestCase):
self.assertEqual(_v.major,0)
self.assertEqual(_v.minor,1)
self.assertEqual(_v.patch,0)
def test_nominal__version___AUTO_bump_commits(self):
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue")
@@ -1114,6 +1115,210 @@ class Test_gitversionhelper(unittest.TestCase):
self.assertEqual(_v.minor,1)
self.assertEqual(_v.patch,3)
def test_nominal__commit_getLast(self):
# sleeps are needed because commit date have a 1-sec accuracy :-/
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue")
os.system("git add .")
os.system("git commit -m \"commit\"")
time.sleep(1)
initial_commit = pygitversionhelper.gitversionhelper.commit.getLast()
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue2")
os.system("git add .")
os.system("git commit -m \"commit\"")
time.sleep(1)
second_commit = pygitversionhelper.gitversionhelper.commit.getLast()
self.assertNotEqual(initial_commit,second_commit)
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue3")
os.system("git add .")
os.system("git commit -m \"commit\"")
time.sleep(1)
third_commit = pygitversionhelper.gitversionhelper.commit.getLast()
self.assertNotEqual(initial_commit,third_commit)
self.assertNotEqual(second_commit,third_commit)
os.system("git checkout -b dev")
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue4")
os.system("git add .")
os.system("git commit -m \"commit\"")
time.sleep(1)
fourth_commit = pygitversionhelper.gitversionhelper.commit.getLast()
self.assertNotEqual(initial_commit,fourth_commit)
self.assertNotEqual(second_commit,fourth_commit)
self.assertNotEqual(third_commit,fourth_commit)
os.system("git switch master")
fourth2_commit = pygitversionhelper.gitversionhelper.commit.getLast()
self.assertEqual(fourth2_commit,fourth_commit)
fourth3_commit = pygitversionhelper.gitversionhelper.commit.getLast(same_branch=True)
self.assertNotEqual(fourth3_commit,fourth_commit)
self.assertEqual(fourth3_commit,third_commit)
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue5")
os.system("git add .")
os.system("git commit -m \"commit\"")
time.sleep(1)
fifth_commit = pygitversionhelper.gitversionhelper.commit.getLast()
self.assertNotEqual(fifth_commit,fourth3_commit)
os.system("git switch dev")
fifth2_commit = pygitversionhelper.gitversionhelper.commit.getLast()
self.assertEqual(fifth2_commit,fifth_commit)
fifth2_commit = pygitversionhelper.gitversionhelper.commit.getLast(same_branch=True)
self.assertEqual(fifth2_commit,fourth_commit)
def test_defect__commit_notfound(self):
with self.assertRaises(pygitversionhelper.gitversionhelper.commit.commitNotFound) :
pygitversionhelper.gitversionhelper.commit.getLast()
with self.assertRaises(pygitversionhelper.gitversionhelper.commit.commitNotFound) :
pygitversionhelper.gitversionhelper.commit.getLast(same_branch=True)
def test_nominal__commit_getFromTag(self):
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue")
os.system("git add .")
os.system("git commit -m \"commit\"")
initial_commit = pygitversionhelper.gitversionhelper.commit.getLast()
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue2")
os.system("git add .")
os.system("git commit -m \"commit2\"")
os.system(f"git tag TAG")
second_commit = pygitversionhelper.gitversionhelper.commit.getLast()
self.assertNotEqual(second_commit,initial_commit)
second_commit2 = pygitversionhelper.gitversionhelper.commit.getFromTag("TAG")
self.assertEqual(second_commit,second_commit2)
self.assertNotEqual(second_commit2,initial_commit)
def test_defect__commit_notfound2(self):
with self.assertRaises(pygitversionhelper.gitversionhelper.commit.commitNotFound) :
pygitversionhelper.gitversionhelper.commit.getFromTag("TAG")
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue")
os.system("git add .")
os.system("git commit -m \"commit\"")
with self.assertRaises(pygitversionhelper.gitversionhelper.commit.commitNotFound) :
pygitversionhelper.gitversionhelper.commit.getFromTag("TAG")
os.system(f"git tag OTHER_TAG")
with self.assertRaises(pygitversionhelper.gitversionhelper.commit.commitNotFound) :
pygitversionhelper.gitversionhelper.commit.getFromTag("TAG")
def test_nominal__commit_getMessage(self):
commit_message="AAAABBB CCCCDDDD".replace('\r\n','\n').replace('\n','\r\n')
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue")
os.system("git add .")
os.system(f"git commit -m \"{commit_message}\"")
commit = pygitversionhelper.gitversionhelper.commit.getLast()
message = pygitversionhelper.gitversionhelper.commit.getMessage(commit)
self.assertEqual(message,commit_message)
def test_nominal__commit_getMessage2(self):
commit_message="""AAAABBB
CCCCDDDD
-f dfsds dfsdfs $""".replace('\r\n','\n').replace('\n','\r\n')
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue")
os.system("git add .")
cmd = "git commit -m".split()
cmd.append(commit_message)
subprocess.run(cmd,text=True,check=True)
commit = pygitversionhelper.gitversionhelper.commit.getLast()
message = pygitversionhelper.gitversionhelper.commit.getMessage(commit)
self.assertEqual(message,commit_message)
def test_nominal__commit_getMessagesSinceLastTag(self):
commit_message1="1.1 update this"+ os.linesep+\
"1.1 fix that" + os.linesep+\
"1.1 test"
commit_message1=commit_message1.replace('\r\n','\n').replace('\n','\r\n')
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue")
os.system("git add .")
cmd = "git commit -m".split()
cmd.append(commit_message1)
subprocess.run(cmd,text=True,check=True)
os.system(f"git tag 0.1.1")
commit_message2="2.1 update this"+ os.linesep+\
"2.1 fix that" + os.linesep+\
"2.1 test"
commit_message2=commit_message2.replace('\r\n','\n').replace('\n','\r\n')
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue2")
os.system("git add .")
cmd = "git commit -m".split()
cmd.append(commit_message2)
subprocess.run(cmd,text=True,check=True)
commit_message3="3.1 update this"+ os.linesep+\
"3.1 fix that" + os.linesep+\
"3.1 test"
commit_message3=commit_message3.replace('\r\n','\n').replace('\n','\r\n')
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue3")
os.system("git add .")
cmd = "git commit -m".split()
cmd.append(commit_message3)
subprocess.run(cmd,text=True,check=True)
commit_message4="4.1 update this" + os.linesep+\
"4.1 fix that" + os.linesep+\
"4.1 test"
commit_message4=commit_message4.replace('\r\n','\n').replace('\n','\r\n')
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue4")
os.system("git add .")
cmd = "git commit -m".split()
cmd.append(commit_message4)
subprocess.run(cmd,text=True,check=True)
res = pygitversionhelper.gitversionhelper.commit.getMessagesSinceTag("0.1.1")
self.assertEqual( [commit_message4,commit_message3,commit_message2],res)
res = pygitversionhelper.gitversionhelper.commit.getMessagesSinceTag("0.1.1",merged_output=True)
self.assertEqual( os.linesep.join([commit_message4,commit_message3,commit_message2]),res)
def tearDown(self):
os.chdir("/")