channel = "#wikiazma"
django_root = "wikiazma"
project_name = "wikiazma_server"
project_display_name = "Wikiazma Server"
action = "Build"
failed_message_sent = false
fast_staging_deploy = false

pipeline {
    agent any
    options {
      disableConcurrentBuilds()
      buildDiscarder(logRotator(numToKeepStr: '100', daysToKeepStr: '20'))
    }

    stages {

        stage('SLACK MESSAGE') {
            steps {
                script {
                    commitMsg = sh(script: "git log -1 --pretty='%s'", returnStdout: true).trim()
                    commitUsr = sh(script: "git log -1 --pretty='%an'", returnStdout: true).trim()

                    if(commitMsg.indexOf("fast_staging_deploy") != -1){
                        fast_staging_deploy = true
                    }

                    message = "${project_display_name} ${action} ${currentBuild.number} Started \nNew Push detected. \nCommit Message: ${commitMsg} \nBy: ${commitUsr} \n<${env.BUILD_URL}|Open>"
                    slackSend(message: message, color: "#FFFFFF", channel: channel)
                }
            }
        }

        stage('PRE DEPLOY TESTING') {
            when{
                expression{
                    fast_staging_deploy == false
                }
            }
            steps {
				script{
					def migrations_check = sh(returnStdout: true, script: "find ./*/migrations/ -type f -exec grep -nH 'PrivateMediaStorage' {} \\;")
                    print(migrations_check)
                    if (migrations_check != "") {
                        currentBuild.result = 'ABORTED'

                        message = "${project_display_name} ${action} ${currentBuild.number} \nmigrations check Failed \nThese files contain the word 'PrivateMediaStorage' which is forbidden in migrations. Use private_storage instead.\n${migrations_check}"
                        slackSend(message: message, color: "#FF0000", channel: channel)
                        slackUploadFile(filePath: 'test.log', initialComment: "test logs", channel: channel)
                    }

                    migrations_check = sh(returnStdout: true, script: "find ./*/migrations/ -type f -exec grep -nH 'PublicMediaStorage' {} \\;")
                    print(migrations_check)
                    if (migrations_check != "") {
                        currentBuild.result = 'ABORTED'

                        message = "${project_display_name} ${action} ${currentBuild.number} \nmigrations check Failed \nThese files contain the word 'PublicMediaStorage' which is forbidden in migrations. Use public_storage instead.\n${migrations_check}"
                        slackSend(message: message, color: "#FF0000", channel: channel)
                        slackUploadFile(filePath: 'test.log', initialComment: "test logs", channel: channel)
                    }
					
					sh "cp /home/ubuntu/${project_name}-Data/.env.testing ./.env.testing"
				}
            }
        }

        stage('BUILD AND DEPLOY TESTING') {
            when{
                expression{
                    fast_staging_deploy == false
                }
            }
            agent {
                dockerfile {
                    reuseNode true
                    filename 'Dockerfile'
                    // don't use .venv folder because it is not friendly with coverage reports
                    // cache in project folder
                    // args '-e WORKON_HOME=./venv/ -e PIPENV_CACHE_DIR=${WORKSPACE}/venv/.cache -e USER=Jenkins'
                    // cache in jenkins home directory, is this safe?
                    // args '-e WORKON_HOME=./venv/ -e PIPENV_CACHE_DIR=${JENKINS_HOME}/.cache -v /var/lib/jenkins/.cache:/var/lib/jenkins/.cache:rw,z -e USER=Jenkins'
                    // cache in another folder at docker container, venv folder is getting deployed to amazon by zappa
                    args '-e WORKON_HOME=./venv/ -e PIPENV_CACHE_DIR=${JENKINS_HOME}/venv/.cache -e USER=Jenkins'
                    // args '-e PIPENV_VENV_IN_PROJECT=1 -e USER=Jenkins'
                    // args '-e WORKON_HOME=./venv/ -e USER=Jenkins'
                }
            }
            steps {
                script {
                    //pipenv run python manage.py makemigrations
                    // I have removed makemigrations, refer to fitcrete_server_deploy_staging comment
                    sh """
                        #!/bin/sh
                        pipenv install --dev
                        echo yes | pipenv run python manage.py collectstatic --settings=${django_root}.settings_testing
                        echo yes | pipenv run python manage.py reset_db --settings=${django_root}.settings_testing
                        pipenv run python manage.py migrate --settings=${django_root}.settings_testing
                    """

                    def status = sh(returnStatus: true, script: """
                        pipenv run python manage.py makemigrations --check --dry-run --settings=${django_root}.settings_testing > makemigrations_check.stdout.log 2> makemigrations_check.stderr.log
                    """)

                    print(status)
                    if (status != 0) {
                        def stdoutLog = readFile('makemigrations_check.stdout.log').trim()
                        def stderrLog = readFile('makemigrations_check.stderr.log').trim()
                        print(stdoutLog)
                        print(stderrLog)
                        sh """
                            #!/bin/sh
                            cat makemigrations_check.stdout.log makemigrations_check.stderr.log > makemigrations_check.log
                        """

                        message = "${project_display_name} ${action} ${currentBuild.number} \nThere are some changes to the models that need to do makemigrations"
                        slackSend(message: message, color: "#FFA500", channel: channel)
                        slackUploadFile(filePath: 'makemigrations_check.log', initialComment: "makemigrations check log", channel: channel)
                    }

                    sh(returnStatus: true, script: """
                        #!/bin/sh
                        rm -f makemigrations_check.stdout.log
                        rm -f makemigrations_check.stderr.log
                        rm -f makemigrations_check.log
                    """)
                }
            }
        }

        stage('Coverage') {
            when{
                expression{
                    fast_staging_deploy == false
                }
            }
            agent {
                dockerfile {
                    reuseNode true
                    filename 'Dockerfile'
                    args '-e WORKON_HOME=./venv/ -e PIPENV_CACHE_DIR=${JENKINS_HOME}/venv/.cache -e USER=Jenkins'
                }
            }
            steps {
                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                    script {
                        def nproc = sh(script: "nproc --all", returnStdout: true).trim()
                        def status = sh(returnStatus: true, script: """
                            #!/bin/sh
                            pipenv run coverage run --parallel-mode --concurrency=multiprocessing manage.py test --parallel=${nproc} --noinput --settings=${django_root}.settings_testing > coverage_test.stdout.log 2> coverage_test.stderr.log
                        """)

                        print(status)
                        if (status != 0) {
                            currentBuild.result = 'ABORTED'
                            def stdoutLog = readFile('coverage_test.stdout.log').trim()
                            def stderrLog = readFile('coverage_test.stderr.log').trim()
                            print(stdoutLog)
                            print(stderrLog)
                            sh """
                                #!/bin/sh
                                cat coverage_test.stdout.log coverage_test.stderr.log > coverage_test.log
                            """

                            message = "${project_display_name} ${action} ${currentBuild.number} \nTest Failed"
                            slackSend(message: message, color: "#FF0000", channel: channel)
                            slackUploadFile(filePath: 'coverage_test.log', initialComment: "test logs", channel: channel)
                        } else {
                            message = "${project_display_name} ${action} ${currentBuild.number} \nAll Tests passed"
                            slackSend(message: message, color: "#00FF00", channel: channel)
                        }

                        sh(returnStatus: true, script: """
                            #!/bin/sh
                            pipenv run coverage combine
                            pipenv run coverage xml -i
                            pipenv run coverage html
                            tar -cf coverage.tar htmlcov
                            pipenv run coverage report > coverage.stdout.log 2> coverage.stderr.log
                        """)
                        def total_coverage = sh(script: "pipenv run coverage report | grep TOTAL", returnStdout: true)
                        print(total_coverage)

                        sh """
                            #!/bin/sh
                            cat coverage.stdout.log coverage.stderr.log > coverage.log
                        """

                        message = total_coverage
                        slackSend(message: message, color: "#FFFFFF", channel: "#jenkins")
                        slackUploadFile(filePath: 'coverage.log', initialComment: "coverage logs", channel: "#jenkins")

                        // move coverage files after sonarQube
                        /*sh(returnStatus: true, script: """
                            #!/bin/sh
                            mkdir coverage || true
                            mv -f htmlcov/ coverage/ || true
                            mv -f coverage.tar coverage/ || true
                            mv -f ${project_name}_coverage.tar coverage/ || true
                            mv -f coverage_test.stdout.log coverage/ || true
                            mv -f coverage_test.stderr.log coverage/ || true
                            mv -f coverage_test.log coverage/ || true
                            mv -f coverage.stdout.log coverage/ || true
                            mv -f coverage.stderr.log coverage/ || true
                            mv -f coverage.log coverage/ || true
                            mv -f .coverage coverage/ || true
                            mv -f coverage.xml coverage/ || true
                        """)*/

                        if (failed_message_sent) {
                            sh "exit 1"
                        }
                    }
                }
            }
        }

        // sonar scanner does not need to be run in docker
        stage('SonarQube analysis') {
            when{
                expression{
                    fast_staging_deploy == false
                }
            }
            steps {
                script{
            		def scannerHome = tool 'DefaultScanner';
            		withSonarQubeEnv('DefaultSonarQube') {
            			sh "${scannerHome}/bin/sonar-scanner"
            		}

            		timeout(time: 1, unit: "HOURS"){
            		    try{
                            def qg = waitForQualityGate()
                            if(qg.status != "OK"){
                                message = "${project_display_name} ${action} ${currentBuild.number} \nQuality Gate status ${qg.status}"
                                slackSend(message: message, color: "#FF0000", channel: channel)

                                // failed_message_sent = true
                                // error "Pipeline aborted due to quality gate failure: ${qg.status}"
                            }else{
                                message = "${project_display_name} ${action} ${currentBuild.number} \nQuality Gate passed."
                                slackSend(message: message, color: "#FFFFFF", channel: "#jenkins")
                            }
            		    }catch (Exception e) {
                            message = "${project_display_name} ${action} ${currentBuild.number} \nQuality Gate Failed(check sonarQube for scan errors)."
                            slackSend(message: message, color: "#FF0000", channel: "#jenkins")
            		    }

                    }

                    // to copy the coverage file to your local
                    // master branch
                    // scp -i "C:\Users\HAROON\.ssh\t3a-large.pem" ubuntu@jenkins.ariodevelopers.com:/var/lib/jenkins/workspace/_children_wikiazma_server_master/coverage/coverage.tar ./
                    // blueprint branch
                    // scp -i "C:\Users\HAROON\.ssh\t3a-large.pem" ubuntu@jenkins.ariodevelopers.com:/var/lib/jenkins/workspace/ildren_wikiazma_server_blueprint/coverage/coverage.tar ./
                    sh(returnStatus: true, script: """
                        #!/bin/sh
                        mkdir coverage || true
                        mv -f htmlcov/ coverage/ || true
                        mv -f coverage.tar coverage/ || true
                        mv -f ${project_name}_coverage.tar coverage/ || true
                        mv -f coverage_test.stdout.log coverage/ || true
                        mv -f coverage_test.stderr.log coverage/ || true
                        mv -f coverage_test.log coverage/ || true
                        mv -f coverage.stdout.log coverage/ || true
                        mv -f coverage.stderr.log coverage/ || true
                        mv -f coverage.log coverage/ || true
                        mv -f .coverage coverage/ || true
                        mv -f coverage.xml coverage/ || true
                    """)
            	}
            }
        }

        stage("PRE DEPLOY STAGING"){
            steps{
                script{
                    sh "cp /home/ubuntu/${project_name}-Data/.env.staging ./.env.staging"
                    if(BRANCH_NAME == 'blueprint'){
                        String new_env = readFile('./.env.staging').replaceAll('wikiazma_pg_db_staging','wikiazma_pg_db_staging_branch_blueprint')
                        writeFile file:'./.env.staging', text: new_env
                    }
                }
            }
        }

        stage('BUILD AND DEPLOY STAGING') {
            agent {
                dockerfile {
                    reuseNode true
                    filename 'Dockerfile'
                    args '-e WORKON_HOME=./venv/ -e PIPENV_CACHE_DIR=${JENKINS_HOME}/venv/.cache -e USER=Jenkins'
                }
            }
            when{
                expression{
                    BRANCH_NAME == 'master' || BRANCH_NAME == 'blueprint'
                }
            }
            steps {
                script {
                    //pipenv run python manage.py makemigrations --settings=${django_root}.settings_staging
                    // I have removed makemigrations, refer to fitcrete_server_deploy_staging comment
                    sh """
                        #!/bin/sh
                        pipenv run python zappa_settings_generator_zappa_utils.py .env.staging ci
                        echo yes | pipenv run python manage.py collectstatic --settings=${django_root}.settings_staging
                        pipenv run python manage.py migrate --settings=${django_root}.settings_staging
                    """
                    zappa_deploy_name = 'staging'
                    if(BRANCH_NAME == 'blueprint'){
                        zappa_deploy_name = 'staging_blueprint_branch'
                    }

                    withAWS(credentials:'aws_credentianl', region: 'eu-central-1') {
                        try {
                            sh """
                                #!/bin/sh
                                pipenv run zappa deploy ${zappa_deploy_name}
                            """
                        } catch (e) {

                        }
                        try {
                            sh """
                                #!/bin/sh
                                pipenv run zappa update ${zappa_deploy_name}
                            """

                            message = "${project_display_name} ${action} ${currentBuild.number} \nDeploy Successful"
                            slackSend(message: message, color: "#00FF00", channel: channel)
                        } catch (e) {
                            message = "${project_display_name} ${action} ${currentBuild.number} \nDeploy Failed"
                            slackSend(message: message, color: "#FF0000", channel: channel)
                            failed_message_sent = true

                            sh """
                                #!/bin/sh
                                pipenv run zappa tail ${zappa_deploy_name} --since 2h --disable-keep-open
                            """
                        }

                        if (failed_message_sent) {
                            sh "exit 1"
                        }
                    }
                }
            }
        }

    }

    post {
        failure {
            script {
                if (!failed_message_sent) {
                    message = "${project_display_name} \n${action} ${currentBuild.number} failed"
                    slackSend(message: message, color: "#FF0000", channel: channel)
                }
            }
        }
    }
}
