initial template commit
This commit is contained in:
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# unitest:
|
||||
site
|
||||
docs
|
||||
helpers-results
|
||||
.coverage
|
||||
/.mypy_cache/
|
||||
17
.project
Normal file
17
.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>{{project_name}}</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.python.pydev.PyDevBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.python.pydev.pythonNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
.pydevproject
Normal file
13
.pydevproject
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?eclipse-pydev version="1.0"?><pydev_project>
|
||||
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
|
||||
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
|
||||
|
||||
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
||||
<path>/${PROJECT_DIR_NAME}/src</path>
|
||||
<path>/${PROJECT_DIR_NAME}</path>
|
||||
</pydev_pathproperty>
|
||||
|
||||
</pydev_project>
|
||||
2
.settings/org.eclipse.core.resources.prefs
Normal file
2
.settings/org.eclipse.core.resources.prefs
Normal file
@@ -0,0 +1,2 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
2
.settings/org.eclipse.core.runtime.prefs
Normal file
2
.settings/org.eclipse.core.runtime.prefs
Normal file
@@ -0,0 +1,2 @@
|
||||
eclipse.preferences.version=1
|
||||
line.separator=\n
|
||||
18
Dockerfile
Normal file
18
Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
# 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 debian:bullseye-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
|
||||
507
Jenkinsfile
vendored
Normal file
507
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,507 @@
|
||||
// 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/>.
|
||||
|
||||
// configurable settings:
|
||||
// use to send email if workflow problem
|
||||
def _MaintainerName = "CHACHA"
|
||||
def _MaintainerEmail = "1000CHACHA0001@gmail.com"
|
||||
// SCM credential ID
|
||||
def _SCMCredentials = "7fbf4db8-eb36-447c-b2e4-44da4535295f"
|
||||
// toogle PreRelease flag on Gitea release system
|
||||
def _bPreRelease = false
|
||||
// toogle Draft flag on Gitea release system => If False, TAG is not created
|
||||
def _bDraft = false
|
||||
// release content / changelog management
|
||||
def _bAutoChangelog = false //Not supported yet
|
||||
def _ReleaseContent_Title = "_CI/CD Automatic Release_"
|
||||
// full rebuild toogle
|
||||
def _bFullRebuilt = true
|
||||
def _MkDocsWebURL = "dabauto--mkdocs-web.dmz.chacha.home/mkdocs-web/"
|
||||
def _MkDocsWebCredentials = "2c5b684e-3787-4b37-8aca-b3dd4a383fe2"
|
||||
|
||||
// commands Helper: /!\ Made for GITEA /!\
|
||||
String determineRepoUserName() {
|
||||
return scm.getUserRemoteConfigs()[0].getUrl().tokenize('/')[3].split("\\.")[0]
|
||||
}
|
||||
|
||||
// commands Helper: /!\ Made for GITEA /!\
|
||||
String determineRepoName() {
|
||||
return scm.getUserRemoteConfigs()[0].getUrl().tokenize('/')[4].split("\\.")[0]
|
||||
}
|
||||
|
||||
// commands Helper: /!\ Made for GITEA /!\
|
||||
String ExtractGiteaBaseUrl(inUrl) {
|
||||
def pattern = ~/(.*)((?<=\/)[^\/]+\/)([^\/\.]+(?:\.git)?)\/?$/
|
||||
assert pattern instanceof java.util.regex.Pattern
|
||||
|
||||
def matcher = inUrl =~ pattern
|
||||
assert matcher instanceof java.util.regex.Matcher
|
||||
|
||||
assert matcher.find()
|
||||
assert matcher.size() == 1
|
||||
return matcher[0][1]
|
||||
}
|
||||
|
||||
String ExtractBaseVersion(inVersion) {
|
||||
def pattern = ~/^(?<version>[0-9]+\.[0-9]+\.[0-9]+)(?<prerelease>(?:(?:a|b|rc)[0-9]*)?[\.\-_](?:dev|post)\d*)?$/
|
||||
assert pattern instanceof java.util.regex.Pattern
|
||||
|
||||
def matcher = inVersion =~ pattern
|
||||
assert matcher instanceof java.util.regex.Matcher
|
||||
|
||||
assert matcher.find()
|
||||
assert matcher.size() == 1
|
||||
return matcher[0][1]
|
||||
}
|
||||
|
||||
pipeline {
|
||||
|
||||
// for Docker based build (preferable)
|
||||
agent { dockerfile true }
|
||||
|
||||
// other builds might fall back to generic mode
|
||||
//agent { any }
|
||||
// or even require a specific machine
|
||||
//agent {node {label 'FOO-CIAgent' }}
|
||||
|
||||
options {
|
||||
// enable if the build is using external resource
|
||||
// or any other reason needing it to be exclusive
|
||||
// => enabled by default, disable is if more performance needed
|
||||
disableConcurrentBuilds()
|
||||
}
|
||||
|
||||
environment {
|
||||
// TERM env variable is needed to make processses think we are on a real terminal
|
||||
TERM="xterm"
|
||||
// HOME env variable is needed to allow pip install packages as users
|
||||
HOME="$env.WORKSPACE"
|
||||
// _GIT_BRANCH env variable is needed to get the branch only (and not origin/XXXX), without fetching it
|
||||
_GIT_BRANCH=sh (
|
||||
script: "echo ${GIT_BRANCH} | sed -e 's|origin/||g'",
|
||||
returnStdout: true
|
||||
).trim()
|
||||
// those 2 env var are for conveniance, get from scm url (which **NEED TO BE GITEA**)
|
||||
_PROJECT_USER_NAME=determineRepoUserName()
|
||||
_PROJECT_NAME=determineRepoName()
|
||||
_GITEA_BASE_URL=ExtractGiteaBaseUrl("$GIT_URL")
|
||||
PY_PROJECT_NAME = "__NOTSET__"
|
||||
PY_PROJECT_VERSION = "__NOTSET__"
|
||||
PY_PROJECT_VERSION_STRIPPED = "__NOTSET__"
|
||||
}
|
||||
|
||||
stages {
|
||||
|
||||
stage("Prepare") {
|
||||
steps {
|
||||
script{
|
||||
if (_bFullRebuilt) {
|
||||
// start by cleaning the workspace (not using cleanWs() because we want to keep the directory itself)
|
||||
// => this is needed to fetch it again with custom options
|
||||
sh("find ~/. -name . -o -prune -exec rm -rf -- {} +")
|
||||
}
|
||||
else {
|
||||
sh("find ~/. -name . ! -path './TEST_ENV/*' ! -path './BUILD_ENV/*' -o -prune -exec rm -rf -- {} +")
|
||||
}
|
||||
if(_GIT_BRANCH!="master")
|
||||
{
|
||||
_bPreRelease = true
|
||||
}
|
||||
}
|
||||
// displaying env vars, mainly for debugging purpose
|
||||
echo("GIT_BRANCH:. . . . . . . . . . . $GIT_BRANCH")
|
||||
echo("_GIT_BRANCH: . . . . . . . . . . $_GIT_BRANCH")
|
||||
echo("GIT_URL: . . . . . . . . . . . . $GIT_URL ")
|
||||
echo("_GITEA_BASE_URL: . . . . . . . . $_GITEA_BASE_URL")
|
||||
echo("GIT_COMMIT:. . . . . . . . . . . $GIT_COMMIT ")
|
||||
echo("_PROJECT_USER_NAME:. . . . . . . $_PROJECT_USER_NAME")
|
||||
echo("_GITEA_PROJECT_NAME: . . . . . . $_PROJECT_NAME")
|
||||
echo("_MaintainerEmail:. . . . . . . . $_MaintainerEmail")
|
||||
echo("_MaintainerName:. . . . . . . . $_MaintainerName")
|
||||
|
||||
sh("virtualenv --pip=embed --setuptools=embed --wheel=embed --no-periodic-update --activators bash,python BUILD_ENV")
|
||||
sh("virtualenv --pip=embed --setuptools=embed --wheel=embed --no-periodic-update --activators bash,python TEST_ENV")
|
||||
|
||||
sh(". ~/BUILD_ENV/bin/activate && pip install --upgrade setuptools build pip copier jinja2-slug toml \"setuptools-git-versioning<2\"")
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
stage("GetCode") {
|
||||
steps {
|
||||
dir("gitrepo") {
|
||||
// manually checkout the repository, with All branches and tags
|
||||
// => because we might need them for version and changelog computing
|
||||
checkout([ $class: "GitSCM",
|
||||
branches: [[name: GIT_BRANCH]],
|
||||
extensions: [[$class: "CloneOption", noTags: false, shallow: false, depth: 0, reference: '']],
|
||||
userRemoteConfigs: [[credentialsId: _SCMCredentials, url: GIT_URL]]])
|
||||
script{
|
||||
if(_GIT_BRANCH=="master")
|
||||
{
|
||||
if(sh(returnStdout: true, script: "git tag --points-at HEAD").trim().isEmpty())
|
||||
{
|
||||
error("master push/merge must have an explicit tag release number")
|
||||
}
|
||||
}
|
||||
|
||||
// using git to get the latest tag on this branch
|
||||
def latestTag = sh(returnStdout: true, script: "git tag --sort=-creatordate | head -n 1").trim()
|
||||
echo("latestTag:. . . . . . . . . . . . $latestTag ")
|
||||
|
||||
// using setuptools_git_versioning to get the generated version number based on Git tags and logs
|
||||
// TODO: read dev_template from toml
|
||||
PY_PROJECT_VERSION = sh(script: """#!/bin/sh -
|
||||
|. ~/BUILD_ENV/bin/activate
|
||||
|exec python - << '__EOWRAPPER__'
|
||||
|
|
||||
|from setuptools_git_versioning import version_from_git
|
||||
|
|
||||
|print(str(version_from_git(tag_filter="^\\d+\\.\\d+\\.\\d+\$",dev_template = "{tag}.post{ccount}")),end ="")
|
||||
|
|
||||
|__EOWRAPPER__
|
||||
""".stripMargin(),
|
||||
returnStdout: true)
|
||||
echo("PY_PROJECT_VERSION: . . . . . . . . . $PY_PROJECT_VERSION")
|
||||
PY_PROJECT_VERSION_STRIPPED=ExtractBaseVersion(PY_PROJECT_VERSION)
|
||||
|
||||
// specific handling to test the template itself
|
||||
// => little hacky... creating a new git repo with a commit/tag corresponding to HEAD of the official one
|
||||
if(_PROJECT_NAME=="pyChaChaDummyProject") //specific case to test the template itself
|
||||
{
|
||||
sh("git config --global user.email $_MaintainerEmail")
|
||||
sh("git config --global user.name $_MaintainerName")
|
||||
|
||||
sh("rm -Rf ~/_gitrepo || true")
|
||||
|
||||
//sh(". ~/BUILD_ENV/bin/activate && python -m copier --vcs-ref HEAD --prereleases --defaults ./ ~/_gitrepo")
|
||||
sh(script: """#!/bin/sh -
|
||||
|. ~/BUILD_ENV/bin/activate
|
||||
|exec python - << '__EOWRAPPER__'
|
||||
|
|
||||
|import dunamai
|
||||
|
|
||||
|# override default dunamai version pattern to make leading 'v' optionnal
|
||||
|dunamai.VERSION_SOURCE_PATTERN = r'''
|
||||
| (?x) (?# ignore whitespace)
|
||||
| ^v?((?P<epoch>\\d+)!)?(?P<base>\\d+(\\.\\d+)*) (?# v1.2.3 or v1!2000.1.2)
|
||||
| ([-._]?((?P<stage>[a-zA-Z]+)[-._]?(?P<revision>\\d+)?))? (?# b0)
|
||||
| (\\+(?P<tagged_metadata>.+))?\$ (?# +linux)
|
||||
|'''.strip()
|
||||
|
|
||||
|import copier
|
||||
|copier.run_auto("./", "../_gitrepo",vcs_ref="HEAD",use_prereleases=True,defaults=True,cleanup_on_error=False)
|
||||
|
|
||||
|__EOWRAPPER__
|
||||
""".stripMargin())
|
||||
|
||||
// we can not remove the directory because Jenkins is inside...
|
||||
// => so we painfully remove the inside stuff
|
||||
sh("find ~/gitrepo/ -mindepth 1 -type f -exec rm {} \\;")
|
||||
sh("find ~/gitrepo/ -mindepth 1 -type d -empty -delete")
|
||||
|
||||
sh("cp -a ~/_gitrepo/. ~/gitrepo")
|
||||
sh("git init")
|
||||
sh("git add .")
|
||||
sh("git commit -m \"init repo\"")
|
||||
sh("git tag ${PY_PROJECT_VERSION}")
|
||||
}
|
||||
|
||||
|
||||
PY_PROJECT_NAME = sh(script: """#!/bin/sh -
|
||||
|. ~/BUILD_ENV/bin/activate
|
||||
|exec python - << '__EOWRAPPER__'
|
||||
|
|
||||
|import toml
|
||||
|
|
||||
|with open("pyproject.toml") as pyproject:
|
||||
| pyproject_data = toml.load(pyproject)
|
||||
| print(pyproject_data["project"]["name"], end ="")
|
||||
|
|
||||
|__EOWRAPPER__
|
||||
""".stripMargin(),
|
||||
returnStdout: true)
|
||||
|
||||
echo("PY_PROJECT_NAME: . . . . . . . . . $PY_PROJECT_NAME")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage("BuildPackage") {
|
||||
steps {
|
||||
// no need for a build-env: setuptools is already creating one
|
||||
dir("gitrepo") {
|
||||
script{
|
||||
// actually doing the package build
|
||||
sh(". ~/BUILD_ENV/bin/activate && python -m build .")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage("Install") {
|
||||
steps {
|
||||
dir("gitrepo") {
|
||||
script {
|
||||
// searching for the built package
|
||||
def wheelPath = findFiles(glob: "**/dist/*.whl")[0]
|
||||
echo "wheel artifact path: $wheelPath"
|
||||
// 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]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage("CheckCode") {
|
||||
steps {
|
||||
dir("gitrepo") {
|
||||
sh(". ~/TEST_ENV/bin/activate && python -m helpers --type-check --quality-check")
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
dir("gitrepo") {
|
||||
publishHTML([
|
||||
reportDir: "helpers-results/quality_check",
|
||||
reportFiles: "report.html",
|
||||
reportName: "quality-report",
|
||||
allowMissing: false,
|
||||
alwaysLinkToLastBuild: true,
|
||||
keepAll: true])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("PlotMetrics") {
|
||||
steps {
|
||||
|
||||
plot([ csvFileName: 'plot-df7f03dc-8146-11ed-a1eb-0242ac120002.csv',
|
||||
csvSeries: [[ file: 'gitrepo/helpers-results/quality_check/metrics_GlobalScore.csv', inclusionFlag: 'OFF', url: '']],
|
||||
group: 'metrics',
|
||||
title: 'code quality score',
|
||||
style: 'line',
|
||||
keepRecords: true,
|
||||
numBuilds: '',
|
||||
yaxisMaximum: '10',
|
||||
yaxisMinimum: '0'])
|
||||
|
||||
plot([ csvFileName: 'plot-c731cc84-8145-11ed-a1eb-0242ac120002.csv',
|
||||
csvSeries: [[ file: 'gitrepo/helpers-results/quality_check/metrics_rawpercent.csv', inclusionFlag: 'OFF', url: '']],
|
||||
group: 'metrics',
|
||||
title: 'code composition (%)',
|
||||
style: 'stackedArea',
|
||||
keepRecords: true,
|
||||
numBuilds: '',
|
||||
yaxisMaximum: '1',
|
||||
yaxisMinimum: '0'])
|
||||
|
||||
plot([ csvFileName: 'plot-cac33982-8145-11ed-a1eb-0242ac120002.csv',
|
||||
csvSeries: [[ file: 'gitrepo/helpers-results/quality_check/metrics_Statistics.csv', inclusionFlag: 'OFF', url: '']],
|
||||
group: 'metrics',
|
||||
title: 'general statistics',
|
||||
style: 'line',
|
||||
keepRecords: true,
|
||||
numBuilds: ''])
|
||||
|
||||
plot([ csvFileName: 'plot-cddaced2-8145-11ed-a1eb-0242ac120002.csv',
|
||||
csvSeries: [[ file: 'gitrepo/helpers-results/quality_check/metrics_MessagesCat.csv', inclusionFlag: 'OFF', url: '']],
|
||||
group: 'metrics',
|
||||
title: 'quality warnings',
|
||||
style: 'stackedArea',
|
||||
keepRecords: true,
|
||||
numBuilds: ''])
|
||||
}
|
||||
}
|
||||
|
||||
stage("RunUnitTests") {
|
||||
steps {
|
||||
dir("gitrepo") {
|
||||
sh(". ~/TEST_ENV/bin/activate && python -m helpers --unit-test --coverage-check")
|
||||
script {
|
||||
unit_test_full_name__html=findFiles(glob: "helpers-results/unit_test_full/*.html")[0].getName()
|
||||
println unit_test_full_name__html
|
||||
unit_test_full_name__xml=findFiles(glob: "helpers-results/unit_test_full/*.xml")[0].getName()
|
||||
println unit_test_full_name__xml
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
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")]
|
||||
publishHTML([
|
||||
reportDir: "helpers-results/unit_test_coverage",
|
||||
reportFiles: "index.html",
|
||||
reportName: "coverage-report-html",
|
||||
allowMissing: false,
|
||||
alwaysLinkToLastBuild: true,
|
||||
keepAll: true])
|
||||
|
||||
publishHTML([
|
||||
reportDir: "helpers-results/unit_test_full",
|
||||
reportFiles: unit_test_full_name__html,
|
||||
reportName: "test-reports-full",
|
||||
allowMissing: false,
|
||||
alwaysLinkToLastBuild: true,
|
||||
keepAll: true])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage("GenDOC") {
|
||||
steps {
|
||||
dir("gitrepo") {
|
||||
//--doc-gen-pdf
|
||||
sh(". ~/TEST_ENV/bin/activate && python -m helpers --doc-gen --doc-gen-pdf")
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
dir("gitrepo") {
|
||||
publishHTML([
|
||||
reportDir: "helpers-results/doc_gen/site",
|
||||
reportFiles: "index.html",
|
||||
reportName: "doc-html",
|
||||
allowMissing: false,
|
||||
alwaysLinkToLastBuild: true,
|
||||
keepAll: true])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage("PostRelease") {
|
||||
environment {
|
||||
def GITEA_LOGIN_TOKEN=credentials("GiteaCHACHAPush")
|
||||
}
|
||||
steps {
|
||||
sh("python3 -m pip install simple_rest_client requests")
|
||||
dir("gitrepo") {
|
||||
script {
|
||||
def CurrentDateTime=java.time.LocalDateTime.now()
|
||||
withCredentials([string( credentialsId: _MkDocsWebCredentials,variable: 'MKDOCSTOKEN' )])
|
||||
{
|
||||
sh(script: """#!/usr/bin/env python3
|
||||
|from simple_rest_client.api import API
|
||||
|from simple_rest_client.resource import Resource
|
||||
|import json
|
||||
|import glob
|
||||
|import requests
|
||||
|import shutil
|
||||
|
|
||||
|from urllib.parse import urljoin
|
||||
|
|
||||
|class GiteaRepoCommits(Resource):
|
||||
| actions = {
|
||||
| 'get': {'method': 'GET', 'url': '/repos/{}/{}//git/commits/{}'},
|
||||
|}
|
||||
|
|
||||
|class GiteaRepoReleases(Resource):
|
||||
| actions = {
|
||||
| 'get': {'method': 'GET', 'url': '/repos/{}/{}/releases'},
|
||||
| 'post': {'method': 'POST', 'url': '/repos/{}/{}/releases'},
|
||||
|}
|
||||
|
|
||||
|class GiteaRepoReleaseAssets(Resource):
|
||||
| actions = {
|
||||
| 'get': {'method': 'GET', 'url': '/repos/{}/{}/releases/{}/assets'},
|
||||
| 'post': {'method': 'POST', 'url': '/repos/{}/{}/releases/{}/assets'},
|
||||
|}
|
||||
|
|
||||
|class GiteaRepoReleaseAssetAttachments(Resource):
|
||||
| actions = {
|
||||
| 'get': {'method': 'GET', 'url': '/repos/{}/{}/releases/{}/assets/{}'},
|
||||
|}
|
||||
|
|
||||
|class _GiteaApi(API):
|
||||
| def __init__(self,**args):
|
||||
| super().__init__(**args)
|
||||
| self.add_resource(resource_name="commits", resource_class = GiteaRepoCommits)
|
||||
| self.add_resource(resource_name="releases", resource_class = GiteaRepoReleases)
|
||||
| self.add_resource(resource_name="assets", resource_class = GiteaRepoReleaseAssets)
|
||||
| self.add_resource(resource_name="attachments", resource_class = GiteaRepoReleaseAssetAttachments)
|
||||
|
|
||||
|GiteaApi = _GiteaApi(
|
||||
| api_root_url=urljoin('${_GITEA_BASE_URL}','api/v1/'),
|
||||
| headers={"Authorization":"token ${GITEA_LOGIN_TOKEN_PSW}"},
|
||||
|)
|
||||
|
|
||||
|
|
||||
|ReleaseContent = "${_ReleaseContent_Title}" + "\\n" \\
|
||||
| + "\\n" \\
|
||||
| + "Reference documentation: [mkdocs page](https://chacha.ddns.net/mkdocs-web/${_PROJECT_USER_NAME}/${PY_PROJECT_NAME}/${_GIT_BRANCH}/${PY_PROJECT_VERSION_STRIPPED}/) "
|
||||
|
|
||||
|data={
|
||||
| "body": ReleaseContent,
|
||||
| "draft": bool("${_bDraft}"=="true"),
|
||||
| "name": "${PY_PROJECT_NAME}_${CurrentDateTime}",
|
||||
| "prerelease": bool("${_bPreRelease}"=="true"),
|
||||
| "tag_name": "${PY_PROJECT_VERSION}",
|
||||
| "target_commitish": "${GIT_COMMIT}",
|
||||
|}
|
||||
|new_release_id = GiteaApi.releases.post("${_PROJECT_USER_NAME}","${PY_PROJECT_NAME}",json=data).body['id']
|
||||
|
|
||||
|data = {
|
||||
| "name": "Wheel Package",
|
||||
| 'attachment': (glob.glob('dist/*.whl')[0], open(glob.glob('dist/*.whl')[0], 'rb')),
|
||||
|}
|
||||
|GiteaApi.assets.post("${_PROJECT_USER_NAME}","${PY_PROJECT_NAME}",new_release_id,files=data)
|
||||
|
|
||||
|data = {
|
||||
| "name": "Documentation (pdf)",
|
||||
| 'attachment': ("${PY_PROJECT_NAME}_${PY_PROJECT_VERSION}_UserManual.pdf", open("helpers-results/doc_gen/site/pdf/manual.pdf", 'rb')),
|
||||
|}
|
||||
|GiteaApi.assets.post("${_PROJECT_USER_NAME}","${PY_PROJECT_NAME}",new_release_id,files=data)
|
||||
|
|
||||
|shutil.make_archive("doc", 'zip', "helpers-results/doc_gen/site")
|
||||
|reqData={
|
||||
| "SECRET": "${MKDOCSTOKEN}",
|
||||
| "USER": "${_PROJECT_USER_NAME}",
|
||||
| "PACKAGE": "${PY_PROJECT_NAME}",
|
||||
| "BRANCH": "${_GIT_BRANCH}",
|
||||
| "VERSION": "${PY_PROJECT_VERSION}",
|
||||
|}
|
||||
|files = {'DATA': open('doc.zip','rb')}
|
||||
|response=requests.post("http://${_MkDocsWebURL}/API.php?REQ=pushDoc",data=reqData,files=files)
|
||||
|if response.status_code != 200:
|
||||
| raise RuntimeError(f"Wrong server response: {response.status_code}")
|
||||
|
|
||||
""".stripMargin())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
failure {
|
||||
emailext body: 'Check console output at $BUILD_URL to view the results. \n\n ${CHANGES} \n\n -------------------------------------------------- \n${BUILD_LOG, maxLines=100, escapeHtml=false}',
|
||||
to: _MaintainerEmail,
|
||||
subject: 'Build failed in Jenkins: $PROJECT_NAME - #$BUILD_NUMBER'
|
||||
}
|
||||
unstable {
|
||||
emailext body: 'Check console output at $BUILD_URL to view the results. \n\n ${CHANGES} \n\n -------------------------------------------------- \n${BUILD_LOG, maxLines=100, escapeHtml=false}',
|
||||
to: _MaintainerEmail,
|
||||
subject: 'Unstable build in Jenkins: $PROJECT_NAME - #$BUILD_NUMBER'
|
||||
}
|
||||
changed {
|
||||
emailext body: 'Check console output at $BUILD_URL to view the results.',
|
||||
to: _MaintainerEmail,
|
||||
subject: 'Jenkins build is back to normal: $PROJECT_NAME - #$BUILD_NUMBER'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
175
LICENSE.md
Normal file
175
LICENSE.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# License
|
||||
|
||||
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
||||
|
||||
Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.
|
||||
|
||||
**Using Creative Commons Public Licenses**
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses.
|
||||
|
||||
* __Considerations for licensors:__ Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. [More considerations for licensors](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors).
|
||||
|
||||
* __Considerations for the public:__ By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. [More considerations for the public](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees).
|
||||
|
||||
## Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
|
||||
|
||||
### Section 1 – Definitions.
|
||||
|
||||
a. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
|
||||
|
||||
b. __Adapter's License__ means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. __BY-NC-SA Compatible License__ means a license listed at [creativecommons.org/compatiblelicenses](http://creativecommons.org/compatiblelicenses), approved by Creative Commons as essentially the equivalent of this Public License.
|
||||
|
||||
d. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
|
||||
|
||||
e. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
|
||||
|
||||
f. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
|
||||
|
||||
g. __License Elements__ means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike.
|
||||
|
||||
h. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
|
||||
|
||||
i. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
j. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License.
|
||||
|
||||
k. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.
|
||||
|
||||
l. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
|
||||
|
||||
m. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
|
||||
|
||||
n. __You__ means the individual or entity exercising the Licensed Rights under this Public License. __Your__ has a corresponding meaning.
|
||||
|
||||
### Section 2 – Scope.
|
||||
|
||||
a. ___License grant.___
|
||||
|
||||
1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and
|
||||
|
||||
B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only.
|
||||
|
||||
2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
|
||||
|
||||
3. __Term.__ The term of this Public License is specified in Section 6(a).
|
||||
|
||||
4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
|
||||
|
||||
5. __Downstream recipients.__
|
||||
|
||||
A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
|
||||
|
||||
B. __Additional offer from the Licensor – Adapted Material.__ Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply.
|
||||
|
||||
C. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
|
||||
|
||||
6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. ___Other rights.___
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.
|
||||
|
||||
### Section 3 – License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the following conditions.
|
||||
|
||||
a. ___Attribution.___
|
||||
|
||||
1. If You Share the Licensed Material (including in modified form), You must:
|
||||
|
||||
A. retain the following if it is supplied by the Licensor with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
|
||||
|
||||
B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
|
||||
|
||||
C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
|
||||
|
||||
b. ___ShareAlike.___
|
||||
|
||||
In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply.
|
||||
|
||||
1. The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License.
|
||||
|
||||
2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material.
|
||||
|
||||
3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply.
|
||||
|
||||
### Section 4 – Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;
|
||||
|
||||
b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
|
||||
|
||||
### Section 5 – Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__
|
||||
|
||||
b. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
|
||||
|
||||
### Section 6 – Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
|
||||
|
||||
### Section 7 – Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
|
||||
|
||||
### Section 8 – Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
|
||||
|
||||
> Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at [creativecommons.org/policies](http://creativecommons.org/policies), Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
|
||||
>
|
||||
> Creative Commons may be contacted at creativecommons.org
|
||||
48
README.md
Normal file
48
README.md
Normal file
@@ -0,0 +1,48 @@
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
# Python project template
|
||||
|
||||
A nice template to start a blank python projet.
|
||||
|
||||
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/pychachadummyproject/{{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
|
||||
BIN
docs-static/Library.jpg
Normal file
BIN
docs-static/Library.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
16
docs-static/usage.md
Normal file
16
docs-static/usage.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# 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.
|
||||
|
||||
## 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.
|
||||
|
||||
## 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.
|
||||
1
helpers/.gitignore
vendored
Normal file
1
helpers/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/.mypy_cache/
|
||||
7
helpers/__init__.py
Normal file
7
helpers/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# 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/>.
|
||||
104
helpers/__main__.py
Normal file
104
helpers/__main__.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# 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 tomli
|
||||
import argparse
|
||||
import os
|
||||
import logging
|
||||
import sys
|
||||
|
||||
if __package__=="helpers":
|
||||
# when calling the module from: > python -m helpers
|
||||
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 .changelog_gen import changelog_gen
|
||||
else:
|
||||
# when calling the __main__.py file (from IDE)
|
||||
from helpers.types_check import types_check
|
||||
from helpers.quality_check import quality_check
|
||||
from helpers.unit_test import unit_test
|
||||
from helpers.doc_gen import doc_gen
|
||||
from helpers.changelog_gen import changelog_gen
|
||||
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
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)
|
||||
|
||||
parser = argparse.ArgumentParser( prog = 'continuous-integration-helper',
|
||||
description = 'A tiny set of scripts to help continous integration on python')
|
||||
|
||||
parser.add_argument('-tc', '--type-check', dest='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)')
|
||||
|
||||
parser.add_argument('-qc', '--quality-check', dest='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)')
|
||||
|
||||
parser.add_argument('-clg', '--changelog-gen', dest='changeloggen', action='store_true', help='enable changelog generation')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
##################################
|
||||
# Dev / Debug forced toogles
|
||||
#
|
||||
#--------------------------------
|
||||
#
|
||||
#args.typecheck = True
|
||||
#args.qualitycheck = True
|
||||
#args.unittest = True
|
||||
#args.coveragecheck = True
|
||||
#args.docgen = True
|
||||
#args.docgenpdf = True
|
||||
#args.changeloggen = True
|
||||
|
||||
helpers = []
|
||||
if args.typecheck == True:
|
||||
helpers.append(types_check)
|
||||
|
||||
if args.unittest == True:
|
||||
helpers.append(unit_test)
|
||||
|
||||
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.changeloggen == True:
|
||||
helpers.append(changelog_gen)
|
||||
|
||||
for helper in helpers:
|
||||
helper.set_context(project_rootdir_path,pyproject)
|
||||
helper.reset_result_dir()
|
||||
helper.do_job()
|
||||
|
||||
24
helpers/changelog_gen.py
Normal file
24
helpers/changelog_gen.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# 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
|
||||
103
helpers/doc_gen.py
Normal file
103
helpers/doc_gen.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# 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
|
||||
|
||||
import shutil
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
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
|
||||
|
||||
class doc_gen(helper_withresults_base):
|
||||
enable_gen_pdf: bool = False
|
||||
|
||||
@classmethod
|
||||
def do_job(cls):
|
||||
print(cls.project_rootdir_path)
|
||||
print()
|
||||
|
||||
# create doc root dir
|
||||
doc_path = cls.project_rootdir_path/"docs"
|
||||
cls._reset_dir(doc_path)
|
||||
|
||||
site_path = cls.get_result_dir()/"site"
|
||||
cls._reset_dir(site_path)
|
||||
|
||||
# copy files from main project dir
|
||||
shutil.copyfile(str(cls.project_rootdir_path/"README.md"),str(doc_path/"README.md"))
|
||||
shutil.copyfile(str(cls.project_rootdir_path/"LICENSE.md"),str(doc_path/"LICENSE.md"))
|
||||
|
||||
# copy files from static-doc dir
|
||||
copy_tree(str(cls.project_rootdir_path/"docs-static"),str(doc_path))
|
||||
|
||||
# generating API doc + nav from python docstrings
|
||||
reference_path = doc_path / "reference"
|
||||
cls._reset_dir(reference_path)
|
||||
|
||||
for path in sorted((cls.project_rootdir_path / "src").rglob("*.py")):
|
||||
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")
|
||||
full_doc_path = Path(reference_path, doc_path)
|
||||
|
||||
parts = list(module_path.parts)
|
||||
|
||||
if parts[-1] == "__init__":
|
||||
parts = parts[:-1]
|
||||
elif parts[-1] == "__main__":
|
||||
continue
|
||||
|
||||
cls._reset_dir(os.path.dirname(full_doc_path))
|
||||
with open(full_doc_path, "w+") as fd:
|
||||
identifier = "src."+".".join(parts)
|
||||
print("::: " + identifier, file=fd)
|
||||
|
||||
|
||||
cmdopts = [f"{sys.executable}","-m","mkdocs","-v","build","--site-dir",str(site_path),"--clean"]
|
||||
|
||||
# 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.
|
||||
mkdocsCfg=None
|
||||
with open(cls.project_rootdir_path / "mkdocs.yml",'r') as mkdocsCfgFile:
|
||||
mkdocsCfg = yaml.load(mkdocsCfgFile, Loader=yaml.SafeLoader)
|
||||
|
||||
if cls.enable_gen_pdf==True:
|
||||
mkdocsCfg['plugins'].append({ 'with-pdf':{
|
||||
'cover_subtitle': 'User Manual',
|
||||
'cover_logo': str(cls.project_rootdir_path / 'docs-static' / 'Library.jpg'),
|
||||
'verbose': False,
|
||||
'media_type': 'print',
|
||||
'exclude_pages': ['LICENSE'],
|
||||
'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:
|
||||
mkdocsCfgFile.write(yaml.dump(mkdocsCfg, Dumper=Dumper,default_flow_style=False, sort_keys=False))
|
||||
|
||||
|
||||
res=cls.run_cmd(cmdopts)
|
||||
print(res.decode())
|
||||
print(' !! done')
|
||||
|
||||
72
helpers/helper_base.py
Normal file
72
helpers/helper_base.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# 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 abc import ABC,abstractmethod
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
if TYPE_CHECKING: # Only imports the below statements during type checking
|
||||
from typing import Union
|
||||
|
||||
class helper_base(ABC):
|
||||
project_rootdir_path: Union[Path,None] = None
|
||||
pyproject: Union[dict,None] = None
|
||||
current_dir: Union[Path,None] = None
|
||||
|
||||
@classmethod
|
||||
def set_context(cls,project_rootdir_path:Path, pyproject:dict):
|
||||
cls.project_rootdir_path = project_rootdir_path
|
||||
cls.pyproject = pyproject
|
||||
cls.current_dir = Path(__file__).parent.absolute()
|
||||
|
||||
@classmethod
|
||||
def get_result_dir(cls):
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _reset_dir(dirpath:Path):
|
||||
dirpath = Path(dirpath)
|
||||
if not os.path.exists(dirpath):
|
||||
os.makedirs(dirpath)
|
||||
[f.unlink() for f in Path(dirpath).glob("*") if f.is_file()]
|
||||
|
||||
@classmethod
|
||||
def reset_result_dir(cls):
|
||||
result_dir = cls.get_result_dir()
|
||||
if result_dir != None:
|
||||
cls._reset_dir(result_dir)
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def do_job(cls):
|
||||
raise NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
def run_cmd_(cls, cmdarray):
|
||||
process = subprocess.run(cmdarray, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, check=True)
|
||||
return process.stdout
|
||||
|
||||
@classmethod
|
||||
def run_cmd(cls, cmdarray):
|
||||
p = subprocess.run(cmdarray, capture_output=True)
|
||||
print(p.stdout)
|
||||
print(p.stderr)
|
||||
return p.stdout
|
||||
|
||||
class helper_withresults_base(helper_base):
|
||||
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
|
||||
221
helpers/quality_check.py
Normal file
221
helpers/quality_check.py
Normal file
@@ -0,0 +1,221 @@
|
||||
# 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 contextlib import redirect_stdout
|
||||
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 .helper_base import helper_withresults_base
|
||||
|
||||
class PyLintMetricNotFound(Warning):
|
||||
pass
|
||||
|
||||
class quality_check(helper_withresults_base):
|
||||
PylintMessageList=dict()
|
||||
|
||||
@classmethod
|
||||
def GetPylintMessageList(cls):
|
||||
Messagelist=dict()
|
||||
regex = r"^:([a-zA-Z-]+) \(([^\)]+)\)"
|
||||
for line in cls.run_cmd([sys.executable,"-m","pylint","--list-msgs"]).splitlines():
|
||||
if res:=re.search(regex,line.decode()):
|
||||
Messagelist[res.group(1)]=res.group(2)
|
||||
cls.PylintMessageList = Messagelist
|
||||
|
||||
@staticmethod
|
||||
def TryExtractPYReportMetric(line: str,tag: str):
|
||||
regex=f"^(?:\|{tag}\s*\|)(\d+)(?=\s*|)"
|
||||
if res:=re.search(regex,line):
|
||||
return float(res.group(1))
|
||||
raise PyLintMetricNotFound()
|
||||
|
||||
@classmethod
|
||||
def do_job(cls):
|
||||
print("checking code quality ...")
|
||||
cls.GetPylintMessageList()
|
||||
|
||||
RES_all=dict()
|
||||
with StringIO() as StdOutput:
|
||||
JsonContent=""
|
||||
with redirect_stdout(StdOutput):
|
||||
pylint_Run(['--output-format=json,parseable',
|
||||
'--disable=invalid-name',
|
||||
'--ignore=_version.py',
|
||||
'--reports=y',
|
||||
'--score=yes',
|
||||
'--max-line-length=120',
|
||||
'src.' + cls.pyproject['project']['name']], exit=False)
|
||||
|
||||
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):
|
||||
TEXT_REPORT = 1
|
||||
JSON_REPORT = 2
|
||||
OTHER_REPORT_START = 3
|
||||
OTHER_REPORT_STATISTICS = 4
|
||||
OTHER_REPORT_METRICS = 5
|
||||
OTHER_REPORT_DUPLICATION = 6
|
||||
OTHER_REPORT_MESSAGES_CAT = 7
|
||||
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['GlobalScore'] = -999
|
||||
RES_all['NbAnalysedStatments'] = -999
|
||||
RES_all['NbAnalysedLines'] = -999
|
||||
|
||||
ScanState = TScanState.TEXT_REPORT
|
||||
for line in StdOutput.getvalue().split('\n'):
|
||||
print(line)
|
||||
if ScanState == TScanState.TEXT_REPORT:
|
||||
# ignoring this part, we need json
|
||||
if line=='[':
|
||||
JsonContent+=line
|
||||
ScanState = TScanState.JSON_REPORT
|
||||
elif line=='[]':
|
||||
JsonContent+=line
|
||||
ScanState = TScanState.OTHER_REPORT_START
|
||||
|
||||
elif ScanState == TScanState.JSON_REPORT:
|
||||
JsonContent+=line
|
||||
if line==']':
|
||||
ScanState = TScanState.OTHER_REPORT_START
|
||||
|
||||
elif ScanState == TScanState.OTHER_REPORT_START:
|
||||
if res:=re.search(r"^(\d+)(?= statements analysed.)",line):
|
||||
RES_all['NbAnalysedStatments'] = float(res.group(1))
|
||||
if line == "Statistics by type":
|
||||
ScanState = TScanState.OTHER_REPORT_STATISTICS
|
||||
|
||||
elif ScanState == TScanState.OTHER_REPORT_STATISTICS:
|
||||
if res:=re.search(r"^(\d+)(?= lines have been analyzed)",line):
|
||||
RES_all['NbAnalysedLines' ]= float(res.group(1))
|
||||
elif line == "Raw metrics":
|
||||
ScanState = TScanState.OTHER_REPORT_METRICS
|
||||
else:
|
||||
with suppress(PyLintMetricNotFound): RES_all['Statistics']['module'] = cls.TryExtractPYReportMetric(line,"module")
|
||||
with suppress(PyLintMetricNotFound): RES_all['Statistics']['class'] = cls.TryExtractPYReportMetric(line,"class")
|
||||
with suppress(PyLintMetricNotFound): RES_all['Statistics']['method'] = cls.TryExtractPYReportMetric(line,"method")
|
||||
with suppress(PyLintMetricNotFound): RES_all['Statistics']['function'] = cls.TryExtractPYReportMetric(line,"function")
|
||||
|
||||
elif ScanState == TScanState.OTHER_REPORT_METRICS:
|
||||
if line == "Duplication":
|
||||
RES_all['RawMetricsPercent']['code'] = RES_all['RawMetrics']['code'] / RES_all['NbAnalysedLines' ]
|
||||
RES_all['RawMetricsPercent']['docstring'] = RES_all['RawMetrics']['docstring'] / RES_all['NbAnalysedLines' ]
|
||||
RES_all['RawMetricsPercent']['comment'] = RES_all['RawMetrics']['comment'] / RES_all['NbAnalysedLines' ]
|
||||
RES_all['RawMetricsPercent']['empty'] = RES_all['RawMetrics']['empty'] / RES_all['NbAnalysedLines' ]
|
||||
ScanState = TScanState.OTHER_REPORT_DUPLICATION
|
||||
else:
|
||||
with suppress(PyLintMetricNotFound): RES_all['RawMetrics']['code'] = cls.TryExtractPYReportMetric(line,"code")
|
||||
with suppress(PyLintMetricNotFound): RES_all['RawMetrics']['docstring'] = cls.TryExtractPYReportMetric(line,"docstring")
|
||||
with suppress(PyLintMetricNotFound): RES_all['RawMetrics']['comment'] = cls.TryExtractPYReportMetric(line,"comment")
|
||||
with suppress(PyLintMetricNotFound): RES_all['RawMetrics']['empty'] = cls.TryExtractPYReportMetric(line,"empty")
|
||||
|
||||
elif ScanState == TScanState.OTHER_REPORT_DUPLICATION:
|
||||
if line == "Messages by category":
|
||||
ScanState = TScanState.OTHER_REPORT_MESSAGES_CAT
|
||||
else:
|
||||
with suppress(PyLintMetricNotFound): RES_all['Duplication']['NbDupLines'] = cls.TryExtractPYReportMetric(line,"nb duplicated lines")
|
||||
with suppress(PyLintMetricNotFound): RES_all['Duplication']['PersentDuplicatedLines'] = cls.TryExtractPYReportMetric(line,"percent duplicated lines")
|
||||
|
||||
elif ScanState == TScanState.OTHER_REPORT_MESSAGES_CAT:
|
||||
if line == "Messages":
|
||||
ScanState = TScanState.OTHER_REPORT_MESSAGES
|
||||
else:
|
||||
with suppress(PyLintMetricNotFound): RES_all['MessagesCat']['Convention'] = cls.TryExtractPYReportMetric(line,"convention")
|
||||
with suppress(PyLintMetricNotFound): RES_all['MessagesCat']['Refactor'] = cls.TryExtractPYReportMetric(line,"refactor")
|
||||
with suppress(PyLintMetricNotFound): RES_all['MessagesCat']['Warning'] = cls.TryExtractPYReportMetric(line,"warning")
|
||||
with suppress(PyLintMetricNotFound): RES_all['MessagesCat']['Error'] = cls.TryExtractPYReportMetric(line,"error")
|
||||
|
||||
elif ScanState == TScanState.OTHER_REPORT_MESSAGES:
|
||||
# approx match because the number of '-' depend on screen width..
|
||||
if line.startswith("--------"):
|
||||
ScanState = TScanState.OTHER_REPORT_END
|
||||
else:
|
||||
for PylintMessage in cls.PylintMessageList.keys():
|
||||
with suppress(PyLintMetricNotFound): RES_all['Messages'][PylintMessage] = cls.TryExtractPYReportMetric(line,PylintMessage)
|
||||
|
||||
elif ScanState == TScanState.OTHER_REPORT_END:
|
||||
if res:=re.search(r"(?<=Your code has been rated at )(\d+(?:\.\d+)?)/10",line):
|
||||
RES_all['GlobalScore']=float(res.group(1))
|
||||
print(RES_all['GlobalScore'])
|
||||
else:
|
||||
raise RuntimeError("Invalid ScanState")
|
||||
Outfile.write(JsonContent)
|
||||
|
||||
with open(cls.get_result_dir()/"metrics.json", 'w') 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...
|
||||
# => to export a working full csv we need to a 'flat' dict (no more nested dict)
|
||||
RES_all_trim = copy.deepcopy(RES_all)
|
||||
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:
|
||||
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:
|
||||
writer = csv.DictWriter(csv_file,fieldnames=RES_all_percent.keys())
|
||||
writer.writeheader()
|
||||
writer.writerow(RES_all_percent)
|
||||
|
||||
# splited csv exports for jenkins plots: Statistics + Duplication + NbAnalysedStatments + NbAnalysedLines
|
||||
RES_all_stats = copy.deepcopy(RES_all["Statistics"])
|
||||
RES_all_stats["NbDupLines"] = RES_all['Duplication']['NbDupLines']
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
writer = csv.DictWriter(csv_file,fieldnames=RES_GlobalScore.keys())
|
||||
writer.writeheader()
|
||||
writer.writerow(RES_GlobalScore)
|
||||
|
||||
# converting the report using pylint_json2html (/!\ internal API, but as their is no leading '_' ...)
|
||||
with open(cls.get_result_dir()/"report.html","w+", encoding='utf-8') as Outfile:
|
||||
raw_data = json.loads(JsonContent)
|
||||
report=pylint_json2html.Report( raw_data)
|
||||
Outfile.write(report.render())
|
||||
|
||||
print("Done")
|
||||
51
helpers/types_check.py
Normal file
51
helpers/types_check.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# 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
|
||||
|
||||
from mypy import api
|
||||
|
||||
from .helper_base import helper_withresults_base
|
||||
|
||||
|
||||
class types_check(helper_withresults_base):
|
||||
JUnitReportName = "junit.xml"
|
||||
|
||||
@classmethod
|
||||
def do_job(cls):
|
||||
print("checking code typing ...")
|
||||
result = api.run([ # project path
|
||||
"-m",
|
||||
"src." + str(cls.pyproject['project']['name']),
|
||||
# analysis configuration
|
||||
"--ignore-missing-imports",
|
||||
"--strict-equality",
|
||||
# reports generation
|
||||
"--cobertura-xml-report", str(cls.get_result_dir()),
|
||||
"--html-report", 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", str(cls.get_result_dir()),
|
||||
"--xml-report", str(cls.get_result_dir()),
|
||||
"--junit-xml", str(cls.get_result_dir()) + "/" + cls.JUnitReportName
|
||||
])
|
||||
|
||||
if result[0]:
|
||||
print('\nType checking report:\n')
|
||||
print(result[0]) # stdout
|
||||
|
||||
if result[1]:
|
||||
print('\nError report:\n')
|
||||
print(result[1]) # stderr
|
||||
|
||||
print('\nExit status:', result[2])
|
||||
print("Done")
|
||||
79
helpers/unit_test.py
Normal file
79
helpers/unit_test.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# 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
|
||||
|
||||
import unittest
|
||||
import xmlrunner
|
||||
from junitparser import JUnitXml
|
||||
from junit2htmlreport import parser as junit2html_parser
|
||||
|
||||
from .helper_base import helper_withresults_base
|
||||
|
||||
|
||||
class unit_test(helper_withresults_base):
|
||||
enable_coverage_check: bool = False
|
||||
enable_xml_export: bool = True
|
||||
enable_full_xml_export: bool = True
|
||||
FullReportName: str = "full_report"
|
||||
CoverageReportName: str = "test_coverage"
|
||||
|
||||
@classmethod
|
||||
def do_job(cls):
|
||||
if cls.enable_coverage_check==True:
|
||||
import coverage
|
||||
|
||||
# preparing unittest framework
|
||||
test_loader = unittest.TestLoader()
|
||||
|
||||
if cls.enable_coverage_check == 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.start()
|
||||
|
||||
package_tests = test_loader.discover(start_dir=str(cls.project_rootdir_path / "test"),top_level_dir=str(cls.project_rootdir_path / "test"))
|
||||
if cls.enable_xml_export:
|
||||
testRunner = xmlrunner.XMLTestRunner(output=str(str(cls.get_result_dir())))
|
||||
else:
|
||||
testRunner = unittest.TextTestRunner()
|
||||
|
||||
# running the test
|
||||
testRunner.run(package_tests)
|
||||
|
||||
print("Test Finished")
|
||||
if cls.enable_coverage_check == True:
|
||||
cov.stop()
|
||||
cov.save()
|
||||
cov.html_report(directory=str(CoverageReportPath))
|
||||
cov.xml_report(outfile=(CoverageReportPath/f"{cls.CoverageReportName}.xml"))
|
||||
|
||||
# computing results (Only if xml available)
|
||||
if cls.enable_full_xml_export == True:
|
||||
print("Full reports generation...")
|
||||
FullReportPath = Path(str(cls.get_result_dir())+"_full")
|
||||
cls._reset_dir(FullReportPath)
|
||||
|
||||
FullJUnitReport = JUnitXml()
|
||||
for fname in [fname for fname in os.listdir(cls.get_result_dir()) if fname.endswith('.xml')]:
|
||||
FullJUnitReport+=JUnitXml.fromfile(str(cls.get_result_dir() / fname))
|
||||
|
||||
current_datetime = datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
|
||||
full_report_base_name = f'{cls.pyproject["project"]["name"]}-{cls.FullReportName}-{current_datetime}'
|
||||
FullJUnitReport.write(str(FullReportPath / f'{full_report_base_name}.xml'))
|
||||
report = junit2html_parser.Junit(FullReportPath/ f'{full_report_base_name}.xml')
|
||||
html = report.html()
|
||||
with open(FullReportPath/ f'{full_report_base_name}.html', "wb") as outfile:
|
||||
outfile.write(html.encode('utf-8'))
|
||||
print("Done")
|
||||
62
mkdocs.yml
Normal file
62
mkdocs.yml
Normal file
@@ -0,0 +1,62 @@
|
||||
# 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
|
||||
site_name: pygitversionhelper
|
||||
site_url: https://chacha.ddns.net/mkdocs-web/chacha/pygitversionhelper/latest/
|
||||
site_description: pygitversionhelper
|
||||
site_author: chacha
|
||||
repo_url: https://chacha.ddns.net/gitea/chacha/pygitversionhelper
|
||||
use_directory_urls: false
|
||||
theme:
|
||||
name: material
|
||||
features:
|
||||
- navigation.instant
|
||||
- navigation.tracking
|
||||
- navigation.tabs
|
||||
- navigation.tabs.sticky
|
||||
- toc.integrate
|
||||
- navigation.top
|
||||
palette:
|
||||
- media: '(prefers-color-scheme: dark)'
|
||||
scheme: slate
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
name: Switch to system preference
|
||||
- media: (prefers-color-scheme)
|
||||
toggle:
|
||||
icon: material/brightness-auto
|
||||
name: Switch to light mode
|
||||
- media: '(prefers-color-scheme: light)'
|
||||
scheme: default
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: Switch to dark mode
|
||||
plugins:
|
||||
- search
|
||||
- localsearch
|
||||
- autorefs
|
||||
- mkdocstrings:
|
||||
default_handler: python
|
||||
handlers:
|
||||
python:
|
||||
selection:
|
||||
filters:
|
||||
- '!^_(?!_init__)'
|
||||
inherited_members: true
|
||||
rendering:
|
||||
show_root_heading: false
|
||||
show_root_toc_entry: false
|
||||
show_root_full_path: false
|
||||
show_if_no_docstring: true
|
||||
show_signature_annotations: true
|
||||
show_source: false
|
||||
heading_level: 2
|
||||
group_by_category: true
|
||||
show_category_heading: true
|
||||
70
pyproject.toml
Normal file
70
pyproject.toml
Normal 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/>.
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=63", "wheel", "setuptools-git-versioning<2"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools-git-versioning]
|
||||
enabled = true
|
||||
dev_template = "{tag}.post{ccount}"
|
||||
#tag_filter = "^\\d+\\.\\d+\\.\\d+$"
|
||||
|
||||
[project]
|
||||
name = "pygitversionhelper"
|
||||
description = "pygitversionhelper"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
keywords = ["chacha","chacha","template","pygitversionhelper"]
|
||||
license = { file = "LICENSE.md" }
|
||||
|
||||
authors = [
|
||||
{name="chacha",email="1000CHACHA0001@gmail.com"},
|
||||
]
|
||||
maintainers = [
|
||||
{name="chacha",email="1000CHACHA0001@gmail.com"},
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
]
|
||||
dependencies = [
|
||||
'importlib-metadata; python_version<"3.9"',
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
[tool.setuptools]
|
||||
platforms = ["any"]
|
||||
include-package-data = true
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"pygitversionhelper.data" = ["*.*"]
|
||||
|
||||
#[tool.setuptools_scm]
|
||||
#write_to = "src/pygitversionhelper/_version.py"
|
||||
#version_scheme = "python-simplified-semver"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://chacha.ddns.net/gitea/chacha/pygitversionhelper"
|
||||
Documentation = "https://chacha.ddns.net/gitea/chacha/pygitversionhelper/wiki"
|
||||
Tracker = "https://chacha.ddns.net/gitea/chacha/pygitversionhelper/issues"
|
||||
|
||||
[project.optional-dependencies]
|
||||
test = ["junitparser>=2.8","junit2html>=30.1","xmlrunner>=1.7","mypy>=0.99" ]
|
||||
coverage-check = ["coverage>=7.0"]
|
||||
quality-check = ["pylint>=2.15","pylint-json2html>=0.4","pandas>=1.5"]
|
||||
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"]
|
||||
|
||||
#[project.scripts]
|
||||
#my-script = "my_package.module:function"
|
||||
|
||||
22
src/pygitversionhelper/__init__.py
Normal file
22
src/pygitversionhelper/__init__.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# 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/>.
|
||||
|
||||
"""
|
||||
Main module __init__ file.
|
||||
"""
|
||||
|
||||
from importlib.metadata import version, PackageNotFoundError
|
||||
|
||||
try: # pragma: no cover
|
||||
__version__ = version("pygitversionhelper")
|
||||
except PackageNotFoundError: # pragma: no cover
|
||||
import warnings
|
||||
warnings.warn("can not read __version__, assuming local test context, setting it to ?.?.?")
|
||||
__version__ = "?.?.?"
|
||||
|
||||
from .test_module import test_function
|
||||
0
src/pygitversionhelper/data/.keep
Normal file
0
src/pygitversionhelper/data/.keep
Normal file
43
src/pygitversionhelper/test_module.py
Normal file
43
src/pygitversionhelper/test_module.py
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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/>.
|
||||
|
||||
"""Phasellus tellus lectus, volutpat eu dapibus ut, suscipit vel augue.
|
||||
|
||||
Tips:
|
||||
Aliquam non leo vel libero sagittis viverra. Quisque lobortis nunc sit amet augue euismod laoreet.
|
||||
Note:
|
||||
Maecenas volutpat porttitor pretium. Aliquam suscipit quis nisi non imperdiet.
|
||||
Note:
|
||||
Vivamus et efficitur lorem, eget imperdiet tortor. Integer vel interdum sem.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING: # Only imports the below statements during type checking
|
||||
pass
|
||||
|
||||
def test_function(testvar: int) -> int:
|
||||
""" A test function that return testvar+1 and print "Hello world !"
|
||||
|
||||
Proin eget sapien eget ipsum efficitur mollis nec ac nibh.
|
||||
|
||||
Note:
|
||||
Morbi id lectus maximus, condimentum nunc eget, porta felis. In tristique velit tortor.
|
||||
|
||||
Args:
|
||||
testvar: any integer
|
||||
|
||||
Returns:
|
||||
testvar+1
|
||||
"""
|
||||
print("Hello world !")
|
||||
return testvar+1
|
||||
7
test/__init__.py
Normal file
7
test/__init__.py
Normal 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/>.
|
||||
32
test/test_test_module.py
Normal file
32
test/test_test_module.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# 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/>.
|
||||
|
||||
import unittest
|
||||
from io import StringIO
|
||||
from contextlib import redirect_stdout,redirect_stderr
|
||||
|
||||
print(__name__)
|
||||
print(__package__)
|
||||
|
||||
from src import pygitversionhelper
|
||||
|
||||
class Testtest_module(unittest.TestCase):
|
||||
def test_version(self):
|
||||
self.assertNotEqual(pygitversionhelper.__version__,"?.?.?")
|
||||
|
||||
def test_test_module(self):
|
||||
|
||||
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
|
||||
self.assertEqual(pygitversionhelper.test_function(41),42)
|
||||
|
||||
self.assertEqual(len(capted_stderr.getvalue()),0)
|
||||
self.assertEqual(capted_stdout.getvalue().strip(),"Hello world !")
|
||||
self.assertEqual(len(capted_stderr.getvalue()),0)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user