// 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'
		}
	}
}

