Compare commits
54 Commits
0.0.1.post
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| fc32bafee8 | |||
| 79fb84f2bc | |||
| 8c2f0f2e4f | |||
| e440880243 | |||
| aa22e534be | |||
| d3f5587e16 | |||
| e8dca96b59 | |||
| 31105cde3b | |||
| 63c881c318 | |||
|
|
16c653e388 | ||
|
|
a5f4664d0a | ||
| fe65553f7c | |||
|
|
aeccb8b813 | ||
| d7fe379563 | |||
|
|
f5f17be805 | ||
|
|
b91708491a | ||
| c3f1fe224d | |||
|
|
8868f77ef3 | ||
| cdf43418fd | |||
|
|
0594c29334 | ||
| 575ace2f94 | |||
|
|
0fa9f8d779 | ||
| f612a58cff | |||
|
|
6fcc465d58 | ||
|
|
01aa492d99 | ||
|
|
f1252f2bd9 | ||
| 589be8e375 | |||
|
|
021ac25f22 | ||
| 48afb560ae | |||
|
|
1778d10685 | ||
| 4263bfc299 | |||
|
|
d4b09d5d7a | ||
| ad32aa256d | |||
|
|
4608fe637e | ||
|
|
be511c366d | ||
| a1447c9f0c | |||
|
|
dbb16a07e7 | ||
| 687b5ae39d | |||
|
|
d662247473 | ||
|
|
7f10031e6d | ||
|
|
019dfed113 | ||
| 07035023a5 | |||
|
|
67478c0fa2 | ||
| 9c7624d068 | |||
|
|
05141eeb3f | ||
| 22213d32ed | |||
|
|
529a3e9922 | ||
|
|
f5b7931ed7 | ||
|
|
6a7be051bb | ||
|
|
3363792810 | ||
|
|
00df364a45 | ||
|
|
60aeac2040 | ||
|
|
a44b344e0d | ||
|
|
7aa3856d40 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -41,5 +41,6 @@ docs
|
||||
helpers-results
|
||||
.coverage
|
||||
/.mypy_cache/
|
||||
test/tmp
|
||||
.coverage
|
||||
.mypy_cache
|
||||
@@ -6,13 +6,9 @@
|
||||
# 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 debian:bullseye-slim
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt update
|
||||
RUN apt install -y python3.9 python3-virtualenv python3-pip git python3.9-venv weasyprint
|
||||
|
||||
RUN python3 -m pip install --upgrade pip
|
||||
RUN python3 -m pip install --upgrade virtualenv
|
||||
RUN python3 -m pip install --upgrade setuptools wheel build
|
||||
RUN apt install -y python3.11 python3-virtualenv python3-pip git python3-venv weasyprint
|
||||
18
Jenkinsfile
vendored
18
Jenkinsfile
vendored
@@ -27,7 +27,7 @@ def _bDraft = false
|
||||
// release content / changelog management
|
||||
def _bAutoChangelog = true //Not supported yet
|
||||
def _ReleaseContent_Title = "# _CI/CD Automatic Release_"
|
||||
def bPushMasterOnPypi = false
|
||||
def bPushMasterOnPypi = true
|
||||
// full rebuild toogle
|
||||
def _bFullRebuilt = true
|
||||
def _MkDocsWebURL = "dabauto--mkdocs-web.dmz.chacha.home/mkdocs-web/"
|
||||
@@ -184,7 +184,7 @@ pipeline {
|
||||
sh("virtualenv --pip=embed --setuptools=embed --wheel=embed --no-periodic-update --activators bash,python TOOLS_ENV")
|
||||
|
||||
sh(". ~/BUILD_ENV/bin/activate && pip install --upgrade setuptools build pip")
|
||||
sh(". ~/BUILD_ENV/bin/activate && pip install --upgrade 'copier==8.*' jinja2-slug toml")
|
||||
sh(". ~/BUILD_ENV/bin/activate && pip install --upgrade 'copier==9.*' jinja2-slug toml")
|
||||
|
||||
sh(". ~/TOOLS_ENV/bin/activate && pip install simple_rest_client requests twine packaging")
|
||||
|
||||
@@ -424,8 +424,16 @@ pipeline {
|
||||
post {
|
||||
always {
|
||||
dir("gitrepo") {
|
||||
publishCoverage adapters: [cobertura(mergeToOneReport: true, path: "helpers-results/types_check/cobertura.xml")]
|
||||
junit 'helpers-results/types_check/junit.xml'
|
||||
//publish coverage
|
||||
recordCoverage( sourceDirectories: [[path: 'src']],
|
||||
tools: [[parser: 'COBERTURA', pattern: 'helpers-results/types_check/cobertura.xml']],
|
||||
id: 'COBERTURA', name: 'COBERTURA Coverage',
|
||||
sourceCodeRetention: 'EVERY_BUILD',)
|
||||
|
||||
//add type check to junit result set
|
||||
junit 'helpers-results/types_check/junit.xml'
|
||||
|
||||
//publish html reports files
|
||||
publishHTML([
|
||||
reportDir: "helpers-results/quality_check",
|
||||
reportFiles: "report.html",
|
||||
@@ -535,7 +543,7 @@ pipeline {
|
||||
dir("gitrepo") {
|
||||
junit 'helpers-results/unit_test/*.xml'
|
||||
// using cobertura format (= coverage xml format)
|
||||
publishCoverage adapters: [cobertura(mergeToOneReport: true, path: "helpers-results/unit_test_coverage/test_coverage.xml")]
|
||||
recordCoverage(tools: [[parser: 'COBERTURA', pattern: 'helpers-results/cl_unit_test_coverage/test_coverage.xml']])
|
||||
publishHTML([
|
||||
reportDir: "helpers-results/unit_test_coverage",
|
||||
reportFiles: "index.html",
|
||||
|
||||
52
README.md
52
README.md
@@ -1,53 +1,13 @@
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
# Python project template
|
||||
A set of tool to help continuous integration.
|
||||
|
||||
A nice template to start blank python projets.
|
||||
|
||||
This template automate a lot of handy things and allow CI/CD automatic releases generation.
|
||||
|
||||
It is also collectings data to feed Jenkins build.
|
||||
|
||||
Checkout [Latest Documentation](https://chacha.ddns.net/mkdocs-web/chacha/{{repository}}/{{branch}}/latest/).
|
||||
|
||||
## Features
|
||||
|
||||
### Generic pipeline skeleton:
|
||||
- Prepare
|
||||
- GetCode
|
||||
- BuildPackage
|
||||
- Install
|
||||
- CheckCode
|
||||
- PlotMetrics
|
||||
- RunUnitTests
|
||||
- GenDOC
|
||||
- PostRelease
|
||||
|
||||
### CI/CD Environment
|
||||
- Jenkins
|
||||
- Gitea (with patch for dynamic Readme variables: https://chacha.ddns.net/gitea/chacha/GiteaMarkupVariable)
|
||||
- Docker
|
||||
- MkDocsWeb
|
||||
|
||||
### CI/CD Helper libs
|
||||
- VirtualEnv
|
||||
- Changelog generation based on commits
|
||||
- copier
|
||||
- pylint + pylint_json2html
|
||||
- mypy
|
||||
- unittest + xmlrunner + junitparser + junit2htmlreport
|
||||
- mkdocs
|
||||
|
||||
### Python project
|
||||
- Full .toml implementation
|
||||
- .whl automatic generation
|
||||
- dynamic versionning using git repository
|
||||
- embedded unit-test
|
||||
Checkout [Latest Documentation](https://chacha.ddns.net/mkdocs-web/chacha/chacha_cicd_helper/master/latest/).
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</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.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--quality-check"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="chacha_cicd_helper"/>
|
||||
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</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.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--type-check"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="chacha_cicd_helper"/>
|
||||
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
|
||||
@@ -1,16 +1,45 @@
|
||||
# Usage
|
||||
|
||||
## Pulvinar dolor
|
||||
Donec dapibus est fermentum justo volutpat condimentum. Integer quis nunc neque. Donec dictum vehicula justo, in facilisis ex tincidunt in.
|
||||
Vivamus sollicitudin sem dui, id mollis orci facilisis ut. Proin sed pulvinar dolor. Donec volutpat commodo urna imperdiet pulvinar. Fusce eget aliquam risus.
|
||||
Vivamus viverra luctus ex, in finibus mi. Nullam elementum dapibus mollis. Ut suscipit volutpat ex, quis feugiat lacus consectetur eu.
|
||||
/// note
|
||||
This helper aim to be used by pychachadummyproject template instantiation.
|
||||
///
|
||||
|
||||
## Condimentum faucibus
|
||||
Quisque auctor egestas sem, luctus suscipit ex maximus vitae. Duis facilisis augue et condimentum faucibus.
|
||||
Donec cursus, enim a sagittis egestas, lectus lorem eleifend libero, at tincidunt leo magna at libero.
|
||||
Nunc eros velit, suscipit luctus tempor vel, finibus et est. Curabitur efficitur pretium pulvinar.
|
||||
Donec urna lectus, vulputate quis turpis sed, placerat congue urna. Phasellus aliquet fermentum quam, non auctor elit porta nec. Morbi eu ligula at nisl ultricies condimentum vitae id ante.
|
||||
```console
|
||||
/> <python_bin> -m chacha_cicd_helper -h
|
||||
usage: chacha-cicd-helper [-pp PROJECTPATH] [-tc] [-ut] [-cc] [-qc] [-dg] [-pdf] [-cpc] [-h]
|
||||
|
||||
## Aliquam lacinia
|
||||
In volutpat lorem ex, et fringilla nibh faucibus quis. Mauris et arcu elementum, auctor dui vitae, egestas arcu. Duis sit amet aliquam quam.
|
||||
Phasellus a odio turpis. Etiam tristique mi eu enim varius, eget facilisis est vestibulum. Aliquam lacinia nec purus sed luctus. Cras at laoreet erat.
|
||||
A bundle of cicd helper tools
|
||||
|
||||
optional arguments:
|
||||
-pp PROJECTPATH, --projectpath PROJECTPATH
|
||||
path of the python project to process
|
||||
-tc, --typecheck enable static typing check
|
||||
-ut, --unittest enable unit-test
|
||||
-cc, --coveragecheck enable unit-test coverage check (requires unit-test)
|
||||
-qc, --qualitycheck enable code quality check
|
||||
-dg, --docgen enable documentation generation using MkDoc
|
||||
-pdf, --docgenpdf enable pdf documentation export (requires doc-gen)
|
||||
-cpc, --complexitycheck
|
||||
enable complexity check
|
||||
-h, --help show this help message and exit
|
||||
```
|
||||
|
||||
|
||||
Calling those commands will create a directory called `helpers-results` in `<PROJECTPATH>` (or in the current directory).
|
||||
|
||||
This directory will contain some of the following subdirectory, depending on what enabled:
|
||||
|
||||
| Directory | Content |
|
||||
|-----------------------|---------------------------------------------|
|
||||
| cl_complexity_check | code complexity measurement report |
|
||||
| cl_doc_gen | mkdocs documentation output (html + pdf) |
|
||||
| cl_quality_check | quality check reports |
|
||||
| cl_quality_check | quality check reports |
|
||||
| cl_types_check | type check reports |
|
||||
| cl_unit_test | unit test reports |
|
||||
| cl_unit_test_coverage | unit test coverage reports |
|
||||
| cl_unit_test_full | full unitest report (merged) |
|
||||
|
||||
/// warning
|
||||
<docgen> needs a docs-static directory in the target project root. Then one can put any .md file inside.
|
||||
///
|
||||
|
||||
@@ -10,11 +10,17 @@ from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from pathlib import Path
|
||||
import tomli
|
||||
|
||||
b_use_tomli=False
|
||||
try:
|
||||
import tomllib
|
||||
except ImportError:
|
||||
import tomli
|
||||
b_use_tomli=True
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import logging
|
||||
import sys
|
||||
|
||||
|
||||
if __package__ == "helpers":
|
||||
# when calling the module from: > python -m helpers
|
||||
@@ -37,7 +43,10 @@ if __name__ == "__main__":
|
||||
project_rootdir_path = Path(__file__).parent.parent.absolute()
|
||||
|
||||
with open(project_rootdir_path / "pyproject.toml", mode="rb") as fp:
|
||||
pyproject = tomli.load(fp)
|
||||
if b_use_tomli:
|
||||
pyproject = tomli.load(fp)
|
||||
else:
|
||||
pyproject = tomllib.load(fp)
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="continuous-integration-helper", description="A tiny set of scripts to help continous integration on python"
|
||||
|
||||
@@ -64,6 +64,7 @@ class quality_check(helper_withresults_base):
|
||||
"--load-plugins=pylint.extensions.mccabe",
|
||||
"--output-format=json,parseable",
|
||||
"--disable=invalid-name,too-few-public-methods,too-many-arguments", # ignore
|
||||
"--extension-pkg-whitelist=mypy",
|
||||
"--ignore=_version.py",
|
||||
"--reports=y",
|
||||
"--score=yes",
|
||||
|
||||
19
mkdocs.yml
19
mkdocs.yml
@@ -1,11 +1,11 @@
|
||||
docs_dir: docs
|
||||
site_name: 'chacha_cicd_helper'
|
||||
site_url: 'https://chacha.ddns.net/mkdocs-web/chacha/chacha_cicd_helper/latest/'
|
||||
site_description: 'A bundle of cicd helper tools'
|
||||
site_author: 'chacha'
|
||||
repo_url: 'https://chacha.ddns.net/gitea/chacha/chacha_cicd_helper'
|
||||
site_name: chacha_cicd_helper
|
||||
site_url: https://chacha.ddns.net/mkdocs-web/chacha/chacha_cicd_helper/latest/
|
||||
site_description: A bundle of cicd helper tools
|
||||
site_author: chacha
|
||||
repo_url: https://chacha.ddns.net/gitea/chacha/chacha_cicd_helper
|
||||
use_directory_urls: false
|
||||
copyright: 'CC BY-NC-SA 4.0'
|
||||
copyright: CC BY-NC-SA 4.0
|
||||
theme:
|
||||
name: material
|
||||
features:
|
||||
@@ -59,6 +59,13 @@ plugins:
|
||||
heading_level: 2
|
||||
docstring_section_style: spacy
|
||||
show_root_toc_entry: false
|
||||
- with-pdf:
|
||||
cover_subtitle: User Manual
|
||||
cover_logo: C:\Users\chacha\git\chacha_cicd_helper\docs-static\Library.jpg
|
||||
verbose: false
|
||||
exclude_pages:
|
||||
- LICENSE
|
||||
output_path: C:\Users\chacha\git\chacha_cicd_helper\helpers-results\doc_gen\site\pdf\manual.pdf
|
||||
markdown_extensions:
|
||||
- def_list
|
||||
- tables
|
||||
|
||||
@@ -31,18 +31,21 @@ classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
]
|
||||
dependencies = [
|
||||
'importlib-metadata; python_version<"3.9"',
|
||||
'typed-argument-parser==1.*'
|
||||
'typed-argument-parser==1.*',
|
||||
'packaging',
|
||||
'tomli; python_version<"3.11"',
|
||||
"junitparser>=2.8",
|
||||
"junit2html>=30.1",
|
||||
"xmlrunner>=1.7",
|
||||
"mypy>=0.99",
|
||||
"coverage>=7.0",
|
||||
"radon>=5.1",
|
||||
"pylint>=2.15",
|
||||
"pylint>=2.15,<3",
|
||||
"pylint-json2html>=0.4",
|
||||
"pandas>=1.5",
|
||||
"mypy[reports]>=0.99",
|
||||
@@ -67,9 +70,26 @@ include-package-data = true
|
||||
where = ["src"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"chacha_cicd_helper.data" = ["*.*"]
|
||||
"chacha_cicd_helper" = ["py.typed"]
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "tomli"
|
||||
ignore_missing_imports = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "tomllib"
|
||||
ignore_missing_imports = true
|
||||
|
||||
[tool.coverage.run]
|
||||
cover_pylib = false
|
||||
branch = true
|
||||
data_file="helpers-results/cl_unit_test_raw_coverage/.coverage"
|
||||
# debug = ["config","multiproc","process"]
|
||||
parallel = true
|
||||
concurrency = [
|
||||
'thread'
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://chacha.ddns.net/gitea/chacha/chacha_cicd_helper"
|
||||
Documentation = "https://chacha.ddns.net/mkdocs-web/chacha/chacha_cicd_helper/master/latest/"
|
||||
@@ -79,7 +99,7 @@ Tracker = "https://chacha.ddns.net/gitea/chacha/chacha_cicd_helper/issue
|
||||
test = ["junitparser>=2.8","junit2html>=30.1","xmlrunner>=1.7","mypy>=0.99" ]
|
||||
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,<3","pylint-json2html>=0.4","pandas>=1.5","types-PyYAML"]
|
||||
type-check = ["mypy[reports]>=0.99" ]
|
||||
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"]
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# pyGameCFG(c) by chacha
|
||||
# chacha_cicd_helper(c) by chacha
|
||||
#
|
||||
# pyGameCFG is licensed under a
|
||||
# chacha_cicd_helper 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/>.
|
||||
# pylint: disable=wrong-import-position
|
||||
"""
|
||||
Main module __init__ file.
|
||||
"""
|
||||
|
||||
"""Main module __init__ file."""
|
||||
|
||||
from importlib.metadata import distribution, version, PackageNotFoundError
|
||||
import warnings
|
||||
@@ -37,4 +36,4 @@ except PackageNotFoundError: # pragma: no cover
|
||||
warnings.warn('can not read dist.metadata["Name"], assuming local test context, setting it to <chacha_cicd_helper>')
|
||||
__Name__ = "chacha_cicd_helper"
|
||||
|
||||
from .__main__ import fct_main
|
||||
from .coverage_tools import CoverageProcess
|
||||
|
||||
@@ -1,89 +1,128 @@
|
||||
# pyChaChaDummyProject (c) by chacha
|
||||
# chacha_cicd_helper (c) by chacha
|
||||
#
|
||||
# pyChaChaDummyProject is licensed under a
|
||||
# chacha_cicd_helper 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/>.
|
||||
|
||||
"""Main module"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import Union
|
||||
|
||||
from pathlib import Path
|
||||
import tomli
|
||||
import argparse
|
||||
import os
|
||||
import logging
|
||||
import sys
|
||||
from tap import Tap
|
||||
|
||||
b_use_tomli=False
|
||||
try:
|
||||
import tomli
|
||||
b_use_tomli=True
|
||||
except ImportError:
|
||||
import tomllib
|
||||
|
||||
from .helper_base import cl_helper_base
|
||||
from .types_check import cl_types_check
|
||||
from .quality_check import cl_quality_check
|
||||
from .unit_test import cl_unit_test
|
||||
from .doc_gen import cl_doc_gen
|
||||
from .complexity_check import cl_complexity_check
|
||||
from .install_deps import cl_install_deps
|
||||
|
||||
|
||||
from .types_check import types_check
|
||||
from .quality_check import quality_check
|
||||
from .unit_test import unit_test
|
||||
from .doc_gen import doc_gen
|
||||
from .complexity_check import complexity_check
|
||||
from . import __Summuary__, __Name__
|
||||
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
|
||||
def fct_main(i_args: list[str]) -> None:
|
||||
class chacha_cicd_helper_args(Tap):
|
||||
"""class that describe cmd arguments"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="continuous-integration-helper", description="A tiny set of scripts to help continous integration on python"
|
||||
)
|
||||
projectpath: Union[str, None] = None
|
||||
installdeps: bool = False
|
||||
typecheck: bool = False
|
||||
unittest: bool = False
|
||||
coveragecheck: bool = False
|
||||
qualitycheck: bool = False
|
||||
docgen: bool = False
|
||||
docgenpdf: bool = False
|
||||
complexitycheck: bool = False
|
||||
|
||||
parser.add_argument("-pp", "--project-path", dest="projectpath", help="path of the python project to process", default=os.getcwd())
|
||||
def configure(self) -> None:
|
||||
"""specific arguments initializer"""
|
||||
|
||||
self.add_argument("-pp", "--projectpath", help="path of the python project to process", default=os.getcwd())
|
||||
|
||||
self.add_argument("-id", "--installdeps", action="store_true", help="install dependencies through pip")
|
||||
|
||||
parser.add_argument("-tc", "--type-check", dest="typecheck", action="store_true", help="enable static typing check")
|
||||
self.add_argument("-tc", "--typecheck", action="store_true", help="enable static typing check")
|
||||
|
||||
parser.add_argument("-ut", "--unit-test", dest="unittest", action="store_true", help="enable unit-test")
|
||||
parser.add_argument(
|
||||
"-cc", "--coverage-check", dest="coveragecheck", action="store_true", help="enable unit-test coverage check (requires unit-test)"
|
||||
)
|
||||
self.add_argument("-ut", "--unittest", action="store_true", help="enable unit-test")
|
||||
self.add_argument(
|
||||
"-cc",
|
||||
"--coveragecheck",
|
||||
action="store_true",
|
||||
help="enable unit-test coverage check (requires unit-test)",
|
||||
)
|
||||
|
||||
parser.add_argument("-qc", "--quality-check", dest="qualitycheck", action="store_true", help="enable code quality check")
|
||||
self.add_argument("-qc", "--qualitycheck", action="store_true", help="enable code quality check")
|
||||
|
||||
parser.add_argument("-dg", "--doc-gen", dest="docgen", action="store_true", help="enable documentation generation using MkDoc")
|
||||
parser.add_argument(
|
||||
"-pdf", "--doc-gen-pdf", dest="docgenpdf", action="store_true", help="enable pdf documentation export (requires doc-gen)"
|
||||
)
|
||||
self.add_argument("-dg", "--docgen", action="store_true", help="enable documentation generation using MkDoc")
|
||||
self.add_argument("-pdf", "--docgenpdf", action="store_true", help="enable pdf documentation export (requires doc-gen)")
|
||||
|
||||
parser.add_argument("-cpc", "--complexity-check", dest="complexitycheck", action="store_true", help="enable complexity check")
|
||||
self.add_argument("-cpc", "--complexitycheck", action="store_true", help="enable complexity check")
|
||||
|
||||
|
||||
def fct_main(i_args: list[str]) -> None: # pylint: disable=too-complex
|
||||
"""argument processing function"""
|
||||
parser = chacha_cicd_helper_args(prog=__Name__, description=__Summuary__)
|
||||
|
||||
args = parser.parse_args(i_args)
|
||||
|
||||
helpers = []
|
||||
if args.typecheck == True:
|
||||
helpers.append(types_check)
|
||||
helpers: list[type[cl_helper_base]] = []
|
||||
|
||||
if args.installdeps is True:
|
||||
helpers.append(cl_install_deps)
|
||||
else:
|
||||
if args.typecheck is True:
|
||||
helpers.append(cl_types_check)
|
||||
|
||||
if args.unittest is True:
|
||||
helpers.append(cl_unit_test)
|
||||
|
||||
if args.coveragecheck is True:
|
||||
if args.unittest is True:
|
||||
cl_unit_test.enable_coverage_check = True
|
||||
else:
|
||||
raise RuntimeError("unit-test is required to enable coverage-check")
|
||||
|
||||
if args.qualitycheck is True:
|
||||
helpers.append(cl_quality_check)
|
||||
|
||||
if args.docgen is True:
|
||||
helpers.append(cl_doc_gen)
|
||||
|
||||
if args.docgenpdf is True:
|
||||
if args.docgen is True:
|
||||
cl_doc_gen.enable_gen_pdf = True
|
||||
else:
|
||||
raise RuntimeError("doc-gen is required to enable doc-gen-pdf")
|
||||
|
||||
if args.complexitycheck is True:
|
||||
helpers.append(cl_complexity_check)
|
||||
|
||||
if args.unittest == True:
|
||||
helpers.append(unit_test)
|
||||
project_rootdir_path = Path(os.getcwd()) if args.projectpath is None else Path(args.projectpath)
|
||||
|
||||
if args.coveragecheck == True:
|
||||
if args.unittest == True:
|
||||
unit_test.enable_coverage_check = True
|
||||
else:
|
||||
raise RuntimeError("unit-test is required to enable coverage-check")
|
||||
|
||||
if args.qualitycheck == True:
|
||||
helpers.append(quality_check)
|
||||
|
||||
if args.docgen == True:
|
||||
helpers.append(doc_gen)
|
||||
|
||||
if args.docgenpdf == True:
|
||||
if args.docgen == True:
|
||||
doc_gen.enable_gen_pdf = True
|
||||
else:
|
||||
raise RuntimeError("doc-gen is required to enable doc-gen-pdf")
|
||||
|
||||
if args.complexitycheck == True:
|
||||
helpers.append(complexity_check)
|
||||
|
||||
project_rootdir_path = Path(args.projectpath)
|
||||
print(f"Working directory: {project_rootdir_path}")
|
||||
|
||||
with open(project_rootdir_path / "pyproject.toml", mode="rb") as fp:
|
||||
pyproject = tomli.load(fp)
|
||||
if b_use_tomli:
|
||||
pyproject = tomli.load(fp)
|
||||
else:
|
||||
pyproject = tomllib.load(fp)
|
||||
|
||||
for helper in helpers:
|
||||
helper.set_context(project_rootdir_path, pyproject)
|
||||
|
||||
@@ -1,31 +1,34 @@
|
||||
# pyChaChaDummyProject (c) by chacha
|
||||
# chacha_cicd_helper (c) by chacha
|
||||
#
|
||||
# pyChaChaDummyProject is licensed under a
|
||||
# chacha_cicd_helper 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
|
||||
"""module that handle code complexity measurement"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
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 pprint import pprint
|
||||
from radon.complexity import SCORE # type: ignore
|
||||
from radon.cli import Config # type: ignore
|
||||
from radon.cli.harvest import CCHarvester, HCHarvester, MIHarvester # type: ignore
|
||||
|
||||
from .helper_base import helper_withresults_base
|
||||
from .helper_base import cl_helper_withresults_base
|
||||
|
||||
|
||||
class complexity_check(helper_withresults_base):
|
||||
class cl_complexity_check(cl_helper_withresults_base):
|
||||
"""complexity check implementation class"""
|
||||
|
||||
@classmethod
|
||||
def do_job(cls):
|
||||
def do_job(cls) -> None:
|
||||
"""helper job method implementation"""
|
||||
|
||||
config = Config(
|
||||
exclude="__init__\.py",
|
||||
exclude=r"__init__.py", ##!!!!! . => \. ???
|
||||
ignore=None,
|
||||
order=SCORE,
|
||||
show_closures=False,
|
||||
@@ -38,7 +41,7 @@ class complexity_check(helper_withresults_base):
|
||||
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:
|
||||
with open(cls.get_result_dir() / "MI.json", "w", newline="", encoding="utf8") as oFile:
|
||||
oFile.write(h)
|
||||
|
||||
mean = statistics.mean(_["mi"] for _ in res.values())
|
||||
@@ -53,17 +56,17 @@ class complexity_check(helper_withresults_base):
|
||||
rank = "C"
|
||||
|
||||
RES_MI = {"MeanMaintainability": mean, "MaintainabilityIndex": rank}
|
||||
with open(cls.get_result_dir() / "MI.csv", "w", newline="") as oFile:
|
||||
with open(cls.get_result_dir() / "MI.csv", "w", newline="", encoding="utf8") 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:
|
||||
with open(cls.get_result_dir() / "CC.json", "w", newline="", encoding="utf8") 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:
|
||||
with open(cls.get_result_dir() / "HC.json", "w", newline="", encoding="utf8") as oFile:
|
||||
oFile.write(h)
|
||||
|
||||
16
src/chacha_cicd_helper/coverage_tools.py
Normal file
16
src/chacha_cicd_helper/coverage_tools.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from coverage import Coverage
|
||||
from multiprocessing import Process
|
||||
import os
|
||||
|
||||
|
||||
class CoverageProcess(Process):
|
||||
def run(self):
|
||||
cov = Coverage(config_file=True, data_suffix=os.getpid(), auto_data=True)
|
||||
cov._warn_no_data = False
|
||||
cov.start()
|
||||
|
||||
try:
|
||||
super().run()
|
||||
finally:
|
||||
cov.stop()
|
||||
cov.save()
|
||||
@@ -1,13 +1,14 @@
|
||||
# pyChaChaDummyProject (c) by chacha
|
||||
# chacha_cicd_helper (c) by chacha
|
||||
#
|
||||
# pyChaChaDummyProject is licensed under a
|
||||
# chacha_cicd_helper 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/>.
|
||||
|
||||
"""module that handle code documentation generation"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import shutil
|
||||
import sys
|
||||
@@ -16,19 +17,17 @@ from distutils.dir_util import copy_tree
|
||||
|
||||
import yaml
|
||||
|
||||
try:
|
||||
from yaml import CLoader as Loader, CDumper as Dumper
|
||||
except ImportError:
|
||||
from yaml import Loader, Dumper
|
||||
|
||||
from .helper_base import helper_withresults_base
|
||||
from .helper_base import cl_helper_withresults_base
|
||||
|
||||
|
||||
class doc_gen(helper_withresults_base):
|
||||
class cl_doc_gen(cl_helper_withresults_base):
|
||||
"""documentation generation implementation class"""
|
||||
|
||||
enable_gen_pdf: bool = False
|
||||
|
||||
@classmethod
|
||||
def do_job(cls):
|
||||
def do_job(cls) -> None:
|
||||
"""helper job method implementation"""
|
||||
|
||||
# create doc root dir
|
||||
doc_path = cls.project_rootdir_path / "docs"
|
||||
@@ -60,7 +59,7 @@ class doc_gen(helper_withresults_base):
|
||||
continue
|
||||
|
||||
cls._create_dir(full_doc_path.parent.resolve())
|
||||
with open(full_doc_path, "w+") as fd:
|
||||
with open(full_doc_path, "w+", encoding="utf8") as fd:
|
||||
identifier = ".".join(parts)
|
||||
print("::: " + identifier, file=fd)
|
||||
|
||||
@@ -69,13 +68,13 @@ class doc_gen(helper_withresults_base):
|
||||
# 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
|
||||
# manually process the configuration file.
|
||||
with open(cls.project_rootdir_path / "mkdocs.yml", "r") as mkdocsCfgFile:
|
||||
with open(cls.project_rootdir_path / "mkdocs.yml", "r", encoding="utf8") as mkdocsCfgFile:
|
||||
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 is True:
|
||||
mkdocsCfg["plugins"].append(
|
||||
{
|
||||
"with-pdf": {
|
||||
@@ -87,8 +86,8 @@ class doc_gen(helper_withresults_base):
|
||||
}
|
||||
}
|
||||
)
|
||||
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))
|
||||
with open(cls.project_rootdir_path / "mkdocs.yml", "w", encoding="utf8") as mkdocsCfgFile:
|
||||
mkdocsCfgFile.write(yaml.dump(mkdocsCfg, Dumper=yaml.Dumper, default_flow_style=False, sort_keys=False))
|
||||
|
||||
print(" !! start doc generation")
|
||||
res = cls.run_cmd(cmdopts)
|
||||
|
||||
@@ -1,82 +1,96 @@
|
||||
# pyChaChaDummyProject (c) by chacha
|
||||
# chacha_cicd_helper (c) by chacha
|
||||
#
|
||||
# pyChaChaDummyProject is licensed under a
|
||||
# chacha_cicd_helper 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/>.
|
||||
|
||||
"""module that describe the base helpers class"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING: # Only imports the below statements during type checking
|
||||
from typing import Union
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
class cl_helper_base(ABC):
|
||||
"""helpers base class"""
|
||||
|
||||
class helper_base(ABC):
|
||||
project_rootdir_path: Union[Path, None] = None
|
||||
pyproject: Union[dict, None] = None
|
||||
current_dir: Union[Path, None] = None
|
||||
project_rootdir_path: Path = Path()
|
||||
pyproject: dict = {}
|
||||
|
||||
@classmethod
|
||||
def set_context(cls, project_rootdir_path: Path, pyproject: dict):
|
||||
def set_context(cls, project_rootdir_path: Path, pyproject: dict) -> None:
|
||||
"""method to set contextual fields"""
|
||||
cls.project_rootdir_path = project_rootdir_path
|
||||
cls.pyproject = pyproject
|
||||
cls.current_dir = Path(__file__).parent.absolute()
|
||||
|
||||
@classmethod
|
||||
def get_result_dir(cls):
|
||||
def get_result_dir(cls) -> Union[None, Path]:
|
||||
"""retrieve the result directory path"""
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _create_dir(dirpath: Path):
|
||||
def _create_dir(dirpath: Path) -> None:
|
||||
"""helper method to create a directory"""
|
||||
dirpath = Path(dirpath)
|
||||
if not os.path.exists(dirpath):
|
||||
os.makedirs(dirpath)
|
||||
|
||||
@staticmethod
|
||||
def _reset_dir(dirpath: Path):
|
||||
def _reset_dir(dirpath: Path) -> None:
|
||||
"""helper method to reset a directory"""
|
||||
dirpath = Path(dirpath)
|
||||
if os.path.exists(dirpath):
|
||||
shutil.rmtree(dirpath)
|
||||
os.makedirs(dirpath)
|
||||
|
||||
@classmethod
|
||||
def reset_result_dir(cls):
|
||||
def reset_result_dir(cls) -> None:
|
||||
"""helper method to reset the results directory"""
|
||||
result_dir = cls.get_result_dir()
|
||||
if result_dir != None:
|
||||
if result_dir is not None:
|
||||
cls._reset_dir(result_dir)
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def do_job(cls):
|
||||
def do_job(cls) -> None:
|
||||
"""helper job virtual method"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
def run_cmd_(cls, cmdarray):
|
||||
def run_cmd_(cls, cmdarray: list[str]):
|
||||
"""helper method to run a command (piped output)"""
|
||||
process = subprocess.run(cmdarray, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, check=True)
|
||||
return process.stdout
|
||||
|
||||
@classmethod
|
||||
def run_cmd(cls, cmdarray, silent: bool = False):
|
||||
p = subprocess.run(cmdarray, capture_output=True)
|
||||
"""helper method to run a command"""
|
||||
p = subprocess.run(cmdarray, capture_output=True, check=True)
|
||||
if not silent:
|
||||
print(p.stdout.decode())
|
||||
print(p.stderr.decode())
|
||||
return p.stdout
|
||||
|
||||
|
||||
class helper_withresults_base(helper_base):
|
||||
class cl_helper_withresults_base(cl_helper_base):
|
||||
"""derived class to handle results"""
|
||||
|
||||
helper_results_dir: Union[Path, None] = None
|
||||
|
||||
@classmethod
|
||||
def get_result_dir(cls):
|
||||
if cls.helper_results_dir == None:
|
||||
cls.helper_results_dir = cls.__name__
|
||||
return Path(__file__).parent.parent.absolute() / "helpers-results" / cls.helper_results_dir
|
||||
def get_result_dir(cls) -> Path:
|
||||
"""retrieve the results directory"""
|
||||
if cls.helper_results_dir is None:
|
||||
cls.helper_results_dir = Path(cls.__name__)
|
||||
return cls.project_rootdir_path / "helpers-results" / cls.helper_results_dir
|
||||
|
||||
39
src/chacha_cicd_helper/install_deps.py
Normal file
39
src/chacha_cicd_helper/install_deps.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# chacha_cicd_helper (c) by chacha
|
||||
#
|
||||
# chacha_cicd_helper 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/>.
|
||||
|
||||
"""module that handle code documentation generation"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from pprint import pprint
|
||||
|
||||
from .helper_base import cl_helper_base
|
||||
|
||||
|
||||
class cl_install_deps(cl_helper_base):
|
||||
"""dependencies installer implementation class"""
|
||||
|
||||
|
||||
@classmethod
|
||||
def do_job(cls) -> None:
|
||||
"""helper job method implementation"""
|
||||
deps=[]
|
||||
opt_deps={}
|
||||
if 'project' in cls.pyproject:
|
||||
prj = cls.pyproject["project"]
|
||||
if 'dependencies' in prj:
|
||||
deps = prj['dependencies']
|
||||
if 'optional-dependencies'in prj:
|
||||
opt_deps = prj['optional-dependencies']
|
||||
if len(deps)>0:
|
||||
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade"] + deps)
|
||||
for opt_key in opt_deps.keys():
|
||||
if len(opt_deps[opt_key]) > 0:
|
||||
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade"] + opt_deps[opt_key])
|
||||
@@ -1,41 +1,47 @@
|
||||
# pyChaChaDummyProject (c) by chacha
|
||||
# chacha_cicd_helper (c) by chacha
|
||||
#
|
||||
# pyChaChaDummyProject is licensed under a
|
||||
# chacha_cicd_helper 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/>.
|
||||
|
||||
"""module that handle code quality measurement"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from contextlib import redirect_stdout
|
||||
from contextlib import redirect_stdout, suppress
|
||||
from io import StringIO
|
||||
import re
|
||||
import json
|
||||
from enum import Enum
|
||||
from contextlib import suppress
|
||||
import sys
|
||||
import pandas
|
||||
import csv
|
||||
import copy
|
||||
|
||||
from pylint.lint import Run as pylint_Run
|
||||
import pylint_json2html
|
||||
from pylint.lint import Run as pylint_Run # type: ignore
|
||||
import pylint_json2html # type: ignore
|
||||
import pandas # type: ignore
|
||||
|
||||
from .helper_base import helper_withresults_base
|
||||
from .helper_base import cl_helper_withresults_base
|
||||
|
||||
if TYPE_CHECKING: # Only imports the below statements during type checking
|
||||
from typing import Any
|
||||
|
||||
|
||||
class PyLintMetricNotFound(Warning):
|
||||
pass
|
||||
"""module specific warning"""
|
||||
|
||||
|
||||
class quality_check(helper_withresults_base):
|
||||
PylintMessageList = dict()
|
||||
class cl_quality_check(cl_helper_withresults_base):
|
||||
"""quality check implementation class"""
|
||||
|
||||
PylintMessageList: dict[str, str] = {}
|
||||
|
||||
@classmethod
|
||||
def GetPylintMessageList(cls):
|
||||
Messagelist = dict()
|
||||
def GetPylintMessageList(cls) -> None:
|
||||
"""helper method to parse pylint messages"""
|
||||
Messagelist: dict[str, str] = {}
|
||||
regex = r"^:([a-zA-Z-]+) \(([^\)]+)\)"
|
||||
for line in cls.run_cmd([sys.executable, "-m", "pylint", "--list-msgs"], True).splitlines():
|
||||
if res := re.search(regex, line.decode()):
|
||||
@@ -43,19 +49,22 @@ class quality_check(helper_withresults_base):
|
||||
cls.PylintMessageList = Messagelist
|
||||
|
||||
@staticmethod
|
||||
def TryExtractPYReportMetric(line: str, tag: str):
|
||||
regex = f"^(?:\|{tag}\s*\|)(\d+)(?=\s*|)"
|
||||
def TryExtractPYReportMetric(line: str, tag: str) -> float:
|
||||
"""helper method to parse pylint metrics"""
|
||||
regex = rf"^(?:\|{tag}\s*\|)(\d+)(?=\s*|)"
|
||||
if res := re.search(regex, line):
|
||||
return float(res.group(1))
|
||||
raise PyLintMetricNotFound()
|
||||
|
||||
@classmethod
|
||||
def do_job(cls):
|
||||
def do_job(cls) -> None: # pylint: disable=too-complex,too-many-locals,too-many-branches,too-many-statements
|
||||
"""helper job method implementation"""
|
||||
|
||||
print("checking code quality ...")
|
||||
|
||||
cls.GetPylintMessageList()
|
||||
|
||||
RES_all = dict()
|
||||
RES_all: dict[str, Any] = {}
|
||||
with StringIO() as StdOutput:
|
||||
JsonContent = ""
|
||||
with redirect_stdout(StdOutput):
|
||||
@@ -76,6 +85,8 @@ class quality_check(helper_withresults_base):
|
||||
with open(cls.get_result_dir() / "report.json", "w+", encoding="utf-8") as Outfile:
|
||||
# hacky way of exctracting json + having overall score...
|
||||
class TScanState(Enum):
|
||||
"""Quality report parsing FSM states definition"""
|
||||
|
||||
TEXT_REPORT = 1
|
||||
JSON_REPORT = 2
|
||||
OTHER_REPORT_START = 3
|
||||
@@ -86,12 +97,12 @@ class quality_check(helper_withresults_base):
|
||||
OTHER_REPORT_MESSAGES = 8
|
||||
OTHER_REPORT_END = 99
|
||||
|
||||
RES_all["Statistics"] = dict()
|
||||
RES_all["RawMetrics"] = dict()
|
||||
RES_all["RawMetricsPercent"] = dict()
|
||||
RES_all["Duplication"] = dict()
|
||||
RES_all["MessagesCat"] = dict()
|
||||
RES_all["Messages"] = dict()
|
||||
RES_all["Statistics"] = {}
|
||||
RES_all["RawMetrics"] = {}
|
||||
RES_all["RawMetricsPercent"] = {}
|
||||
RES_all["Duplication"] = {}
|
||||
RES_all["MessagesCat"] = {}
|
||||
RES_all["Messages"] = {}
|
||||
RES_all["GlobalScore"] = -999
|
||||
RES_all["NbAnalysedStatments"] = -999
|
||||
RES_all["NbAnalysedLines"] = -999
|
||||
@@ -180,7 +191,7 @@ class quality_check(helper_withresults_base):
|
||||
if line.startswith("--------"):
|
||||
ScanState = TScanState.OTHER_REPORT_END
|
||||
else:
|
||||
for PylintMessage in cls.PylintMessageList.keys():
|
||||
for PylintMessage in cls.PylintMessageList:
|
||||
with suppress(PyLintMetricNotFound):
|
||||
RES_all["Messages"][PylintMessage] = cls.TryExtractPYReportMetric(line, PylintMessage)
|
||||
|
||||
@@ -192,7 +203,7 @@ class quality_check(helper_withresults_base):
|
||||
raise RuntimeError("Invalid ScanState")
|
||||
Outfile.write(JsonContent)
|
||||
|
||||
with open(cls.get_result_dir() / "metrics.json", "w") as json_file:
|
||||
with open(cls.get_result_dir() / "metrics.json", "w", encoding="utf8") as json_file:
|
||||
json.dump(RES_all, json_file)
|
||||
|
||||
# exporting all Data in one csv, unused atm because jenkins seems not able to select columns from csv an keep displaying all...
|
||||
@@ -201,14 +212,14 @@ class quality_check(helper_withresults_base):
|
||||
del RES_all_trim["Messages"]
|
||||
flat_RES_all = pandas.json_normalize(RES_all_trim, sep="_").to_dict(orient="records")[0]
|
||||
|
||||
with open(cls.get_result_dir() / "metrics.csv", "w", newline="") as csv_file:
|
||||
with open(cls.get_result_dir() / "metrics.csv", "w", newline="", encoding="utf8") as csv_file:
|
||||
writer = csv.DictWriter(csv_file, fieldnames=flat_RES_all.keys())
|
||||
writer.writeheader()
|
||||
writer.writerow(flat_RES_all)
|
||||
|
||||
# splited csv exports for jenkins plots: RawMetricsPercent
|
||||
RES_all_percent = RES_all["RawMetricsPercent"]
|
||||
with open(cls.get_result_dir() / "metrics_rawpercent.csv", "w", newline="") as csv_file:
|
||||
with open(cls.get_result_dir() / "metrics_rawpercent.csv", "w", newline="", encoding="utf8") as csv_file:
|
||||
writer = csv.DictWriter(csv_file, fieldnames=RES_all_percent.keys())
|
||||
writer.writeheader()
|
||||
writer.writerow(RES_all_percent)
|
||||
@@ -219,21 +230,21 @@ class quality_check(helper_withresults_base):
|
||||
RES_all_stats["PersentDuplicatedLines"] = RES_all["Duplication"]["PersentDuplicatedLines"]
|
||||
RES_all_stats["NbAnalysedStatments"] = RES_all["NbAnalysedStatments"]
|
||||
RES_all_stats["NbAnalysedLines"] = RES_all["NbAnalysedLines"]
|
||||
with open(cls.get_result_dir() / "metrics_Statistics.csv", "w", newline="") as csv_file:
|
||||
with open(cls.get_result_dir() / "metrics_Statistics.csv", "w", newline="", encoding="utf8") as csv_file:
|
||||
writer = csv.DictWriter(csv_file, fieldnames=RES_all_stats.keys())
|
||||
writer.writeheader()
|
||||
writer.writerow(RES_all_stats)
|
||||
|
||||
# splited csv exports for jenkins plots: Statistics + Duplication
|
||||
RES_all_MessagesCat = RES_all["MessagesCat"]
|
||||
with open(cls.get_result_dir() / "metrics_MessagesCat.csv", "w", newline="") as csv_file:
|
||||
with open(cls.get_result_dir() / "metrics_MessagesCat.csv", "w", newline="", encoding="utf8") as csv_file:
|
||||
writer = csv.DictWriter(csv_file, fieldnames=RES_all_MessagesCat.keys())
|
||||
writer.writeheader()
|
||||
writer.writerow(RES_all_MessagesCat)
|
||||
|
||||
# splited csv exports for jenkins plots: GlobalScore
|
||||
RES_GlobalScore = {"GlobalScore": RES_all["GlobalScore"]}
|
||||
with open(cls.get_result_dir() / "metrics_GlobalScore.csv", "w", newline="") as csv_file:
|
||||
with open(cls.get_result_dir() / "metrics_GlobalScore.csv", "w", newline="", encoding="utf8") as csv_file:
|
||||
writer = csv.DictWriter(csv_file, fieldnames=RES_GlobalScore.keys())
|
||||
writer.writeheader()
|
||||
writer.writerow(RES_GlobalScore)
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
# pyChaChaDummyProject (c) by chacha
|
||||
# chacha_cicd_helper (c) by chacha
|
||||
#
|
||||
# pyChaChaDummyProject is licensed under a
|
||||
# chacha_cicd_helper 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
|
||||
"""module that handle code type checking"""
|
||||
|
||||
from pathlib import Path
|
||||
from __future__ import annotations
|
||||
|
||||
from mypy import api
|
||||
|
||||
from .helper_base import helper_withresults_base
|
||||
from .helper_base import cl_helper_withresults_base
|
||||
|
||||
|
||||
class types_check(helper_withresults_base):
|
||||
class cl_types_check(cl_helper_withresults_base):
|
||||
"""type check implementation class"""
|
||||
|
||||
JUnitReportName = "junit.xml"
|
||||
|
||||
@classmethod
|
||||
def do_job(cls):
|
||||
def do_job(cls) -> None:
|
||||
"""helper job method implementation"""
|
||||
|
||||
print("checking code typing ...")
|
||||
result = api.run(
|
||||
[ # project path
|
||||
@@ -31,6 +34,7 @@ class types_check(helper_withresults_base):
|
||||
"--explicit-package-bases",
|
||||
# "--strict-equality",
|
||||
# "--check-untyped-defs",
|
||||
"--enable-incomplete-feature=Unpack",
|
||||
# reports generation
|
||||
"--cobertura-xml-report",
|
||||
str(cls.get_result_dir()),
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
# pyChaChaDummyProject (c) by chacha
|
||||
# chacha_cicd_helper (c) by chacha
|
||||
#
|
||||
# pyChaChaDummyProject is licensed under a
|
||||
# chacha_cicd_helper 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/>.
|
||||
|
||||
"""module that handle code unit-test"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
import datetime
|
||||
|
||||
import unittest
|
||||
import xmlrunner
|
||||
from junitparser import JUnitXml
|
||||
from junit2htmlreport import parser as junit2html_parser
|
||||
import xmlrunner # type: ignore
|
||||
from junitparser import JUnitXml # type: ignore
|
||||
from junit2htmlreport import parser as junit2html_parser # type: ignore
|
||||
|
||||
from .helper_base import helper_withresults_base
|
||||
from .helper_base import cl_helper_withresults_base
|
||||
|
||||
|
||||
class unit_test(helper_withresults_base):
|
||||
class cl_unit_test(cl_helper_withresults_base):
|
||||
"""unit test implementation class"""
|
||||
|
||||
enable_coverage_check: bool = False
|
||||
enable_xml_export: bool = True
|
||||
enable_full_xml_export: bool = True
|
||||
@@ -29,18 +32,20 @@ class unit_test(helper_withresults_base):
|
||||
CoverageReportName: str = "test_coverage"
|
||||
|
||||
@classmethod
|
||||
def do_job(cls):
|
||||
if cls.enable_coverage_check == True:
|
||||
import coverage
|
||||
def do_job(cls) -> None:
|
||||
"""helper job method implementation"""
|
||||
|
||||
if cls.enable_coverage_check is True:
|
||||
import coverage # type: ignore # pylint: disable=import-outside-toplevel
|
||||
|
||||
# preparing unittest framework
|
||||
test_loader = unittest.TestLoader()
|
||||
|
||||
if cls.enable_coverage_check == True:
|
||||
if cls.enable_coverage_check is True:
|
||||
# we start coverage now because module files discovery is part of the coverage measurement
|
||||
CoverageReportPath = Path(str(cls.get_result_dir()) + "_coverage")
|
||||
cls._reset_dir(CoverageReportPath)
|
||||
cov = coverage.Coverage(cover_pylib=False, branch=True, source_pkgs=["src." + cls.pyproject["project"]["name"]])
|
||||
cov = coverage.Coverage(config_file=True, source_pkgs=["src." + cls.pyproject["project"]["name"]])
|
||||
cov.start()
|
||||
|
||||
package_tests = test_loader.discover(
|
||||
@@ -55,14 +60,15 @@ class unit_test(helper_withresults_base):
|
||||
testRunner.run(package_tests)
|
||||
|
||||
print("Test Finished")
|
||||
if cls.enable_coverage_check == True:
|
||||
if cls.enable_coverage_check is True:
|
||||
cov.stop()
|
||||
cov.save()
|
||||
cov.combine()
|
||||
cov.html_report(directory=str(CoverageReportPath))
|
||||
cov.xml_report(outfile=(CoverageReportPath / f"{cls.CoverageReportName}.xml"))
|
||||
cov.xml_report(outfile=str(CoverageReportPath / f"{cls.CoverageReportName}.xml"))
|
||||
|
||||
# computing results (Only if xml available)
|
||||
if cls.enable_full_xml_export == True:
|
||||
if cls.enable_full_xml_export is True:
|
||||
print("Full reports generation...")
|
||||
FullReportPath = Path(str(cls.get_result_dir()) + "_full")
|
||||
cls._reset_dir(FullReportPath)
|
||||
|
||||
@@ -16,6 +16,7 @@ print(__name__)
|
||||
print(__package__)
|
||||
|
||||
from src import chacha_cicd_helper
|
||||
from src.chacha_cicd_helper.__main__ import fct_main
|
||||
|
||||
testdir_path = Path(__file__).parent.resolve()
|
||||
|
||||
@@ -29,12 +30,22 @@ class Test_main(unittest.TestCase):
|
||||
self.assertNotEqual(chacha_cicd_helper.__version__, "?.?.?")
|
||||
|
||||
def test_help(self):
|
||||
|
||||
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
|
||||
with self.assertRaises(SystemExit):
|
||||
chacha_cicd_helper.fct_main(["-h", "-pp", str(testdir_path.parent.resolve())])
|
||||
fct_main(["-h", "-pp", str(testdir_path.parent.resolve())])
|
||||
self.assertIn(f"usage: {chacha_cicd_helper.__Name__}", capted_stdout.getvalue())
|
||||
self.assertEqual(capted_stderr.getvalue(), "")
|
||||
|
||||
@unittest.skip
|
||||
def test_help_print(self):
|
||||
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
|
||||
fct_main(["-h", "-pp", str(testdir_path.parent.resolve())])
|
||||
print(capted_stdout.getvalue())
|
||||
print(capted_stderr.getvalue())
|
||||
|
||||
self.assertIn("usage: continuous-integration-helper", capted_stdout.getvalue())
|
||||
self.assertEqual(capted_stderr.getvalue(), "")
|
||||
|
||||
def test_install_deps(self):
|
||||
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
|
||||
fct_main(["--installdeps", "-pp", str(testdir_path.parent.resolve())])
|
||||
print(capted_stdout.getvalue())
|
||||
print(capted_stderr.getvalue())
|
||||
|
||||
Reference in New Issue
Block a user