Compare commits

...

50 Commits

Author SHA1 Message Date
cclecle
facd431c59 update docgen & cicd tools 2023-09-24 18:56:29 +01:00
cclecle
a07bcc37c4 Fix title 2023-03-28 01:09:46 +01:00
cclecle
0d1e981b6b beautify jenkins file 2023-03-28 01:01:10 +01:00
cclecle
b01779dd06 add missing parentheses 2023-03-28 00:50:43 +01:00
cclecle
f7ec5d98a7 fix changelog multiline 2023-03-28 00:45:48 +01:00
cclecle
82d4b1bd70 fix: remove useless printf in getMessagesSinceTag() 2023-03-28 00:45:00 +01:00
cclecle
880fa29bbd try to get changelog before tag creation 2023-03-28 00:35:00 +01:00
cclecle
7347106694 test printing changelog parts 2023-03-28 00:21:37 +01:00
cclecle
e1c1e643c3 include others changes in changelog 2023-03-28 00:08:18 +01:00
cclecle
a5f39d6cb2 update changelog 2023-03-28 00:04:31 +01:00
cclecle
c6ed7ef0a1 fix wrong typo in jenkinsfile 2023-03-27 23:48:18 +01:00
cclecle
0ef5a8f463 feature: add changelog ! 2023-03-27 23:46:34 +01:00
cclecle
e06a3b00ae fix wrong getMessagesSinceTag() command 2023-03-27 23:27:21 +01:00
cclecle
d3bf6ddaa6 add ignore_merged to git messages 2023-03-27 23:18:42 +01:00
cclecle
a419af2ade add ignore_merged option to commit search 2023-03-27 23:11:16 +01:00
cclecle
5f9d513c59 finish fixing typing
updating and fixing doc
2023-03-27 01:59:15 +01:00
cclecle
5087cf9614 improve typing checks and fix warnings / errors 2023-03-27 01:03:21 +01:00
cclecle
dc322e52da fix quality cmd line 2023-03-26 20:38:00 +01:00
cclecle
a9052f1d71 back port from pychangelogfactory 2023-03-26 20:25:57 +01:00
cclecle
e64dcf4978 improve project configuration from pychangelogfactory 2023-03-25 19:59:20 +00:00
cclecle
a045a47cb0 re-add readme 2023-03-24 22:48:03 +00:00
cclecle
876e428003 remove dynamic link to doc in readme 2023-03-24 22:38:22 +00:00
cclecle
f32127abd7 revert to static readme file... :-/ 2023-03-24 22:12:08 +00:00
cclecle
b20e5bd868 fix 2023-03-24 21:46:51 +00:00
cclecle
f1b1901f8a format + dynamic readme 2023-03-24 21:37:55 +00:00
cclecle
f63a7b76cd format 2023-03-24 21:08:57 +00:00
cclecle
7ce1a04eb9 formating 2023-03-24 20:28:57 +00:00
cclecle
658eef2df6 update readme 2023-03-24 20:25:16 +00:00
cclecle
3d2b8d3762 adjust colours 2023-03-24 20:02:17 +00:00
cclecle
da9fa475e8 fix csv path 2023-03-24 19:53:01 +00:00
cclecle
93d2046c98 set badge colour 2023-03-24 19:51:24 +00:00
cclecle
fa7eaf0769 update badge_maintainability 2023-03-24 19:47:54 +00:00
cclecle
cad73c4711 add complexity check 2023-03-24 19:26:11 +00:00
cclecle
5621ba99f6 start implementation of complexity measurement 2023-03-24 01:09:10 +00:00
cclecle
c49468e294 fix decimal + colours 2023-03-24 00:19:30 +00:00
cclecle
bec5f9234d fix 2023-03-24 00:11:08 +00:00
cclecle
4e50b8d03f add badge colour
score all /10
2023-03-23 23:56:11 +00:00
cclecle
e74a53e3bf fix tostring() method from BigDecimal 2023-03-23 23:47:46 +00:00
cclecle
ef7469f35b aaaaaaa 2023-03-23 23:28:01 +00:00
cclecle
9e5f0de5c4 add badges in readme
fix float value for quality
2023-03-23 23:23:24 +00:00
cclecle
99f38741fd add code quality badge 2023-03-23 23:13:55 +00:00
cclecle
eee2bf551c bvlabnaal 2023-03-23 23:02:57 +00:00
cclecle
01ce809823 add missing import 2023-03-23 22:57:14 +00:00
cclecle
4ab70409a0 switch to BigDecimal grrrrr 2023-03-23 22:49:08 +00:00
cclecle
7b9752a17a cast to decimalformat 2023-03-23 22:47:52 +00:00
cclecle
879a7ca03d fix wrong method name :-/ 2023-03-23 22:42:07 +00:00
cclecle
bd3389aeb2 cast to double + fix complexity 2023-03-23 22:37:45 +00:00
cclecle
886a22bef2 add missing java lib in groovy 2023-03-23 22:23:57 +00:00
cclecle
e9355471ba cast float to strings 2023-03-23 22:19:48 +00:00
cclecle
3bbbe946a1 switch to readfile 2023-03-23 21:54:21 +00:00
22 changed files with 847 additions and 503 deletions

2
.gitignore vendored
View File

@@ -41,3 +41,5 @@ docs
helpers-results helpers-results
.coverage .coverage
/.mypy_cache/ /.mypy_cache/
.coverage
.mypy_cache

View File

@@ -6,7 +6,6 @@
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property> <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH"> <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}/src</path>
<path>/${PROJECT_DIR_NAME}</path> <path>/${PROJECT_DIR_NAME}</path>
</pydev_pathproperty> </pydev_pathproperty>

140
Jenkinsfile vendored
View File

@@ -11,6 +11,8 @@ import static javax.xml.xpath.XPathConstants.*
import javax.xml.xpath.* import javax.xml.xpath.*
import groovy.xml.DOMBuilder import groovy.xml.DOMBuilder
import groovy.xml.dom.DOMCategory import groovy.xml.dom.DOMCategory
import java.math.RoundingMode
import java.math.BigDecimal
// configurable settings: // configurable settings:
// use to send email if workflow problem // use to send email if workflow problem
@@ -24,7 +26,7 @@ def _bPreRelease = false
def _bDraft = false def _bDraft = false
// release content / changelog management // release content / changelog management
def _bAutoChangelog = true //Not supported yet def _bAutoChangelog = true //Not supported yet
def _ReleaseContent_Title = "_CI/CD Automatic Release_" def _ReleaseContent_Title = "# _CI/CD Automatic Release_"
def bPushMasterOnPypi = true def bPushMasterOnPypi = true
// full rebuild toogle // full rebuild toogle
def _bFullRebuilt = true def _bFullRebuilt = true
@@ -33,8 +35,9 @@ def _MkDocsWebCredentials = "2c5b684e-3787-4b37-8aca-b3dd4a383fe2"
def _PypiCredentials = "Pypi" def _PypiCredentials = "Pypi"
def badge_coverage = addEmbeddableBadgeConfiguration(id: "coverage", subject: "unit-test coverage") def badge_coverage = addEmbeddableBadgeConfiguration(id: "coverage", subject: "coverage")
def badge_complexity = addEmbeddableBadgeConfiguration(id: "complexity", subject: "code complexity") def badge_maintainability = addEmbeddableBadgeConfiguration(id: "maintainability", subject: "maintainability")
def badge_quality = addEmbeddableBadgeConfiguration(id: "quality", subject: "quality score")
// commands Helper: /!\ Made for GITEA /!\ // commands Helper: /!\ Made for GITEA /!\
String determineRepoUserName() { String determineRepoUserName() {
@@ -73,8 +76,9 @@ String ExtractBaseVersion(inVersion) {
int GetCoverageValue(String CoverageFilePath,String XPath) int GetCoverageValue(String CoverageFilePath,String XPath)
{ {
File file = new File(CoverageFilePath) //File file = new File(CoverageFilePath)
coverageReportRaw = file.getText('UTF-8') //coverageReportRaw = file.getText('UTF-8')
coverageReportRaw = readFile(CoverageFilePath)
coverageReport = DOMBuilder.parse(new StringReader(coverageReportRaw), false, false) coverageReport = DOMBuilder.parse(new StringReader(coverageReportRaw), false, false)
coverageReportRoot = coverageReport.documentElement coverageReportRoot = coverageReport.documentElement
@@ -83,13 +87,31 @@ int GetCoverageValue(String CoverageFilePath,String XPath)
return res return res
} }
String getColorScale(BigDecimal value)
{
if( value >9) { return "Goldenrod"}
else if( value >6) { return "seagreen"}
else if( value >4) { return "orange"}
else if( value >2) { return "darkred"}
else { return "dimgrey"}
}
String getColorScale_reversed(BigDecimal value)
{
if( value >9) { return "dimgrey"}
else if( value >6) { return "darkred"}
else if( value >4) { return "orange"}
else if( value >2) { return "seagreen"}
else { return "Goldenrod"}
}
int GetCoverageValue_lines_valid(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@lines-valid") } int GetCoverageValue_lines_valid(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@lines-valid") }
int GetCoverageValue_lines_covered(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@lines-covered") } int GetCoverageValue_lines_covered(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@lines-covered") }
int GetCoverageValue_line_rate(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@line-rate") } int GetCoverageValue_line_rate(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@line-rate") }
int GetCoverageValue_branches_valid(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@branches-valid") } int GetCoverageValue_branches_valid(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@branches-valid") }
int GetCoverageValue_branches_covered(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@branches-covered") } int GetCoverageValue_branches_covered(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@branches-covered") }
int GetCoverageValue_branch_rate(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@branch-rate") } int GetCoverageValue_branch_rate(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@branch-rate") }
int GetCoverageValue_branches_complexity(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@branches-complexity") } int GetCoverageValue_complexity(String CoverageFilePath) { return GetCoverageValue(CoverageFilePath,"/coverage/@complexity") }
pipeline { pipeline {
@@ -126,6 +148,7 @@ pipeline {
PY_PROJECT_NAME = "__NOTSET__" PY_PROJECT_NAME = "__NOTSET__"
PY_PROJECT_VERSION = "__NOTSET__" PY_PROJECT_VERSION = "__NOTSET__"
PY_PROJECT_VERSION_STRIPPED = "__NOTSET__" PY_PROJECT_VERSION_STRIPPED = "__NOTSET__"
CHANGELOG = "__NOTSET__"
} }
stages { stages {
@@ -162,16 +185,14 @@ pipeline {
sh(". ~/BUILD_ENV/bin/activate && pip install --upgrade setuptools build pip copier jinja2-slug toml") sh(". ~/BUILD_ENV/bin/activate && pip install --upgrade setuptools build pip copier jinja2-slug toml")
sh(". ~/TOOLS_ENV/bin/activate && pip install simple_rest_client requests twine") sh(". ~/TOOLS_ENV/bin/activate && pip install simple_rest_client requests twine packaging")
script { script {
if(_PROJECT_NAME!="pygitversionhelper") { if(_PROJECT_NAME!="pygitversionhelper") {
sh(". ~/TOOLS_ENV/bin/activate && pip install pygitversionhelper") sh(". ~/TOOLS_ENV/bin/activate && pip install pygitversionhelper")
} }
else if(_PROJECT_NAME!="pychangelogfactory") {
{ sh(". ~/TOOLS_ENV/bin/activate && pip install pychangelogfactory")
//TODO: need to install pygitversionhelper deps from a better way...
sh(". ~/TOOLS_ENV/bin/activate && pip install packaging")
} }
} }
sh("git config --global user.email $_MaintainerEmail") sh("git config --global user.email $_MaintainerEmail")
@@ -195,6 +216,32 @@ pipeline {
withCredentials([usernamePassword(credentialsId: _SCMCredentials, passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) { withCredentials([usernamePassword(credentialsId: _SCMCredentials, passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
sh("git remote set-url origin https://${GIT_USERNAME}:${GIT_PASSWORD}@chacha.ddns.net/gitea/${_PROJECT_USER_NAME}/${_PROJECT_NAME}.git") sh("git remote set-url origin https://${GIT_USERNAME}:${GIT_PASSWORD}@chacha.ddns.net/gitea/${_PROJECT_USER_NAME}/${_PROJECT_NAME}.git")
} }
CHANGELOG = sh(script: """#!/bin/sh -
|. ~/TOOLS_ENV/bin/activate
|exec python - << '__EOWRAPPER__'
|
|import re
|
|try:
| from pychangelogfactory import ChangelogFactory
|except ImportError:
| from src.pychangelogfactory import ChangelogFactory
|
|try:
| from pygitversionhelper import gitversionhelper
|except ImportError:
| from src.pygitversionhelper import gitversionhelper
|
|
|LastTag=gitversionhelper.tag.getLastTag(same_branch=True)
|CommitHistory=gitversionhelper.commit.getMessagesSinceTag(LastTag, merged_output=True, ignore_merged=True)
|Changelog = ChangelogFactory(CommitHistory).RenderFullChangelog(include_unknown=True)
|print(Changelog.replace("\\n","\\n\\n"))
|
|__EOWRAPPER__
""".stripMargin(),
returnStdout: true).trim()
if(_GIT_BRANCH=="master") { if(_GIT_BRANCH=="master") {
if(sh(returnStdout: true, script: "git tag --points-at HEAD").trim().isEmpty()) { if(sh(returnStdout: true, script: "git tag --points-at HEAD").trim().isEmpty()) {
@@ -353,7 +400,7 @@ pipeline {
def wheelPath = findFiles(glob: "**/dist/*.whl")[0] def wheelPath = findFiles(glob: "**/dist/*.whl")[0]
echo "wheel artifact path: $wheelPath" echo "wheel artifact path: $wheelPath"
// install the package, with *test* optionnal packages, as user // install the package, with *test* optionnal packages, as user
sh(". ~/TEST_ENV/bin/activate && pip install --find-links dist/ ${PY_PROJECT_NAME} .[test,coverage-check,quality-check,type-check,doc-gen]") sh(". ~/TEST_ENV/bin/activate && pip install --find-links dist/ ${PY_PROJECT_NAME} .[test,coverage-check,quality-check,type-check,doc-gen,complexity-check]")
} }
} }
} }
@@ -363,18 +410,35 @@ pipeline {
steps { steps {
dir("gitrepo") { dir("gitrepo") {
sh(". ~/TEST_ENV/bin/activate && python -m helpers --type-check --quality-check") sh(". ~/TEST_ENV/bin/activate && python -m helpers --type-check --quality-check")
script {
def jsonObj = readJSON file: "helpers-results/quality_check/metrics.json"
quality_score = new BigDecimal(jsonObj["GlobalScore"])
sz_quality_score = quality_score.setScale(2, RoundingMode.HALF_EVEN).toString()
badge_quality.setStatus(sz_quality_score)
badge_quality.setColor(getColorScale(quality_score))
}
sh(". ~/TEST_ENV/bin/activate && python -m helpers --complexity-check")
} }
} }
post { post {
always { always {
dir("gitrepo") { dir("gitrepo") {
publishHTML([ publishCoverage adapters: [cobertura(mergeToOneReport: true, path: "helpers-results/types_check/cobertura.xml")]
reportDir: "helpers-results/quality_check", junit 'helpers-results/types_check/junit.xml'
reportFiles: "report.html", publishHTML([
reportName: "quality-report", reportDir: "helpers-results/quality_check",
allowMissing: false, reportFiles: "report.html",
alwaysLinkToLastBuild: true, reportName: "quality-report",
keepAll: true]) allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true])
publishHTML([
reportDir: "helpers-results/types_check",
reportFiles: "index.html",
reportName: "types_check",
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true])
} }
} }
} }
@@ -417,6 +481,14 @@ pipeline {
style: 'stackedArea', style: 'stackedArea',
keepRecords: true, keepRecords: true,
numBuilds: '']) numBuilds: ''])
plot([ csvFileName: 'plot-4ceb9ee2-ca78-11ed-afa1-0242ac120002.csv',
csvSeries: [[ file: 'gitrepo/helpers-results/complexity_check/MI.csv', inclusionFlag: 'INCLUDE_BY_STRING',exclusionValues: 'MeanMaintainability', url: '']],
group: 'metrics',
title: 'maintainability',
style: 'stackedArea',
keepRecords: true,
numBuilds: ''])
} }
} }
@@ -437,13 +509,23 @@ pipeline {
println GetCoverageValue_branches_valid(coverage_report_path) println GetCoverageValue_branches_valid(coverage_report_path)
println GetCoverageValue_branches_covered(coverage_report_path) println GetCoverageValue_branches_covered(coverage_report_path)
println GetCoverageValue_branch_rate(coverage_report_path) println GetCoverageValue_branch_rate(coverage_report_path)
println GetCoverageValue_branches_complexity(coverage_report_path) println GetCoverageValue_complexity(coverage_report_path)
full_rate = (GetCoverageValue_line_rate(coverage_report_path) + GetCoverageValue_branch_rate(coverage_report_path)) / 2 full_rate = new BigDecimal( 10*(GetCoverageValue_line_rate(coverage_report_path) + GetCoverageValue_branch_rate(coverage_report_path)) / 2 )
badge_coverage.setStatus(full_rate) sz_full_rate = full_rate.setScale(2, RoundingMode.HALF_EVEN).toString()
badge_coverage.setStatus(sz_full_rate)
badge_coverage.setColor(getColorScale(full_rate))
//badge_maintainability
records = readCSV file: 'helpers-results/complexity_check/MI.csv'
maintainability = records[1][1]
badge_maintainability.setStatus(maintainability)
complexity = GetCoverageValue_branches_complexity(coverage_report_path) if ( maintainability == 'D') { badge_maintainability.setColor( "dimgrey")}
badge_complexity.setStatus(complexity) else if( maintainability == 'C') { badge_maintainability.setColor( "darkred")}
else if( maintainability == 'B') { badge_maintainability.setColor( "orange")}
else if( maintainability == 'A') { badge_maintainability.setColor( "seagreen")}
else if( maintainability == 'A+') { badge_maintainability.setColor( "Goldenrod")}
} }
} }
} }
@@ -516,6 +598,7 @@ pipeline {
|from simple_rest_client.api import API |from simple_rest_client.api import API
|from simple_rest_client.resource import Resource |from simple_rest_client.resource import Resource
| |
|
|try: |try:
| from pygitversionhelper import gitversionhelper | from pygitversionhelper import gitversionhelper
|except ImportError: |except ImportError:
@@ -562,6 +645,13 @@ pipeline {
|ReleaseContent = "${_ReleaseContent_Title}" + "\\n" \\ |ReleaseContent = "${_ReleaseContent_Title}" + "\\n" \\
| + "\\n" \\ | + "\\n" \\
| + "Reference documentation: [mkdocs page](https://chacha.ddns.net/mkdocs-web/${_PROJECT_USER_NAME}/${PY_PROJECT_NAME}/${_GIT_BRANCH}/${PY_PROJECT_VERSION_STRIPPED}/) " | + "Reference documentation: [mkdocs page](https://chacha.ddns.net/mkdocs-web/${_PROJECT_USER_NAME}/${PY_PROJECT_NAME}/${_GIT_BRANCH}/${PY_PROJECT_VERSION_STRIPPED}/) "
|
|Changelog='''${CHANGELOG}'''
|
|ReleaseContent = ReleaseContent + "\\n"+ "\\n"+ "## Changelog:\\n" + Changelog
|
|if not Changelog:
| ReleaseContent = ReleaseContent + "code/project maintainance"
| |
|data={ |data={
| "body": ReleaseContent, | "body": ReleaseContent,

View File

@@ -1,5 +1,10 @@
![](https://chacha.ddns.net/jenkins/buildStatus/icon?subject=status&status=active&color=seagreen)
![](https://chacha.ddns.net/jenkins/buildStatus/icon?subject=doc&status=MkDocs&color=blue)
![](https://chacha.ddns.net/jenkins/buildStatus/icon?subject=jenkins-unittest&job={{repository}}-{{branch}}) ![](https://chacha.ddns.net/jenkins/buildStatus/icon?subject=jenkins-unittest&job={{repository}}-{{branch}})
![](https://chacha.ddns.net/jenkins/buildStatus/icon?subject=licence&status=CC%20BY-NC-SA%204.0&color=blue) ![](https://chacha.ddns.net/jenkins/buildStatus/icon?job={{repository}}-{{branch}}&build=0&config=coverage)
![](https://chacha.ddns.net/jenkins/buildStatus/icon?job={{repository}}-{{branch}}&build=0&config=maintainability)
![](https://chacha.ddns.net/jenkins/buildStatus/icon?job={{repository}}-{{branch}}&build=0&config=quality)
![](https://chacha.ddns.net/jenkins/buildStatus/icon?subject=licence&status=CC%20BY-NC-SA%204.0&color=teal)
![](docs-static/Library.jpg) ![](docs-static/Library.jpg)
@@ -9,8 +14,7 @@ _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. 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/master/latest/).
Checkout [Latest Documentation](https://chacha.ddns.net/mkdocs-web/chacha/pygitversionhelper/{{branch}}/latest/).
## Features ## Features
- list tags - list tags
@@ -19,7 +23,8 @@ Checkout [Latest Documentation](https://chacha.ddns.net/mkdocs-web/chacha/pygitv
- get current version (bumped) - get current version (bumped)
- convert / switch from SemVer to PEP440 (both ways) - convert / switch from SemVer to PEP440 (both ways)
- automatic version format detection (SemVer by default) - automatic version format detection (SemVer by default)
- get commit message history
## Options ## Options
- restrict to same branch - restrict to same branch
- both SemVer and PEP440 support - both SemVer and PEP440 support

16
RUN_changelog.launch Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/${project_name}/helpers"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="2"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${project_loc}/helpers"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--changelog-gen"/>
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="pygitversionhelper"/>
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
</launchConfiguration>

16
RUN_complexity.launch Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/${project_name}/helpers"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="2"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${project_loc}/helpers"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--complexity-check"/>
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="pygitversionhelper"/>
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
</launchConfiguration>

19
RUN_mkdocs.launch Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/${project_name}/helpers"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="2"/>
</listAttribute>
<mapAttribute key="org.eclipse.debug.core.environmentVariables">
<mapEntry key="PATH" value="C:\Program Files\GTK3-Runtime Win64\bin"/>
</mapAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${project_loc}/helpers"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--doc-gen --doc-gen-pdf"/>
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="pygitversionhelper"/>
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
</launchConfiguration>

16
RUN_quality.launch Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/${project_name}/helpers"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="2"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${project_loc}/helpers"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--type-check --quality-check"/>
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="pygitversionhelper"/>
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
</launchConfiguration>

16
RUN_unittest.launch Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/${project_name}/helpers"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="2"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${project_loc}/helpers"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--unit-test --coverage-check"/>
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="pygitversionhelper"/>
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
</launchConfiguration>

View File

@@ -3,30 +3,32 @@
## Installation ## Installation
From pypi repository (prefered): From pypi repository (prefered):
```console
python -m pip install pygitversionhelper /> python -m pip install pygitversionhelper
```
From downloaded .whl file: From downloaded .whl file:
```console
/> python -m pip install pygitversionhelper-<VERSION>-py3-none-any.whl
```
python -m pip install pygitversionhelper-<VERSION>-py3-none-any.whl
From master git repository: From master git repository:
```console
python -m pip install git+https://chacha.ddns.net/gitea/chacha/pygitversionhelper.git@master /> python -m pip install git+https://chacha.ddns.net/gitea/chacha/pygitversionhelper.git@master
```
## Import in your project ## Import in your project
Add this line on the top of your python script: Add this line on the top of your python script:
```py
#from pygitversionhelper import gitversionhelper from pygitversionhelper import gitversionhelper
```
[optionnal] If you need to catch exception from this module: [optionnal] If you need to catch exception from this module:
```py
#from pygitversionhelper import gitversionhelperException from pygitversionhelper import gitversionhelperException
```
## Basic API ## Basic API
All the API commands are static 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.
@@ -34,93 +36,100 @@ All the API commands are static so it is not needed to create instantiate any ob
They are all executed in the current active directory. They are all executed in the current active directory.
One easy way to change directory: One easy way to change directory:
```py
import os import os
os.chdir("<YOUR DIRECTORY>") os.chdir("<YOUR DIRECTORY>")
```
### sublib: repository ### sub-lib: repository
To check if a repository is dirty: To check if a repository is dirty:
```py
if gitversionhelper.repository.isDirty(): if gitversionhelper.repository.isDirty():
print("repository is dirty") print("repository is dirty")
```
### sublib: tag ### sub-lib: tag
List all tags [default to taggerdate order]: List all tags [default to taggerdate order]:
```py
for tag in gitversionhelper.tag.getTags(): for tag in gitversionhelper.tag.getTags():
print(f"found tag: {tag}") print(f"found tag: {tag}")
```
List all tags [using git refname order]: List all tags [using git refname order]:
```py
for tag in gitversionhelper.tag.getTags("v:refname"): for tag in gitversionhelper.tag.getTags("v:refname"):
print(f"found tag: {tag}") print(f"found tag: {tag}")
```
Get the last tag: Get the last tag:
```py
print(f"most recent repository tag: {gitversionhelper.tag.getLastTag()}") 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]:
```py
print(f"most recent repository tag: {gitversionhelper.tag.getLastTag(same_branch=True)}") print(f"most recent repository tag: {gitversionhelper.tag.getLastTag(same_branch=True)}")
```
Get the distance from HEAD to last tag: Get the distance from HEAD to last tag:
```py
print(f"number of commit since last tag: {gitversionhelper.tag.getDistanceFromTag()}") 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]:
```py
print(f"number of commit since last tag: {gitversionhelper.tag.getDistanceFromTag(same_branch=True)}") print(f"number of commit since last tag: {gitversionhelper.tag.getDistanceFromTag(same_branch=True)}")
```
### sublib: version ### sub-lib: version
Get the last found version in the repository [return MetaVersion object]: Get the last found version in the repository [return MetaVersion object]:
```py
print(f"most recent repository version: {gitversionhelper.version.getLastVersion()}") print(f"most recent repository version: {gitversionhelper.version.getLastVersion()}")
```
Get the last found version in the repository [return formated string]: Get the last found version in the repository [return formated string]:
```py
print(f"most recent repository version: {gitversionhelper.version.getLastVersion(formated_output=True)}") print(f"most recent repository version: {gitversionhelper.version.getLastVersion(formated_output=True)}")
```
Others kwargs available to this function: 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 * same_branch: boolean to force searching on same branch
* ignore\_unknown\_tags: boolean to allow unknown tag to be ignored * ignore\_unknown\_tags: boolean to allow unknown tag to be ignored
Get the current version of the repository, automatically bump it if the last one is not tagged [returns MetaVersion object]: Get the current version of the repository, automatically bump it if the last commit is not tagged [returns MetaVersion object]:
```py
print(f"current repository version: {gitversionhelper.version.getCurrentVersion()}") print(f"current repository version: {gitversionhelper.version.getCurrentVersion()}")
```
Or with formated output: Or with formated output:
```py
print(f"current repository version: {gitversionhelper.version.getCurrentVersion(formated_output=True)}") print(f"current repository version: {gitversionhelper.version.getCurrentVersion(formated_output=True)}")
```
Typical usage in CI/CD env: Typical usage in CI/CD env:
```py
bumped_version = gitversionhelper.version.getCurrentVersion( formated_output=True, \ bumped_version = gitversionhelper.version.getCurrentVersion( formated_output=True, \
version_std="PEP440", \ version_std="PEP440", \
bump_type="dev", \ bump_type="dev", \
bump_dev_strategy="post") bump_dev_strategy="post")
print(f"current repository version: {bumped_version}") print(f"current repository version: {bumped_version}")
```
kwargs available to this function: kwargs available to this function:
* All same args as getLastVersion() * All same args as getLastVersion()
* bump_type: if version need to be pump, allow to configure next release update type: __major, minor, patch, dev__ * bump_type: if version need to be pump, allow to configure next release update type: __major, minor, patch, dev__
* bump\_dev\_strategy: if bump\_type is dev, allow to choose dev update strategy: __post, pre-patch, pre-minor, pre-major__ * bump\_dev\_strategy: if bump\_type is dev, allow to choose dev update strategy: __post, pre-patch, pre-minor, pre-major__
A version object can also be manually formated: A version object can also be manually formated:
```py
_version = gitversionhelper.tag.getCurrentVersion()
```
Then;
```py
_version.doFormatVersion()
```
or;
```py
gitversionhelper.version.doFormatVersion(_version)
```
kwargs available to this function:
_version = gitversionhelper.tag.getCurrentVersion() - output_format: string to choose a rendering format ["Auto","PEP440" or "SemVer"]
_version.doFormatVersion()
#or
gitversionhelper.version.doFormatVersion(_version)
kwargs available to those function:
- output_format: string to choose a rendering format ["Auto","PEP440" or "SemVer"]
## Limitations ## Limitations

View File

@@ -22,14 +22,14 @@ if __package__ == "helpers":
from .quality_check import quality_check from .quality_check import quality_check
from .unit_test import unit_test from .unit_test import unit_test
from .doc_gen import doc_gen from .doc_gen import doc_gen
from .changelog_gen import changelog_gen from .complexity_check import complexity_check
else: else:
# when calling the __main__.py file (from IDE) # when calling the __main__.py file (from IDE)
from helpers.types_check import types_check from helpers.types_check import types_check
from helpers.quality_check import quality_check from helpers.quality_check import quality_check
from helpers.unit_test import unit_test from helpers.unit_test import unit_test
from helpers.doc_gen import doc_gen from helpers.doc_gen import doc_gen
from helpers.changelog_gen import changelog_gen from helpers.complexity_check import complexity_check
logging.getLogger().setLevel(logging.INFO) logging.getLogger().setLevel(logging.INFO)
@@ -57,7 +57,7 @@ if __name__ == "__main__":
"-pdf", "--doc-gen-pdf", dest="docgenpdf", action="store_true", help="enable pdf documentation export (requires doc-gen)" "-pdf", "--doc-gen-pdf", dest="docgenpdf", action="store_true", help="enable pdf documentation export (requires doc-gen)"
) )
parser.add_argument("-clg", "--changelog-gen", dest="changeloggen", action="store_true", help="enable changelog generation") parser.add_argument("-cpc", "--complexity-check", dest="complexitycheck", action="store_true", help="enable complexity check")
args = parser.parse_args() args = parser.parse_args()
@@ -72,7 +72,7 @@ if __name__ == "__main__":
# args.coveragecheck = True # args.coveragecheck = True
# args.docgen = True # args.docgen = True
# args.docgenpdf = True # args.docgenpdf = True
# args.changeloggen = True # args.complexitycheck = True
helpers = [] helpers = []
if args.typecheck == True: if args.typecheck == True:
@@ -99,8 +99,8 @@ if __name__ == "__main__":
else: else:
raise RuntimeError("doc-gen is required to enable doc-gen-pdf") raise RuntimeError("doc-gen is required to enable doc-gen-pdf")
if args.changeloggen == True: if args.complexitycheck == True:
helpers.append(changelog_gen) helpers.append(complexity_check)
for helper in helpers: for helper in helpers:
helper.set_context(project_rootdir_path, pyproject) helper.set_context(project_rootdir_path, pyproject)

View File

@@ -1,23 +0,0 @@
# pyChaChaDummyProject (c) by chacha
#
# pyChaChaDummyProject is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Unported License.
#
# 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/>.
from __future__ import annotations
from typing import TYPE_CHECKING
# from pathlib import Path
# import os
# import datetime
from .helper_base import helper_base
class changelog_gen(helper_base):
@classmethod
def do_job(cls):
pass

View File

@@ -0,0 +1,70 @@
# pyChaChaDummyProject (c) by chacha
#
# pyChaChaDummyProject is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Unported License.
#
# 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/>.
from __future__ import annotations
from typing import TYPE_CHECKING
# from pathlib import Path
# import os
import statistics
import csv
from json import loads as JSON_LOADS
from radon.complexity import cc_rank, SCORE
from radon.cli import Config
from radon.cli.harvest import CCHarvester, HCHarvester, MIHarvester
from .helper_base import helper_withresults_base
from pprint import pprint
class complexity_check(helper_withresults_base):
@classmethod
def do_job(cls):
config = Config(
exclude="__init__\.py",
ignore=None,
order=SCORE,
show_closures=False,
no_assert=True,
min="A",
max="F",
multi=False,
)
h = MIHarvester([str(_) for _ in sorted((cls.project_rootdir_path / "src").rglob("*.py"))], config).as_json()
res = JSON_LOADS(h)
with open(cls.get_result_dir() / "MI.json", "w", newline="") as oFile:
oFile.write(h)
mean = statistics.mean(_["mi"] for _ in res.values())
if mean >= 65:
rank = "A+"
elif mean >= 20:
rank = "A"
elif mean >= 10:
rank = "B"
else:
rank = "C"
RES_MI = {"MeanMaintainability": mean, "MaintainabilityIndex": rank}
with open(cls.get_result_dir() / "MI.csv", "w", newline="") as oFile:
writer = csv.DictWriter(oFile, fieldnames=RES_MI.keys())
writer.writeheader()
writer.writerow(RES_MI)
config = Config(exclude=None, ignore=None, order=SCORE, show_closures=False, no_assert=True, min="A", max="F", multi=False)
h = CCHarvester([str(_) for _ in sorted((cls.project_rootdir_path / "src").rglob("*.py"))], config).as_json()
with open(cls.get_result_dir() / "CC.json", "w", newline="") as oFile:
oFile.write(h)
config = Config(exclude=None, ignore=None, order=SCORE, show_closures=False, no_assert=True, min="A", max="F", by_function=None)
h = HCHarvester([str(_) for _ in sorted((cls.project_rootdir_path / "src").rglob("*.py"))], config).as_json()
with open(cls.get_result_dir() / "HC.json", "w", newline="") as oFile:
oFile.write(h)

View File

@@ -31,8 +31,6 @@ class doc_gen(helper_withresults_base):
@classmethod @classmethod
def do_job(cls): def do_job(cls):
print(cls.project_rootdir_path)
print()
# create doc root dir # create doc root dir
doc_path = cls.project_rootdir_path / "docs" doc_path = cls.project_rootdir_path / "docs"
@@ -52,6 +50,7 @@ class doc_gen(helper_withresults_base):
reference_path = doc_path / "reference" reference_path = doc_path / "reference"
cls._reset_dir(reference_path) cls._reset_dir(reference_path)
# create one .md per python module
for path in sorted((cls.project_rootdir_path / "src").rglob("*.py")): for path in sorted((cls.project_rootdir_path / "src").rglob("*.py")):
module_path = path.relative_to(cls.project_rootdir_path / "src").with_suffix("") module_path = path.relative_to(cls.project_rootdir_path / "src").with_suffix("")
doc_path = path.relative_to(cls.project_rootdir_path / "src").with_suffix(".md") doc_path = path.relative_to(cls.project_rootdir_path / "src").with_suffix(".md")
@@ -59,14 +58,12 @@ class doc_gen(helper_withresults_base):
parts = list(module_path.parts) parts = list(module_path.parts)
if parts[-1] == "__init__": if parts[-1] in ("__init__", "__main__"):
parts = parts[:-1]
elif parts[-1] == "__main__":
continue continue
cls._reset_dir(os.path.dirname(full_doc_path)) cls._create_dir(full_doc_path.parent.resolve())
with open(full_doc_path, "w+") as fd: with open(full_doc_path, "w+") as fd:
identifier = "src." + ".".join(parts) identifier = ".".join(parts)
print("::: " + identifier, file=fd) print("::: " + identifier, file=fd)
cmdopts = [f"{sys.executable}", "-m", "mkdocs", "-v", "build", "--site-dir", str(site_path), "--clean"] cmdopts = [f"{sys.executable}", "-m", "mkdocs", "-v", "build", "--site-dir", str(site_path), "--clean"]
@@ -74,9 +71,11 @@ class doc_gen(helper_withresults_base):
# little hack here, to enable / disable pdf generation using own class config # little hack here, to enable / disable pdf generation using own class config
# => reason is mkdocs seems to try loading the plugin even if we disable it, so we need to # => reason is mkdocs seems to try loading the plugin even if we disable it, so we need to
# manually process the configuration file. # manually process the configuration file.
mkdocsCfg = None
with open(cls.project_rootdir_path / "mkdocs.yml", "r") as mkdocsCfgFile: with open(cls.project_rootdir_path / "mkdocs.yml", "r") as mkdocsCfgFile:
mkdocsCfg = yaml.load(mkdocsCfgFile, Loader=yaml.SafeLoader) mkdocsCfg = yaml.load(mkdocsCfgFile, Loader=yaml.Loader)
if "plugins" in mkdocsCfg:
mkdocsCfg["plugins"] = [_ for _ in mkdocsCfg["plugins"] if (not isinstance(_, dict) or "with-pdf" not in _.keys())]
if cls.enable_gen_pdf == True: if cls.enable_gen_pdf == True:
mkdocsCfg["plugins"].append( mkdocsCfg["plugins"].append(
@@ -85,22 +84,15 @@ class doc_gen(helper_withresults_base):
"cover_subtitle": "User Manual", "cover_subtitle": "User Manual",
"cover_logo": str(cls.project_rootdir_path / "docs-static" / "Library.jpg"), "cover_logo": str(cls.project_rootdir_path / "docs-static" / "Library.jpg"),
"verbose": False, "verbose": False,
"media_type": "print",
"exclude_pages": ["LICENSE"], "exclude_pages": ["LICENSE"],
"output_path": str(site_path / "pdf" / "manual.pdf"), "output_path": str(site_path / "pdf" / "manual.pdf"),
} }
} }
) )
else:
for subelem in mkdocsCfg["plugins"]:
if isinstance(subelem, dict):
if "with-pdf" in subelem.keys():
mkdocsCfg["plugins"].remove(subelem)
break
with open(cls.project_rootdir_path / "mkdocs.yml", "w") as mkdocsCfgFile: with open(cls.project_rootdir_path / "mkdocs.yml", "w") as mkdocsCfgFile:
mkdocsCfgFile.write(yaml.dump(mkdocsCfg, Dumper=Dumper, default_flow_style=False, sort_keys=False)) mkdocsCfgFile.write(yaml.dump(mkdocsCfg, Dumper=Dumper, default_flow_style=False, sort_keys=False))
print(" !! start doc generation")
res = cls.run_cmd(cmdopts) res = cls.run_cmd(cmdopts)
print(res.decode()) print(res.decode())
print(" !! done") print(" !! done")

View File

@@ -11,6 +11,7 @@ from typing import TYPE_CHECKING
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import os import os
import shutil
from pathlib import Path from pathlib import Path
import subprocess import subprocess
@@ -34,11 +35,17 @@ class helper_base(ABC):
return None return None
@staticmethod @staticmethod
def _reset_dir(dirpath: Path): def _create_dir(dirpath: Path):
dirpath = Path(dirpath) dirpath = Path(dirpath)
if not os.path.exists(dirpath): if not os.path.exists(dirpath):
os.makedirs(dirpath) os.makedirs(dirpath)
[f.unlink() for f in Path(dirpath).glob("*") if f.is_file()]
@staticmethod
def _reset_dir(dirpath: Path):
dirpath = Path(dirpath)
if os.path.exists(dirpath):
shutil.rmtree(dirpath)
os.makedirs(dirpath)
@classmethod @classmethod
def reset_result_dir(cls): def reset_result_dir(cls):
@@ -57,10 +64,11 @@ class helper_base(ABC):
return process.stdout return process.stdout
@classmethod @classmethod
def run_cmd(cls, cmdarray): def run_cmd(cls, cmdarray, silent: bool = False):
p = subprocess.run(cmdarray, capture_output=True) p = subprocess.run(cmdarray, capture_output=True)
print(p.stdout) if not silent:
print(p.stderr) print(p.stdout.decode())
print(p.stderr.decode())
return p.stdout return p.stdout

View File

@@ -37,7 +37,7 @@ class quality_check(helper_withresults_base):
def GetPylintMessageList(cls): def GetPylintMessageList(cls):
Messagelist = dict() Messagelist = dict()
regex = r"^:([a-zA-Z-]+) \(([^\)]+)\)" regex = r"^:([a-zA-Z-]+) \(([^\)]+)\)"
for line in cls.run_cmd([sys.executable, "-m", "pylint", "--list-msgs"]).splitlines(): for line in cls.run_cmd([sys.executable, "-m", "pylint", "--list-msgs"], True).splitlines():
if res := re.search(regex, line.decode()): if res := re.search(regex, line.decode()):
Messagelist[res.group(1)] = res.group(2) Messagelist[res.group(1)] = res.group(2)
cls.PylintMessageList = Messagelist cls.PylintMessageList = Messagelist
@@ -52,6 +52,7 @@ class quality_check(helper_withresults_base):
@classmethod @classmethod
def do_job(cls): def do_job(cls):
print("checking code quality ...") print("checking code quality ...")
cls.GetPylintMessageList() cls.GetPylintMessageList()
RES_all = dict() RES_all = dict()
@@ -60,8 +61,9 @@ class quality_check(helper_withresults_base):
with redirect_stdout(StdOutput): with redirect_stdout(StdOutput):
pylint_Run( pylint_Run(
[ [
"--load-plugins=pylint.extensions.mccabe",
"--output-format=json,parseable", "--output-format=json,parseable",
"--disable=invalid-name", "--disable=invalid-name,too-few-public-methods,too-many-arguments", # ignore
"--ignore=_version.py", "--ignore=_version.py",
"--reports=y", "--reports=y",
"--score=yes", "--score=yes",

View File

@@ -24,22 +24,18 @@ class types_check(helper_withresults_base):
print("checking code typing ...") print("checking code typing ...")
result = api.run( result = api.run(
[ # project path [ # project path
"-m", "-p",
"src." + str(cls.pyproject["project"]["name"]), "src." + cls.pyproject["project"]["name"],
# analysis configuration # analysis configuration
"--ignore-missing-imports", # "--show-traceback",
"--strict-equality", "--explicit-package-bases",
# "--strict-equality",
# "--check-untyped-defs",
# reports generation # reports generation
"--cobertura-xml-report", "--cobertura-xml-report",
str(cls.get_result_dir()), str(cls.get_result_dir()),
"--html-report", "--html-report",
str(cls.get_result_dir()), str(cls.get_result_dir()),
"--linecount-report",
str(cls.get_result_dir()),
"--linecoverage-report",
str(cls.get_result_dir()),
"--lineprecision-report",
str(cls.get_result_dir()),
"--txt-report", "--txt-report",
str(cls.get_result_dir()), str(cls.get_result_dir()),
"--xml-report", "--xml-report",
@@ -52,6 +48,9 @@ class types_check(helper_withresults_base):
if result[0]: if result[0]:
print("\nType checking report:\n") print("\nType checking report:\n")
print(result[0]) # stdout print(result[0]) # stdout
# converting the report using pylint_json2html (/!\ internal API, but as their is no leading '_' ...)
with open(cls.get_result_dir() / "raw_eport.txt", "w+", encoding="utf-8") as Outfile:
Outfile.write(result[0])
if result[1]: if result[1]:
print("\nError report:\n") print("\nError report:\n")

View File

@@ -1,92 +1,121 @@
# pyChaChaDummyProject (c) by chacha
#
# pyChaChaDummyProject is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Unported License.
#
# 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/>.
docs_dir: docs docs_dir: docs
site_name: pygitversionhelper site_name: pygitversionhelper
site_url: 'https://chacha.ddns.net/mkdocs-web/chacha/pygitversionhelper/latest/' site_url: https://chacha.ddns.net/mkdocs-web/chacha/pygitversionhelper/latest/
site_description: pygitversionhelper site_description: A simple simple git version helper in python.
site_author: ChaCha site_author: prune
repo_url: 'https://chacha.ddns.net/gitea/chacha/pygitversionhelper' repo_url: https://chacha.ddns.net/gitea/chacha/pygitversionhelper
use_directory_urls: false use_directory_urls: false
copyright: CC BY-NC-SA 4.0 copyright: CC BY-NC-SA 4.0
theme: theme:
name: material name: material
features: features:
- navigation.instant - navigation.instant
- navigation.tracking - navigation.tracking
- navigation.tabs - navigation.tabs
- navigation.tabs.sticky - navigation.tabs.sticky
- toc.integrate - navigation.footer
- navigation.top - navigation.path
- navigation.top
- navigation.section
- content.code.annotate
- navigation.expand
- toc.follow
palette: palette:
- media: '(prefers-color-scheme: dark)' - media: '(prefers-color-scheme: dark)'
scheme: slate scheme: slate
toggle: toggle:
icon: material/brightness-4 icon: material/brightness-4
name: Switch to system preference name: Switch to system preference
- media: (prefers-color-scheme) - media: (prefers-color-scheme)
toggle: toggle:
icon: material/brightness-auto icon: material/brightness-auto
name: Switch to light mode name: Switch to light mode
- media: '(prefers-color-scheme: light)' - media: '(prefers-color-scheme: light)'
scheme: default scheme: default
toggle: toggle:
icon: material/brightness-7 icon: material/brightness-7
name: Switch to dark mode name: Switch to dark mode
plugins: plugins:
- search - search
- markdownextradata - markdownextradata
- mermaid2 - mermaid2
- localsearch - localsearch
- autorefs - mkdocstrings:
- mkdocstrings: default_handler: python
default_handler: python handlers:
handlers: python:
python: path:
selection: - src
filters: options:
- '!^_(?!_init__)' filters:
inherited_members: true - '!^_[^_]'
rendering: inherited_members: false
show_root_heading: false show_if_no_docstring: true
show_root_toc_entry: false show_signature_annotations: true
show_root_full_path: false show_source: false
show_if_no_docstring: true show_category_heading: true
show_signature_annotations: true group_by_category: true
show_source: false show_root_full_path: false
heading_level: 2 merge_init_into_class: true
group_by_category: true separate_signature: true
show_category_heading: true heading_level: 2
docstring_section_style: spacy
show_root_toc_entry: false
- with-pdf:
cover_subtitle: User Manual
cover_logo: C:\Users\chacha\git\pygitversionhelper\docs-static\Library.jpg
verbose: false
exclude_pages:
- LICENSE
output_path: C:\Users\chacha\git\pygitversionhelper\helpers-results\doc_gen\site\pdf\manual.pdf
markdown_extensions: markdown_extensions:
- def_list - def_list
- tables - tables
- attr_list - attr_list
- abbr - abbr
- pymdownx.betterem: - pymdownx.blocks.admonition:
smart_enable: all types:
- pymdownx.caret - new
- pymdownx.critic - settings
- pymdownx.details - note
- pymdownx.inlinehilite - abstract
- pymdownx.snippets - info
- pymdownx.highlight: - tip
anchor_linenums: true - success
line_spans: __span - question
pygments_lang_class: true - warning
- pymdownx.keys - failure
- pymdownx.mark - danger
- pymdownx.progressbar - bug
- pymdownx.smartsymbols - example
- pymdownx.tasklist: - quote
custom_checkbox: true - pymdownx.blocks.definition
- pymdownx.tilde - pymdownx.blocks.details
- footnotes - pymdownx.blocks.tab
- pymdownx.blocks.html
- pymdownx.betterem:
smart_enable: all
- pymdownx.caret
- pymdownx.critic
- pymdownx.details
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.keys
- pymdownx.mark
- pymdownx.progressbar
- pymdownx.smartsymbols
- pymdownx.tasklist:
custom_checkbox: true
- pymdownx.tilde
- footnotes
- pymdownx.superfences
- pymdownx.emoji:
emoji_index: !!python/name:materialx.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg
extra: extra:
branch: master branch: master
repository: pygitversionhelper repository: pygitversionhelper

View File

@@ -12,11 +12,10 @@ build-backend = "setuptools.build_meta"
[tool.setuptools_scm] [tool.setuptools_scm]
version_scheme= "post-release" version_scheme= "post-release"
# tag_regex="^(?:v)?(?P<version>\\d+\\.\\d+\\.\\d+)([\\.\\-\\+])?(?:.*)?"
[project] [project]
name = "pygitversionhelper" name = "pygitversionhelper"
description = "pygitversionhelper" description = "A simple simple git version helper in python."
readme = "README.md" readme = "README.md"
requires-python = ">=3.9" requires-python = ">=3.9"
keywords = ["chacha","chacha","template","pygitversionhelper"] keywords = ["chacha","chacha","template","pygitversionhelper"]
@@ -57,9 +56,10 @@ Tracker = "https://chacha.ddns.net/gitea/chacha/pygitversionhelper/issue
[project.optional-dependencies] [project.optional-dependencies]
test = ["junitparser>=2.8","junit2html>=30.1","xmlrunner>=1.7","mypy>=0.99" ] test = ["junitparser>=2.8","junit2html>=30.1","xmlrunner>=1.7","mypy>=0.99" ]
coverage-check = ["coverage>=7.0"] coverage-check = ["coverage>=7.0"]
complexity-check = ["radon>=5.1"]
quality-check = ["pylint>=2.15","pylint-json2html>=0.4","pandas>=1.5"] quality-check = ["pylint>=2.15","pylint-json2html>=0.4","pandas>=1.5"]
type-check = ["mypy[reports]>=0.99" ] type-check = ["mypy[reports]>=0.99" ]
doc-gen = ["mkdocs>=1.4.0", "mkdocs-material>=8.5", "mkdocs-localsearch>=0.9.0", "mkdocstrings[python]>=0.19", "mkdocs-with-pdf>=0.9.3","pyyaml>=6.0","pymdown-extensions>=9","mkdocs-markdownextradata-plugin","mkdocs-mermaid2-plugin"] doc-gen = ["mkdocs>=1.4.0", "mkdocs-material>=8.5","mkdocs-pymdownx-material-extras", "mkdocs-localsearch>=0.9.0", "mkdocstrings[python]>=0.19", "mkdocs-with-pdf>=0.9.3","pyyaml>=6.0","pymdown-extensions>=9","mkdocs-markdownextradata-plugin","mkdocs-mermaid2-plugin"]
#[project.scripts] #[project.scripts]
#my-script = "my_package.module:function" #my-script = "my_package.module:function"

View File

@@ -0,0 +1,7 @@
# pygitversionhelper (c) by chacha
#
# pygitversionhelper is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Unported License.
#
# 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/>.

View File

@@ -5,17 +5,16 @@
# #
# You should have received a copy of the license along with this # 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/>. # work. If not, see <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
""" """
This project try to help doing handy operations with git when This project try to help doing handy operations with git when
dealing with project versioning and tags on python project - dealing with project versioning and tags on python project -
at leat for project using PEP440 or SemVer standards. at leat for project using PEP440 or SemVer standards.
One requirement is to keep it compact and to not cover too much fancy features. 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. 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 made for repository that uses tags as version.
This library is maid for repository using tag as version.
Support for non-version tags is optional and not well tested. Support for non-version tags is optional and not well tested.
This module is the main project file, containing all the code. This module is the main project file, containing all the code.
@@ -23,34 +22,69 @@ This module is the main project file, containing all the code.
Read the read me for more information. Read the read me for more information.
Check the unittest s for usage samples. Check the unittest s for usage samples.
Note: _Other Parameters_ are **kwargs ///Note
_Other Parameters_ are **kwargs
///
Attributes:
TKwargs: Kwargs type definition for type hints
""" """
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, cast, TypedDict, Literal
import os import os
import subprocess import subprocess
import re import re
from copy import copy from copy import copy
import logging import logging
from pathlib import Path
from packaging.version import VERSION_PATTERN as packaging_VERSION_PATTERN from packaging.version import VERSION_PATTERN as packaging_VERSION_PATTERN
if TYPE_CHECKING:
from typing import List, Optional, Any, Dict
from typing_extensions import Unpack
def _exec(cmd: str, root: str | os.PathLike | None = None, raw: bool = False) -> list[str]: TKwargs = TypedDict(
""" "TKwargs",
helper function to handle system cmd execution {
"version_std": Optional[str],
"same_branch": Optional[bool],
"formated_output": Optional[bool],
"bump_type": Optional[str],
"bump_dev_strategy": Optional[str],
"merged_output": Optional[bool],
"ignore_unknown_tags": Optional[bool],
"output_format": Optional[str],
"ignore_merged": Optional[bool],
},
total=False,
)
def _exec(cmd: str, root: Optional[str | os.PathLike[str]] = None, raw: bool = False) -> str | List[str]:
"""helper function to handle system cmd execution
Args: Args:
cmd: command line to be executed cmd: command line to be executed
root: root directory where the command need to be executed root: root directory where the command need to be executed
raw: return bytes if True, str if False or None
Returns: Returns:
a list of command's return lines a list of command's return lines or the raw output
""" """
_root: Path
if isinstance(root, str):
_root = Path(root)
elif isinstance(root, os.PathLike):
_root = Path(root)
else:
_root = Path(os.getcwd())
p = subprocess.run( p = subprocess.run(
cmd, cmd,
text=True, text=True,
cwd=root, cwd=_root,
capture_output=True, capture_output=True,
check=False, check=False,
timeout=2, timeout=2,
@@ -63,115 +97,99 @@ def _exec(cmd: str, root: str | os.PathLike | None = None, raw: bool = False) ->
if int(p.returncode) < 0: if int(p.returncode) < 0:
raise gitversionhelper.unknownGITError(p.stderr) raise gitversionhelper.unknownGITError(p.stderr)
if raw: if raw is True:
return p.stdout return p.stdout
lines = p.stdout.splitlines() lines = p.stdout.splitlines()
return [line.rstrip() for line in lines if line.rstrip()] return [line.rstrip() for line in lines if line.rstrip()]
class gitversionhelperException(Exception): class gitversionhelperException(Exception):
""" """general Module Exception"""
general Module Exception
"""
class gitversionhelper: # pylint: disable=too-few-public-methods class gitversionhelper: # pylint: disable=too-few-public-methods
""" """main gitversionhelper class"""
main gitversionhelper class
"""
class wrongArguments(gitversionhelperException): class wrongArguments(gitversionhelperException):
""" """wrong argument generic exception"""
wrong argument generic exception
"""
class unknownGITError(gitversionhelperException): class unknownGITError(gitversionhelperException):
""" """unknown git error generic exception"""
unknown git error generic exception
"""
class unknownGITFatalError(unknownGITError): class unknownGITFatalError(unknownGITError):
""" """unknown fatal git error generic exception"""
unknown fatal git error generic exception
"""
class repository: class repository:
""" """class containing methods focusing on repository"""
class containing methods focusing on repository
"""
class repositoryException(gitversionhelperException): class repositoryException(gitversionhelperException):
""" """generic repository exeption"""
generic repository exeption
"""
class notAGitRepository(repositoryException): class notAGitRepository(repositoryException):
""" """not-a-git-repository repository exception"""
not a git repository exception
"""
class repositoryDirty(repositoryException): class repositoryDirty(repositoryException):
""" """dirty-repository repository exception"""
dirty repository exception
"""
@classmethod @classmethod
def isDirty(cls) -> bool: def isDirty(cls) -> bool:
""" """check if the repository is in dirty state
check if the repository is in dirty state
Returns: Returns:
True if it is dirty True if it is dirty
""" """
return bool(_exec("git status --short")) return bool(_exec("git status --short"))
class commit: class commit:
""" """class containing methods focusing on commits"""
class containing methods focusing on commits
"""
__OptDict = {"same_branch": "same_branch", "merged_output": "merged_output"}
class commitException(gitversionhelperException): class commitException(gitversionhelperException):
""" """generic commit exception"""
generic commit exception
"""
class commitNotFound(commitException): class commitNotFound(commitException):
""" """tag not found exception"""
tag not found exception
"""
@classmethod @classmethod
def getMessagesSinceTag(cls, tag: str, **kwargs) -> str: def getMessagesSinceTag(cls, tag: str, **kwargs: Unpack[TKwargs]) -> str | List[str]:
""" """Retrieve a commits message history from repository.
retrieve a commits message history from repository
from Latest commit to the given tag Start from Latest found commit until the given tag.
Args:
tag: tag of the commit where search will stop
Keyword Arguments: Keyword Arguments:
merged_output: output one single merged string kwargs/merged_output (bool): Output one single merged string
same_branch(bool): force searching only in the same branch kwargs/same_branch (bool): Force searching only in the same branch
kwargs/ignore_merged (bool): ignore merged commits
Returns: Returns:
the commit message the commit message
""" """
current_commit_id = cls.getLast(**kwargs) current_commit_id = cls.getLast(**kwargs)
tag_commit_id = cls.getFromTag(tag) tag_commit_id = cls.getFromTag(tag)
if (cls.__OptDict["same_branch"] in kwargs) and (kwargs[cls.__OptDict["same_branch"]] is True): str_cmd: str
commits = _exec(f"git rev-list --first-parent --ancestry-path {tag_commit_id}..{current_commit_id}") if ("same_branch" in kwargs) and (kwargs["same_branch"] is True):
str_cmd = f"git rev-list --first-parent {tag_commit_id}..{current_commit_id}" # ok
else: else:
commits = _exec(f"git rev-list --ancestry-path {tag_commit_id}..{current_commit_id}") str_cmd = f"git rev-list {tag_commit_id}..{current_commit_id}" # ok
if ("ignore_merged" in kwargs) and (kwargs["ignore_merged"] is True):
str_cmd = str_cmd + " --no-merges" # ok
try:
commits = _exec(str_cmd)
except gitversionhelper.unknownGITFatalError as _e:
raise cls.commitNotFound("no commit found in commit history") from _e
result = [] result = []
for commit in commits: for commit in commits:
result.append(cls.getMessage(commit)) result.append(cls.getMessage(commit))
if (cls.__OptDict["merged_output"] in kwargs) and (kwargs[cls.__OptDict["merged_output"]] is True): if ("merged_output" in kwargs) and (kwargs["merged_output"] is True):
print("JOIN")
return os.linesep.join(result) return os.linesep.join(result)
return result return result
@classmethod @classmethod
def getMessage(cls, commit_hash: str) -> str: def getMessage(cls, commit_hash: str) -> str:
""" """retrieve a commit message from repository
retrieve a commit message from repository
Args: Args:
commit_hash: id of the commit commit_hash: id of the commit
Returns: Returns:
@@ -179,10 +197,11 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
""" """
try: try:
res = _exec( res = _exec(
f'git log -z --pretty="tformat:%B%-C()" -n 1 {commit_hash}', f'git log -z --pretty="tformat:%B%-C()" -n 1 {commit_hash}', # ok
None, None,
True, True,
).rstrip("\x00") )
res = cast(str, res).rstrip("\x00")
except gitversionhelper.unknownGITFatalError as _e: except gitversionhelper.unknownGITFatalError as _e:
raise cls.commitNotFound("no commit found in commit history") from _e raise cls.commitNotFound("no commit found in commit history") from _e
@@ -190,48 +209,51 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
@classmethod @classmethod
def getFromTag(cls, tag: str) -> str: def getFromTag(cls, tag: str) -> str:
""" """retrieve a commit from repository associated to a tag
retrieve a commit from repository associated to a tag
Args: Args:
tag: tag of the commit tag: tag of the commit
Returns: Returns:
the commit Id the commit Id
""" """
try: try:
res = _exec(f"git rev-list -n 1 {tag}") res = _exec(f"git rev-list -n 1 {tag}") # ok
except gitversionhelper.unknownGITFatalError as _e: except gitversionhelper.unknownGITFatalError as _e:
raise cls.commitNotFound("no commit found in commit history") from _e raise cls.commitNotFound("no commit found in commit history") from _e
if len(res) == 0: if len(res) == 0:
raise cls.commitNotFound("no commit found in commit history") raise cls.commitNotFound("no commit found in commit history")
return res[0] return res[0]
@classmethod @classmethod
def getLast(cls, **kwargs) -> str: def getLast(cls, **kwargs: Unpack[TKwargs]) -> str:
""" """retrieve last commit from repository
retrieve last commit from repository
Keyword Arguments: Keyword Arguments:
same_branch(bool): force searching only in the same branch kwargs/same_branch (bool): force searching only in the same branch
kwargs/ignore_merged (bool): ignore merged commits
Returns: Returns:
the commit Id the commit Id
""" """
if (cls.__OptDict["same_branch"] in kwargs) and (kwargs[cls.__OptDict["same_branch"]] is True): str_cmd: str
try: if ("same_branch" in kwargs) and (kwargs["same_branch"] is True):
res = _exec("git rev-parse HEAD") str_cmd = "git rev-list --max-count=1 --date-order HEAD --first-parent" # ok
except gitversionhelper.unknownGITFatalError as _e:
raise cls.commitNotFound("no commit found in commit history") from _e
else: else:
res = _exec('git for-each-ref --sort=-committerdate refs/heads/ --count 1 --format="%(objectname)"') str_cmd = "git log --format=%H --all -n1" # ok
if ("ignore_merged" in kwargs) and (kwargs["ignore_merged"] is True):
str_cmd = str_cmd + " --no-merges" # ok
try:
res = _exec(str_cmd)
except gitversionhelper.unknownGITFatalError as _e:
raise cls.commitNotFound("no commit found in commit history") from _e
if len(res) == 0: if len(res) == 0:
raise cls.commitNotFound("no commit found in commit history") raise cls.commitNotFound("no commit found in commit history")
return res[0] return res[0]
class tag: class tag:
""" """class containing methods focusing on tags"""
class containing methods focusing on tags
"""
__OptDict = {"same_branch": "same_branch"}
__validGitTagSort = [ __validGitTagSort = [
"", "",
"v:refname", "v:refname",
@@ -243,48 +265,42 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
] ]
class tagException(gitversionhelperException): class tagException(gitversionhelperException):
""" """generic tag exception"""
generic tag exception
"""
class tagNotFound(tagException): class tagNotFound(tagException):
""" """tag-not-found tag exception"""
tag not found exception
"""
class moreThanOneTag(tagException): class moreThanOneTag(tagException):
""" """more-than-one-tag tag exception"""
more than one tag exception
"""
@classmethod @classmethod
def getTags(cls, sort: str = "taggerdate", **kwargs) -> list[str | None]: def getTags(cls, Sort: str = "taggerdate", **kwargs: Unpack[TKwargs]) -> List[str]:
""" """retrieve all tags from a repository
retrieve all tags from a repository
Args: Args:
sort: sorting constraints (git format) Sort: sorting constraints (git format)
Keyword Arguments:
kwargs/same_branch (bool): force searching only in the same branch
Returns: Returns:
the tags list the tags list
""" """
if sort not in cls.__validGitTagSort: if Sort not in cls.__validGitTagSort:
raise gitversionhelper.wrongArguments("sort option not in allowed list") raise gitversionhelper.wrongArguments("Sort option not in allowed list")
if (cls.__OptDict["same_branch"] in kwargs) and (kwargs[cls.__OptDict["same_branch"]] is True): if ("same_branch" in kwargs) and (kwargs["same_branch"] is True):
currentBranch = _exec("git rev-parse --abbrev-ref HEAD") currentBranch = _exec("git rev-parse --abbrev-ref HEAD")
return list(reversed(_exec(f"git tag --merged {currentBranch[0]} --sort={sort}"))) return list(reversed(_exec(f"git tag --merged {currentBranch[0]} --sort={Sort}")))
return list(reversed(_exec(f"git tag -l --sort={sort}"))) return list(reversed(_exec(f"git tag -l --sort={Sort}")))
@classmethod @classmethod
def getLastTag(cls, **kwargs) -> str | None: def getLastTag(cls, **kwargs: Unpack[TKwargs]) -> str:
""" """retrieve the Latest tag from a repository
retrieve the Latest tag from a repository
Keyword Arguments: Keyword Arguments:
same_branch(bool): force searching only in the same branch kwargs/same_branch (bool): force searching only in the same branch
Returns: Returns:
the tag the tag
""" """
if (cls.__OptDict["same_branch"] in kwargs) and (kwargs[cls.__OptDict["same_branch"]] is True): if ("same_branch" in kwargs) and (kwargs["same_branch"] is True):
res = _exec("git describe --tags --first-parent --abbrev=0") res = _exec("git describe --tags --first-parent --abbrev=0")
else: else:
res = _exec("git rev-list --tags --date-order --max-count=1") res = _exec("git rev-list --tags --date-order --max-count=1")
@@ -298,13 +314,12 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
return res[0] return res[0]
@classmethod @classmethod
def getDistanceFromTag(cls, tag: str = None, **kwargs) -> int: def getDistanceFromTag(cls, tag: Optional[str] = None, **kwargs: Unpack[TKwargs]) -> int:
""" """retrieve the distance between Latest commit and tag in the repository
retrieve the distance between Latest commit and tag in the repository
Arguments: Arguments:
tag: reference tag, if None the most recent one will be used tag: reference tag, if None the most recent one will be used
Keyword Arguments: Keyword Arguments:
same_branch(bool): force searching only in the same branch kwargs/same_branch (bool): force searching only in the same branch
Returns: Returns:
the tag the tag
""" """
@@ -313,18 +328,13 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
return int(_exec(f"git rev-list {tag}..HEAD --count")[0]) return int(_exec(f"git rev-list {tag}..HEAD --count")[0])
class version: class version:
""" """class containing methods focusing on versions"""
class containing methods focusing on versions
"""
__OptDict = {
"version_std": "version_std",
"formated_output": "formated_output",
"output_format": "output_format",
"ignore_unknown_tags": "ignore_unknown_tags",
}
DefaultInputFormat = "Auto" DefaultInputFormat = "Auto"
VersionStds = {
TVersionStds = TypedDict("TVersionStds", {"regex": str, "regex_preversion_num": str, "regex_build_num": str}, total=False)
VersionStds: dict[str, TVersionStds] = {
"SemVer": { "SemVer": {
"regex": r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)" "regex": r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)"
r"(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)" r"(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)"
@@ -333,41 +343,32 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
"regex_preversion_num": r"(?:\.)(?P<num>(?:\d+(?!\w))+)", "regex_preversion_num": r"(?:\.)(?P<num>(?:\d+(?!\w))+)",
"regex_build_num": r"(?:\.)(?P<num>(?:\d+(?!\w))+)", "regex_build_num": r"(?:\.)(?P<num>(?:\d+(?!\w))+)",
}, },
"PEP440": {"regex": packaging_VERSION_PATTERN, "Auto": None}, "PEP440": {"regex": packaging_VERSION_PATTERN},
} }
__versionReseted = False __versionReseted = False
class versionException(gitversionhelperException): class versionException(gitversionhelperException):
""" """generic version exception"""
generic version exception
"""
class noValidVersion(versionException): class noValidVersion(versionException):
""" """no valid version found exception"""
no valid version found exception
"""
class PreAndPostVersionUnsupported(versionException): class PreAndPostVersionUnsupported(versionException):
""" """pre and post release can not be present at the same time"""
pre and post release can not be present at the same time
"""
class MetaVersion: class MetaVersion:
""" """generic version object"""
generic version object
"""
__OptDict = { TBumpTypes = Literal["major", "minor", "patch", "dev"]
"bump_type": "bump_type", DefaultBumpType: TBumpTypes = "patch"
"bump_dev_strategy": "bump_dev_strategy", BumpTypes: set[TBumpTypes] = {"major", "minor", "patch", "dev"}
"formated_output": "formated_output",
}
DefaultBumpType = "patch"
BumpTypes = ["major", "minor", "patch", "dev"]
DefaultBumpDevStrategy = "post"
BumpDevStrategys = ["post", "pre-patch", "pre-minor", "pre-major"]
version_std: str = "None" TBumpDevStrategies = Literal["post", "pre-patch", "pre-minor", "pre-major"]
DefaultBumpDevStrategy: TBumpDevStrategies = "post"
BumpDevStrategies: set[TBumpDevStrategies] = {"post", "pre-patch", "pre-minor", "pre-major"}
TVersionStd = Literal["Auto", "PEP440", "SemVer"]
version_std: TVersionStd = "Auto"
major: int = 0 major: int = 0
minor: int = 1 minor: int = 1
patch: int = 0 patch: int = 0
@@ -377,13 +378,13 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
def __init__( def __init__(
self, self,
version_std, version_std: TVersionStd = "Auto",
major=0, major: int = 0,
minor=1, minor: int = 1,
patch=0, patch: int = 0,
pre_count=0, pre_count: int = 0,
post_count=0, post_count: int = 0,
raw="0.1.0", raw: str = "0.1.0",
): # pylint: disable=R0913 ): # pylint: disable=R0913
self.version_std = version_std self.version_std = version_std
self.major = major self.major = major
@@ -394,50 +395,55 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
self.raw = raw self.raw = raw
@classmethod @classmethod
def _getBumpDevStrategy(cls, **kwargs) -> str: def _getBumpDevStrategy(cls, **kwargs: Unpack[TKwargs]) -> str:
""" """get selected bump_dev_strategy
get selected bump_dev_strategy
Keyword Arguments: Keyword Arguments:
bump_dev_strategy(str): the given bump_dev_strategy (can be None) kwargs/bump_dev_strategy (str): the given bump_dev_strategy (can be None)
Returns: Returns:
Kwargs given bump_dev_strategy or the default one. Kwargs given bump_dev_strategy or the default one.
""" """
BumpDevStrategy = cls.DefaultBumpDevStrategy BumpDevStrategy: str = cls.DefaultBumpDevStrategy
if cls.__OptDict["bump_dev_strategy"] in kwargs:
if kwargs[cls.__OptDict["bump_dev_strategy"]] in cls.BumpDevStrategys: if kwargs and ("bump_dev_strategy" in kwargs):
BumpDevStrategy = kwargs[cls.__OptDict["bump_dev_strategy"]] if kwargs["bump_dev_strategy"] in cls.BumpDevStrategies:
BumpDevStrategy = kwargs["bump_dev_strategy"]
else: else:
raise gitversionhelper.wrongArguments(f"invalid {cls.__OptDict['bump_type']} requested") raise gitversionhelper.wrongArguments(f"invalid {'bump_type'} requested")
return BumpDevStrategy return BumpDevStrategy
@classmethod @classmethod
def _getBumpType(cls, **kwargs) -> str: def _getBumpType(cls, **kwargs: Unpack[TKwargs]) -> str:
""" """get selected bump_type
get selected bump_type
Keyword Arguments: Keyword Arguments:
bump_type(str): the given bump_type (can be None) kwargs/bump_type (str): the given bump_type (can be None)
Returns: Returns:
Kwargs given bump_type or the default one. Kwargs given bump_type or the default one.
""" """
BumpType = cls.DefaultBumpType BumpType: str = cls.DefaultBumpType
if cls.__OptDict["bump_type"] in kwargs: if "bump_type" in kwargs:
if kwargs[cls.__OptDict["bump_type"]] in cls.BumpTypes: if kwargs["bump_type"] in cls.BumpTypes:
BumpType = kwargs[cls.__OptDict["bump_type"]] BumpType = kwargs["bump_type"]
else: else:
raise gitversionhelper.wrongArguments(f"invalid {cls.__OptDict['bump_type']} requested") raise gitversionhelper.wrongArguments(f"invalid {'bump_type'} requested")
return BumpType return BumpType
def bump(self, amount: int = 1, **kwargs) -> gitversionhelper.version.MetaVersion | str: # pylint: disable=R0912 def bump( # pylint: disable=R1260,R0912
""" self, amount: int = 1, **kwargs: Unpack[TKwargs]
bump the version to the next one ) -> gitversionhelper.version.MetaVersion | str:
"""bump the version to the next one
Args:
amount: number of revision to bump
Keyword Arguments: Keyword Arguments:
bump_type(str): the given bump_type (can be None) kwargs/bump_type (str): the given bump_type (can be None)
bump_dev_strategy(str): the given bump_dev_strategy (can be None) kwargs/bump_dev_strategy (str): the given bump_dev_strategy (can be None)
Returns: Returns:
the bumped version the bumped version
""" """
BumpType = self._getBumpType(**kwargs)
BumpType: str = self._getBumpType(**kwargs)
BumpDevStrategy = self._getBumpDevStrategy(**kwargs) BumpDevStrategy = self._getBumpDevStrategy(**kwargs)
_v = copy(self) _v = copy(self)
if BumpType == "dev": if BumpType == "dev":
@@ -473,88 +479,97 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
_v.patch = _v.patch + amount _v.patch = _v.patch + amount
_v.pre_count = 0 _v.pre_count = 0
_v.post_count = 0 _v.post_count = 0
_v.raw = _v.doFormatVersion(**kwargs) _v.raw = _v.doFormatVersion(**kwargs)
if (self.__OptDict["formated_output"] in kwargs) and (kwargs[self.__OptDict["formated_output"]] is True): if ("formated_output" in kwargs) and (kwargs["formated_output"] is True):
return _v.doFormatVersion(**kwargs) return _v.doFormatVersion(**kwargs)
return _v return _v
def doFormatVersion(self, **kwargs) -> str: def doFormatVersion(self, **kwargs: Unpack[TKwargs]) -> str:
""" """output a formated version string
output a formated version string
Keyword Arguments: Keyword Arguments:
output_format: output format to render ("Auto" or "PEP440" or "SemVer") kwargs/output_format: output format to render ("Auto" or "PEP440" or "SemVer")
Returns: Returns:
formated version string formated version string
""" """
return gitversionhelper.version.doFormatVersion(self, **kwargs) return gitversionhelper.version.doFormatVersion(self, **kwargs)
@classmethod @classmethod
def _getVersionStd(cls, **kwargs) -> str: def _getVersionStd(cls, **kwargs: Unpack[TKwargs]) -> gitversionhelper.version.MetaVersion.TVersionStd:
""" """get selected version_std
get selected version_std
Keyword Arguments: Keyword Arguments:
version_std(str): the given version_std (can be None) kwargs/version_std (str): the given version_std (can be None)
Returns: Returns:
Kwargs given version_std or the default one. Kwargs given version_std or the default one.
""" """
VersionStd = cls.DefaultInputFormat VersionStd: str = cls.DefaultInputFormat
if cls.__OptDict["version_std"] in kwargs: if "version_std" in kwargs:
if kwargs[cls.__OptDict["version_std"]] in cls.VersionStds: if kwargs["version_std"] in cls.VersionStds:
VersionStd = kwargs[cls.__OptDict["version_std"]] VersionStd = kwargs["version_std"]
else: else:
raise gitversionhelper.wrongArguments(f"invalid {cls.__OptDict['version_std']} requested") raise gitversionhelper.wrongArguments(f"invalid {'version_std'} requested")
return VersionStd return cast(gitversionhelper.version.MetaVersion.TVersionStd, VersionStd)
@classmethod @classmethod
def getCurrentVersion(cls, **kwargs) -> MetaVersion | str: def getCurrentVersion(cls, **kwargs: Unpack[TKwargs]) -> gitversionhelper.version.MetaVersion | str:
""" """get the current version or bump depending of repository state.
get the current version or bump depending of repository state
Keyword Arguments: Keyword Arguments:
version_std(str): the given version_std (can be None) kwargs/version_std (str): the given version_std (can be None)
same_branch(bool): force searching only in the same branch kwargs/same_branch (bool): force searching only in the same branch
formated_output(bool) : output a formated version string kwargs/formated_output (bool): output a formated version string
bump_type(str): the given bump_type (can be None) kwargs/bump_type (str): the given bump_type (can be None)
bump_dev_strategy(str): the given bump_dev_strategy (can be None) kwargs/bump_dev_strategy (str): the given bump_dev_strategy (can be None)
kwargs/output_format (str): output format to render ("Auto" or "PEP440" or "SemVer")
Returns: Returns:
the last version the last version
""" """
if gitversionhelper.repository.isDirty() is not False: if gitversionhelper.repository.isDirty() is not False:
raise gitversionhelper.repository.repositoryDirty("The repository is dirty and a current version can not be generated.") raise gitversionhelper.repository.repositoryDirty("The repository is dirty and a current version can not be generated.")
saved_kwargs = copy(kwargs) saved_kwargs = copy(kwargs)
if "formated_output" in kwargs: if "formated_output" in kwargs:
del saved_kwargs["formated_output"] del saved_kwargs["formated_output"]
_v = cls.getLastVersion(**saved_kwargs) _v = cast(gitversionhelper.version.MetaVersion, cls.getLastVersion(**saved_kwargs))
if not cls.__versionReseted: if not cls.__versionReseted:
amount = gitversionhelper.tag.getDistanceFromTag(_v.raw, **kwargs) amount = gitversionhelper.tag.getDistanceFromTag(_v.raw, **saved_kwargs)
_v = _v.bump(amount, **saved_kwargs) _v = cast(gitversionhelper.version.MetaVersion, _v.bump(amount, **saved_kwargs))
if (cls.__OptDict["formated_output"] in kwargs) and (kwargs[cls.__OptDict["formated_output"]] is True): if ("formated_output" in kwargs) and (kwargs["formated_output"] is True):
return _v.doFormatVersion(**kwargs) return _v.doFormatVersion(**kwargs)
return _v return _v
@classmethod @classmethod
def getCurrentFormatedVersion(cls, **kwargs) -> str: def getCurrentFormatedVersion(cls, **kwargs: Unpack[TKwargs]) -> str:
""" """same as getCurrentVersion() with formated_output kwarg forced activated.
Same as getCurrentVersion() with formated_output kwarg activated
"""
kwargs["formated_output"] = True
return cls.getCurrentVersion(**kwargs)
@classmethod
def _parseTag(cls, tag, **kwargs): # pylint: disable=R0914, R0912, R0915
"""get the last version from tags
Arguments:
tag: the tag to be parsed
Keyword Arguments: Keyword Arguments:
version_std(str): the given version_std (can be None) kwargs/version_std (str): the given version_std (can be None)
ignore_unknown_tags(bool): skip tags with not decoded versions (default to False) kwargs/same_branch (bool): force searching only in the same branch
kwargs/bump_type (str): the given bump_type (can be None)
kwargs/bump_dev_strategy (str): the given bump_dev_strategy (can be None)
kwargs/output_format (str): output format to render ("Auto" or "PEP440" or "SemVer")
Returns: Returns:
the last version the last version
""" """
VersionStd = cls._getVersionStd(**kwargs) kwargs["formated_output"] = True
return cast(str, cls.getCurrentVersion(**kwargs))
@classmethod
def _parseTag( # pylint: disable=R1260,R0914,R0912,R0915
cls, tag: str, **kwargs: Unpack[TKwargs]
) -> gitversionhelper.version.MetaVersion:
"""get version from tags.
Arguments:
tag: the tag to be parsed
Keyword Arguments:
kwargs/version_std (str): the given version_std (can be None)
Returns:
MetaVersion object
"""
_m: Optional[re.Match[str]]
VersionStd: gitversionhelper.version.MetaVersion.TVersionStd = cls._getVersionStd(**kwargs)
bAutoVersionStd = False bAutoVersionStd = False
if VersionStd == "Auto": if VersionStd == "Auto":
bAutoVersionStd = True bAutoVersionStd = True
@@ -565,14 +580,12 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
re.VERBOSE | re.IGNORECASE, re.VERBOSE | re.IGNORECASE,
) )
_m = re.match(_r, tag) _m = re.match(_r, tag)
if not _m: if _m is None:
pass pass
else: else:
major, minor, patch = ( major = int(_m.group("major"))
int(_m.group("major")), minor = int(_m.group("minor"))
int(_m.group("minor")), patch = int(_m.group("patch"))
int(_m.group("patch")),
)
pre_count = 0 pre_count = 0
if _pre := _m.group("prerelease"): if _pre := _m.group("prerelease"):
@@ -596,19 +609,25 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
re.VERBOSE | re.IGNORECASE, re.VERBOSE | re.IGNORECASE,
) )
_m = re.match(_r, tag) _m = re.match(_r, tag)
if not _m: if _m is None:
pass pass
else: else:
ver = _m.group("release").split(".") res: str = _m.group("release")
ver += ["0"] * (3 - len(ver)) if isinstance(res, str):
ver[0] = int(ver[0])
ver[1] = int(ver[1]) ver = res.split(".")
ver[2] = int(ver[2]) ver += ["0"] * (3 - len(ver))
major, minor, patch = tuple(ver)
pre_count = int(_m.group("pre_n")) if _m.group("pre_n") else 0 ver_int: List[int] = [0, 0, 0]
post_count = int(_m.group("post_n2")) if _m.group("post_n2") else 0 ver_int[0] = int(ver[0])
bFound = True ver_int[1] = int(ver[1])
VersionStd = "PEP440" ver_int[2] = int(ver[2])
major, minor, patch = tuple(ver_int)
pre_count = int(_m.group("pre_n")) if _m.group("pre_n") else 0
post_count = int(_m.group("post_n2")) if _m.group("post_n2") else 0
bFound = True
VersionStd = "PEP440"
if not bFound: if not bFound:
raise gitversionhelper.version.noValidVersion("no valid version found in tags") raise gitversionhelper.version.noValidVersion("no valid version found in tags")
@@ -618,30 +637,30 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
return cls.MetaVersion(VersionStd, major, minor, patch, pre_count, post_count, tag) return cls.MetaVersion(VersionStd, major, minor, patch, pre_count, post_count, tag)
@classmethod @classmethod
def getLastVersion(cls, **kwargs) -> MetaVersion | str: # pylint: disable=R0914, R0912, R0915 def getLastVersion(cls, **kwargs: Unpack[TKwargs]) -> gitversionhelper.version.MetaVersion | str: # pylint: disable=R1260
"""get the last version from tags """get the last version from tags
Keyword Arguments: Keyword Arguments:
version_std(str): the given version_std (can be None) kwargs/version_std (str): the given version_std (can be None)
same_branch(bool): force searching only in the same branch kwargs/same_branch (bool): force searching only in the same branch
formated_output(bool) : output a formated version string kwargs/formated_output (bool): output a formated version string
ignore_unknown_tags(bool): skip tags with not decoded versions (default to False) kwargs/ignore_unknown_tags (bool): skip tags with not decoded versions (default to False)
Returns: Returns:
the last version the last version in MetaVersion object or string
""" """
lastTag = cls.MetaVersion.raw lastTag: str = cls.MetaVersion.raw
cls.__versionReseted = False cls.__versionReseted = False
try: try:
lastTag = gitversionhelper.tag.getLastTag(**kwargs) lastTag = cast(str, gitversionhelper.tag.getLastTag(**kwargs))
except gitversionhelper.tag.tagNotFound: except gitversionhelper.tag.tagNotFound:
logging.warning("tag not found, reseting versionning") logging.warning("tag not found, reseting versionning")
cls.__versionReseted = True cls.__versionReseted = True
_v = None _v: Optional[gitversionhelper.version.MetaVersion] = None
try: try:
_v = cls._parseTag(lastTag, **kwargs) _v = cls._parseTag(lastTag, **kwargs)
except gitversionhelper.version.noValidVersion as _ex: except gitversionhelper.version.noValidVersion as _ex:
if (cls.__OptDict["ignore_unknown_tags"] in kwargs) and (kwargs[cls.__OptDict["ignore_unknown_tags"]] is True): if ("ignore_unknown_tags" in kwargs) and (kwargs["ignore_unknown_tags"] is True):
tags = gitversionhelper.tag.getTags(sort="taggerdate", **kwargs) tags = gitversionhelper.tag.getTags(Sort="taggerdate", **kwargs)
_v = None _v = None
for _tag in tags: for _tag in tags:
try: try:
@@ -652,16 +671,15 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
if _v is None: if _v is None:
raise gitversionhelper.version.noValidVersion() from _ex raise gitversionhelper.version.noValidVersion() from _ex
if (cls.__OptDict["formated_output"] in kwargs) and (kwargs[cls.__OptDict["formated_output"]] is True): if ("formated_output" in kwargs) and (kwargs["formated_output"] is True):
return _v.doFormatVersion(**kwargs) return _v.doFormatVersion(**kwargs)
return _v return _v
@classmethod @classmethod
def doFormatVersion(cls, inputversion: MetaVersion, **kwargs) -> str: def doFormatVersion(cls, inputversion: MetaVersion, **kwargs: Unpack[TKwargs]) -> str:
""" """output a formated version string from a MetaVersion object
output a formated version string
Keyword Arguments: Keyword Arguments:
output_format: output format to render ("Auto" or "PEP440" or "SemVer") kwargs/output_format (str): output format to render ("Auto" or "PEP440" or "SemVer")
Args: Args:
inputversion: version to be rendered inputversion: version to be rendered
Returns: Returns:
@@ -672,15 +690,13 @@ class gitversionhelper: # pylint: disable=too-few-public-methods
if VersionStd == "Auto": if VersionStd == "Auto":
VersionStd = inputversion.version_std VersionStd = inputversion.version_std
OutputFormat = None
revpattern = "" revpattern = ""
revcount = "" revcount = ""
post_count = inputversion.post_count post_count = inputversion.post_count
pre_count = inputversion.pre_count pre_count = inputversion.pre_count
patch = inputversion.patch patch = inputversion.patch
if cls.__OptDict["output_format"] in kwargs: OutputFormat = kwargs.get("output_format")
OutputFormat = kwargs[cls.__OptDict["output_format"]]
if OutputFormat is None: if OutputFormat is None:
OutputFormat = "{major}.{minor}.{patch}{revpattern}{revcount}" OutputFormat = "{major}.{minor}.{patch}{revpattern}{revcount}"

View File

@@ -730,9 +730,9 @@ class Test_gitversionhelper(unittest.TestCase):
with self.assertRaises(pygitversionhelper.gitversionhelper.version.PreAndPostVersionUnsupported): with self.assertRaises(pygitversionhelper.gitversionhelper.version.PreAndPostVersionUnsupported):
pygitversionhelper.gitversionhelper.version._parseTag("0.0.1.pre1.post1") pygitversionhelper.gitversionhelper.version._parseTag("0.0.1.pre1.post1")
def test_defect__git__wrongargument_sortargs(self): def test_defect__git__wrongargument_Sortargs(self):
with self.assertRaises(pygitversionhelper.gitversionhelper.wrongArguments): with self.assertRaises(pygitversionhelper.gitversionhelper.wrongArguments):
pygitversionhelper.gitversionhelper.tag.getTags(sort="toto") pygitversionhelper.gitversionhelper.tag.getTags(Sort="toto")
def test_defect__git__notagfound(self): def test_defect__git__notagfound(self):
with open("demofile.txt", "w+t") as tmpFile: with open("demofile.txt", "w+t") as tmpFile:
@@ -1009,6 +1009,16 @@ class Test_gitversionhelper(unittest.TestCase):
self.assertEqual(_v.minor, 3) self.assertEqual(_v.minor, 3)
self.assertEqual(_v.patch, 0) self.assertEqual(_v.patch, 0)
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue4")
os.system("git add .")
os.system('git commit -m "4th commit"')
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue5")
os.system("git add .")
os.system('git commit -m "5th commit"')
os.system("git switch master") os.system("git switch master")
_v = pygitversionhelper.gitversionhelper.version.getLastVersion(version_std="PEP440") _v = pygitversionhelper.gitversionhelper.version.getLastVersion(version_std="PEP440")
@@ -1404,6 +1414,17 @@ class Test_gitversionhelper(unittest.TestCase):
cmd = "git commit -m".split() cmd = "git commit -m".split()
cmd.append(commit_message5) cmd.append(commit_message5)
subprocess.run(cmd, text=True, check=True) subprocess.run(cmd, text=True, check=True)
os.system(f"git tag 0.1.1.post4")
commit_message6 = "6.1 update this" + os.linesep + "6.1 fix that" + os.linesep + "6.1 test"
commit_message6 = commit_message6.replace("\r\n", "\n").replace("\n", "\r\n")
with open("demofile.txt", "w+t") as tmpFile:
tmpFile.write("testvalue6")
os.system("git add .")
cmd = "git commit -m".split()
cmd.append(commit_message6)
subprocess.run(cmd, text=True, check=True)
cmd = "git switch master".split() cmd = "git switch master".split()
subprocess.run(cmd, text=True, check=True) subprocess.run(cmd, text=True, check=True)
@@ -1412,7 +1433,42 @@ class Test_gitversionhelper(unittest.TestCase):
self.assertEqual(os.linesep.join([commit_message4, commit_message3, commit_message2]), res) self.assertEqual(os.linesep.join([commit_message4, commit_message3, commit_message2]), res)
res = pygitversionhelper.gitversionhelper.commit.getMessagesSinceTag("0.1.1", merged_output=True) res = pygitversionhelper.gitversionhelper.commit.getMessagesSinceTag("0.1.1", merged_output=True)
self.assertEqual(os.linesep.join([commit_message5, commit_message4, commit_message3, commit_message2]), res) self.assertEqual(os.linesep.join([commit_message6, commit_message5, commit_message4, commit_message3, commit_message2]), res)
time.sleep(1)
merge_message = "automerge"
cmd = "git merge --no-ff dev -m".split()
cmd.append(merge_message)
subprocess.run(cmd, text=True, check=True)
res = pygitversionhelper.gitversionhelper.commit.getMessagesSinceTag("0.1.1", merged_output=True, same_branch=True)
self.assertEqual(os.linesep.join([merge_message, commit_message4, commit_message3, commit_message2]), res)
res = pygitversionhelper.gitversionhelper.commit.getMessagesSinceTag("0.1.1", merged_output=True)
self.assertEqual(
set(
merge_message.splitlines()
+ commit_message6.splitlines()
+ commit_message5.splitlines()
+ commit_message4.splitlines()
+ commit_message3.splitlines()
+ commit_message2.splitlines()
),
set(res.splitlines()),
)
res = pygitversionhelper.gitversionhelper.commit.getMessagesSinceTag("0.1.1", merged_output=True, ignore_merged=True)
self.assertEqual(
set(
commit_message6.splitlines()
+ commit_message5.splitlines()
+ commit_message4.splitlines()
+ commit_message3.splitlines()
+ commit_message2.splitlines()
),
set(res.splitlines()),
)
def tearDown(self): def tearDown(self):
os.chdir("/") os.chdir("/")