def baseBuildImage = "icr.io/streamsets_private/sdk-test-env"
def baseIcrImage = "icr.io/streamsets_private/sdk-python"
def buildImageTag = "latest"
def buildImage = "${baseBuildImage}:${buildImageTag}"
def failedStages = []
def integrationTestResults = ''
def integrationTestSummary = [:]
def unitTestResults_310 = ''
def unitTestResults_311 = ''
def unitTestResults_312 = ''
def unitTestSummary = [:]
def unstableStages = []
def sonarqubeURL = "https://sonar.streamsets.net"
def testStagesRun = [:]
def testStagesStatus = [:]

@Library('jenkins-common-library') _

pipeline {
    options {
        timestamps()
        ansiColor('xterm')
    }
    agent {
        label 'podman-4g-4c'
    }
    environment {
        DOCKER_ARGS = "--userns=keep-id --privileged " +
                      "--env CONTAINER_HOST=unix:///run/podman/podman.sock " +
                      "--volume $XDG_RUNTIME_DIR/podman/podman.sock:/run/podman/podman.sock " +
                      "--network host"
        UV_LINK_MODE = 'copy'
        NO_PROXY = "${env.NO_PROXY},${sh(script: 'hostname', returnStdout: true).trim()}"
    }

    triggers {
        cron(env.BRANCH_NAME == 'master' ? '0 5 * * 1-5' : '')
    }
    parameters {
        booleanParam(name: 'build_testframework_image', defaultValue: false, description: 'Build and push SDK image to trigger STF build')
        booleanParam(name: 'run_package_installation_test', defaultValue: false, description: 'Test package installation')
        booleanParam(name: 'run_documentation_test', defaultValue: false, description: 'Run documentation build test')
        booleanParam(name: 'run_integration_test', defaultValue: false, description: 'Run integration tests')
        string(name: 'integration_test_cases', defaultValue: '', description: 'Specific test cases to run (e.g., "test_file.py::test_name" or "test_file.py"). Leave empty to run all tests.')
        booleanParam(name: 'run_sonarqube_scan', defaultValue: false, description: 'Run SonarQube scan')
        choice(name: 'environment', choices: ['dev', 'preprod'], description: 'Which environment to run against')
    }
    stages {
        stage('Setup') {
            steps {
                script {
                    def triggerInfo = getTriggerCauseInfo(currentBuild)

                    // Set initial build description
                    if (triggerInfo) {
                        currentBuild.description = triggerInfo
                    }
                }
            }
        }
        stage('Test & Build') {
            parallel {
                stage('Lint & Format') {
                    agent {
                        docker {
                            reuseNode true
                            image "${buildImage}"
                            args DOCKER_ARGS
                        }
                    }
                    steps {
                        catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
                            withEnv(["HOME=${env.WORKSPACE}"]) {
                                sh 'git config --global --add safe.directory ${WORKSPACE}'
                                sh 'pip install pre-commit'
                                sh '.local/bin/pre-commit run --config python/evergreen/.pre-commit-config.yaml --all-files --show-diff-on-failure --verbose'
                            }
                        }
                    }
                    post {
                        unstable {
                            script {
                                unstableStages.add('Lint & Format')
                            }
                        }
                    }
                }
                stage('Unit Tests') {
                    agent {
                        docker {
                            reuseNode true
                            image "${buildImage}"
                            args DOCKER_ARGS
                        }
                    }
                    steps {
                        catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
                            withEnv(["HOME=${env.WORKSPACE}"]) {
                                sh 'git config --global --add safe.directory ${WORKSPACE}'
                                sh 'tox -c python/tox.ini -e unit-310,unit-311,unit-312'
                            }
                        }
                    }
                    post {
                        always {
                            script {
                                testStagesRun['Unit Tests'] = true
                                unitTestResults_310 = junit testResults: 'python/results_unit_310.xml'
                                unitTestResults_311 = junit testResults: 'python/results_unit_311.xml'
                                unitTestResults_312 = junit testResults: 'python/results_unit_312.xml'
                            }
                        }
                        success {
                            script {
                                testStagesStatus['Unit Tests'] = 'PASSED'
                            }
                            recordCoverage(
                                tools: [[parser: 'COBERTURA', pattern: '**/coverage_unit_310.xml']],
                                id: 'unit_310',
                                name: 'Python3.10 Unit Tests'
                            )
                            recordCoverage(
                                tools: [[parser: 'COBERTURA', pattern: '**/coverage_unit_311.xml']],
                                id: 'unit_311',
                                name: 'Python3.11 Unit Tests'
                            )
                            recordCoverage(
                                tools: [[parser: 'COBERTURA', pattern: '**/coverage_unit_312.xml']],
                                id: 'unit_312',
                                name: 'Python3.12 Unit Tests'
                            )
                        }
                        failure {
                            script {
                                testStagesStatus['Unit Tests'] = 'FAILED'
                                if (unitTestResults_310.failCount || unitTestResults_311.failCount || unitTestResults_312.failCount) {
                                    failedStages.add('Unit Tests')
                                    unitTestSummary.put('Python 3.10', ['total': unitTestResults_310.totalCount,
                                                                    'passing': unitTestResults_310.passCount,
                                                                    'failing': unitTestResults_310.failCount,
                                                                    'skipped': unitTestResults_310.skipCount])
                                    unitTestSummary.put('Python 3.11', ['total': unitTestResults_311.totalCount,
                                                                        'passing': unitTestResults_311.passCount,
                                                                        'failing': unitTestResults_311.failCount,
                                                                        'skipped': unitTestResults_311.skipCount])
                                    unitTestSummary.put('Python 3.12', ['total': unitTestResults_312.totalCount,
                                                                        'passing': unitTestResults_312.passCount,
                                                                        'failing': unitTestResults_312.failCount,
                                                                        'skipped': unitTestResults_312.skipCount])
                                }
                            }
                        }
                    }
                }
                stage('Integration Tests') {
                    when {
                        anyOf {
                            allOf {
                                expression {
                                    params.run_integration_test
                                }
                                triggeredBy cause: "UserIdCause"
                            }
                            branch 'master'
                        }
                        beforeAgent true
                    }
                    steps {
                        script {
                            if (params.environment == 'dev') {
                                env.ASTER_URL = "https://dev.login.streamsets.com"
                                env.TRANSFORMER_VERSION = "4.2.0"
                                env.CRED_ID = "ebf3adfe-5bed-4de2-8137-677723792dd1"
                                env.SYS_ADMIN = "86ce4d9f-b17d-408b-a932-2835b3213c29"
                            } else {
                                env.ASTER_URL = "https://cascade.login.streamsets.com"
                                env.TRANSFORMER_VERSION = "5.2.0"
                                env.CRED_ID = "08c98114-44f0-43cf-8bd2-09a3798b90ba"
                                env.SYS_ADMIN = "4489142f-e036-4671-958b-9b6798f34350"
                            }
                            env.FIREBASE_ID = "ASTER_DEV_FIREBASE_API_KEY"
                            env.ORG_ADMIN = "jenkins_org_admin_test_account"
                            catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
                                withEnv(["HOME=${env.WORKSPACE}", "ASTER_URL=${env.ASTER_URL}",
                                        "SDC_VERSION=5.2.0", "TRANSFORMER_VERSION=${env.TRANSFORMER_VERSION}", "SUBNET_IDS=[subnet-319c5468]",
                                        "VPC_ID=vpc-12279b77", "SECURITY_GROUP_ID=sg-03434536f7156364d", "REGION=us-west-2",
                                        "INSTANCE_PROFILE='arn:aws:iam::316386816690:instance-profile/csp-test-role'",
                                        "REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt",
                                        "SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt"]) {
                                    withCredentials([
                                        usernamePassword(
                                            credentialsId: env.ORG_ADMIN,
                                            passwordVariable: 'ORG_ADMIN_PASSWORD',
                                            usernameVariable: 'ORG_ADMIN_EMAIL'
                                        ),
                                        usernamePassword(
                                            credentialsId: env.SYS_ADMIN,
                                            passwordVariable: 'SYS_ADMIN_PASSWORD',
                                            usernameVariable: 'SYS_ADMIN_EMAIL'
                                        ),
                                        string(
                                            credentialsId: env.FIREBASE_ID,
                                            variable: 'FIREBASE_API_KEY'
                                        ),
                                        usernamePassword(
                                            credentialsId: '50cddd55-b7a4-4fc1-a0e3-064c17af537c',
                                            usernameVariable: 'AWS_ACCESS_KEY_ID',
                                            passwordVariable: 'AWS_SECRET_ACCESS_KEY'
                                        ),
                                        usernamePassword(
                                            credentialsId: '1f48db52-3ea4-4c95-9b47-194c5a3c9798',
                                            passwordVariable: 'SNOWFLAKE_PASSWORD',
                                            usernameVariable: 'SNOWFLAKE_USERNAME'
                                        )
                                    ]) {
                                        script {
                                            def testCommand = 'tox -c python/tox.ini -e integration-312'
                                            if (params.integration_test_cases) {
                                                testCommand += " -- ${params.integration_test_cases}"
                                            }
                                            sh 'git config --global --add safe.directory ${WORKSPACE}'
                                            sh 'pip install tox'
                                            sh testCommand
                                        }
                                    }
                                }
                            }
                        }
                    }
                    post {
                        always {
                            script {
                                testStagesRun['Integration Tests'] = true
                                integrationTestResults = junit allowEmptyResults: true, testResults: 'python/results_integration_312.xml'
                                integrationTestSummary['total'] = integrationTestResults.totalCount
                                integrationTestSummary['passing'] = integrationTestResults.passCount
                                integrationTestSummary['failing'] = integrationTestResults.failCount
                                integrationTestSummary['skipped'] = integrationTestResults.skipCount
                            }
                        }
                        success {
                            script {
                                testStagesStatus['Integration Tests'] = 'PASSED'
                            }
                            recordCoverage(
                                tools: [[parser: 'COBERTURA', pattern: '**/coverage_integration_312.xml']],
                                id: 'integration_312',
                                name: 'Integration Tests'
                            )
                        }
                        failure {
                            script {
                                testStagesStatus['Integration Tests'] = 'FAILED'
                                failedStages.add('Integration Tests')
                            }
                        }
                    }
                }
                stage('Test package installation'){
                    when {
                        anyOf {
                            allOf {
                                expression {
                                    params.run_package_installation_test
                                }
                                triggeredBy cause: "UserIdCause"
                            }
                            branch 'master'
                        }
                        beforeAgent true
                    }
                    agent {
                        docker {
                            reuseNode true
                            image "${buildImage}"
                            args DOCKER_ARGS
                        }
                    }
                    steps {
                        script {
                            withEnv(["HOME=${env.WORKSPACE}"]) {
                                sh 'git config --global --add safe.directory ${WORKSPACE}'
                                sh 'tox -c python/tox.ini -e install-310,install-311,install-312'
                            }
                        }
                    }
                    post {
                        unstable {
                            script {
                                unstableStages.add('Test package installation')
                            }
                        }
                    }
                }
                stage('Build Container(s)') {
                    stages {
                        stage('SDK Container Build') {
                            steps {
                                script {
                                    def icrImage = "${baseIcrImage}:${env.GIT_BRANCH}"
                                    // We pass the container file from the folder but set context as the current directory
                                    // This lets it copy the current directory properly as it has context
                                    def image = docker.build(icrImage, '-f python/Dockerfile python')

                                    docker.withRegistry('https://icr.io') {
                                        image.push()
                                    }

                                    currentBuild.description = (currentBuild.description ? currentBuild.description + "<br/>" : '') +
                                                               "📦 SDK Image: ${icrImage}"
                                }
                            }
                        }
                        stage('STF Containers Build') {
                            when {
                                allOf {
                                    expression {
                                        params.build_testframework_image
                                    }
                                    triggeredBy cause: "UserIdCause"
                                }
                                beforeAgent true
                            }
                            steps {
                                script {
                                    // Determine which STF job to trigger based on target branch for PRs or current branch
                                    // env.CHANGE_TARGET is only not null when its a Changeset build
                                    // if not a PR build, env.BRANCH_NAME (current Jenkins branch is the target branch name)
                                    def targetBranch = env.CHANGE_TARGET ?: env.BRANCH_NAME
                                    echo "Target branch: ${targetBranch}"

                                    def stfDockerJob, stfPodmanJob
                                    if (targetBranch.startsWith('3.')) {
                                        stfDockerJob = 'testframework/3.x'
                                        stfPodmanJob = 'testframework/3.x-podman'
                                    } else {
                                        stfDockerJob = 'testframework/master'
                                        stfPodmanJob = 'testframework/master-podman'
                                    }
                                    def parameters = [
                                        // env.BRANCH_NAME for GitHub PRs is something like 'PR-123'
                                        string(name: 'SDK_BRANCH', value: env.BRANCH_NAME)
                                    ]


                                    echo "Triggering STF Docker job: ${stfDockerJob} for target branch: ${targetBranch}"
                                    def stfDockerBuild = build([
                                        job: stfDockerJob,
                                        parameters: parameters,
                                        wait: false,
                                        waitForStart: true
                                    ])

                                    echo "Triggering STF Podman job: ${stfPodmanJob} for target branch: ${targetBranch}"
                                    def stfPodmanBuild = build([
                                        job: stfPodmanJob,
                                        parameters: parameters,
                                        wait: false,
                                        waitForStart: true
                                    ])

                                    currentBuild.description = (currentBuild.description ? currentBuild.description + "<br/>" : '') +
                                                               "<a href='${stfDockerBuild.absoluteUrl}'>🏗 STF Docker Build</a>" +
                                                               "<br/><a href='${stfPodmanBuild.absoluteUrl}'>🏗 STF Podman Build</a>"

                                    waitForBuild(runId: stfDockerBuild.externalizableId, propagate: true)
                                    waitForBuild(runId: stfPodmanBuild.externalizableId, propagate: true)

                                    // Get Docker Images and update description
                                    def stfbuildDescriptionRegex = /STF image:\s*([^\s<]+)/
                                    [
                                        'Docker': (stfDockerBuild.description =~ stfbuildDescriptionRegex),
                                        'Podman': (stfPodmanBuild.description =~ stfbuildDescriptionRegex)
                                    ].each { name, matcher ->
                                        if (matcher.find()) {
                                            def image = matcher.group(1)
                                            currentBuild.description = currentBuild.description +
                                                                       "<br/>📦 STF ${name} Image: ${image}"
                                        } else {
                                            currentBuild.description = currentBuild.description +
                                                                       "<br/>📦 STF ${name} Image: ---"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                stage('Documentation Build') {
                    when {
                        anyOf {
                            allOf {
                                expression {
                                    params.run_documentation_test
                                }
                                triggeredBy cause: "UserIdCause"
                            }
                            branch 'master'
                        }
                        beforeAgent true
                    }
                    agent {
                        docker {
                            reuseNode true
                            image "${buildImage}"
                            args DOCKER_ARGS
                        }
                    }
                    steps {
                        catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
                            withEnv(["HOME=${env.WORKSPACE}"]) {
                                sh 'git config --global --add safe.directory ${WORKSPACE}'
                                sh 'tox -c python/tox.ini -e docs'
                            }
                        }
                    }
                    post {
                        unstable {
                            script {
                                unstableStages.add('Documentation Build')
                            }
                        }
                    }
                }
            }
        }
        stage('SonarQube Scan') {
            when {
                anyOf {
                    allOf {
                        expression {
                            params.run_sonarqube_scan
                        }
                        triggeredBy cause: "UserIdCause"
                    }
                    branch 'master'
                }
                beforeAgent true
            }
            steps {
                withCredentials([string(credentialsId: 'sdk-evergreen-sonarqube-token', variable: 'SONAR_TOKEN')]) {
                    withEnv (["SONARQUBE_URL=https://sonar.streamsets.net", "REPO_PATH=${WORKSPACE}/python"]){
                        script {
                            // Our internal SonarQube instance is version 7.9.5 which is EOL, so we have to grab an older version of the scanner image (4.6)
                            // Java doesn't support SOCKS5 proxy via HTTP_PROXY, so we need to use Java system properties
                            sh '''
                                # Extract proxy host and port from HTTPS_PROXY
                                PROXY_HOST=$(echo ${HTTPS_PROXY} | sed 's|socks5h://||' | cut -d: -f1)
                                PROXY_PORT=$(echo ${HTTPS_PROXY} | sed 's|socks5h://||' | cut -d: -f2)

                                echo "Using SOCKS proxy: ${PROXY_HOST}:${PROXY_PORT}"

                                podman run \
                                    --rm \
                                    --network host \
                                    -e SONAR_HOST_URL="${SONARQUBE_URL}" \
                                    -e SONAR_TOKEN=${SONAR_TOKEN} \
                                    -e SONAR_SCANNER_OPTS="-DsocksProxyHost=${PROXY_HOST} -DsocksProxyPort=${PROXY_PORT} -Djava.net.useSystemProxies=false" \
                                    -v "${REPO_PATH}:/usr/src" \
                                    sonarsource/sonar-scanner-cli:4.6 -X
                            '''
                        }
                    }
                }
            }
            post {
                unstable {
                    script {
                        unstableStages.add('SonarQube scan')
                    }
                }
            }
        }
    }
    post {
        success {
            script {
                if (env.BRANCH_NAME == 'master') {
                    slackSend(
                        channel: "#ep-controlplane",
                        color: '#007D00',
                        message:"Evergreen Master Status: PASSING \n Build: ${env.BUILD_NUMBER} (<${env.BUILD_URL}|Open>)"
                    )
                }
            }
        }
        unstable {
            script {
                if (env.BRANCH_NAME == 'master') {
                    def unstableMessage = "Evergreen Master Status: UNSTABLE \n Unstable Stages:"
                    unstableStages.each { stage ->
                        unstableMessage += "\n - ${stage}"
                    }
                    unstableMessage += "\n Build: ${env.BUILD_NUMBER} (<${env.BUILD_URL}|Open>)"
                    slackSend(
                        channel: "#ep-controlplane",
                        color: '#FFEF66',
                        message: unstableMessage
                    )
                }
            }
        }
        failure {
            script {
                if (env.BRANCH_NAME == 'master') {
                    def failureMessage = "Evergreen Master Status: FAILED \n\n"

                    if ('Unit Tests' in failedStages) {
                        unitTestSummary.each{ key, value ->
                            failureMessage += """${key} Unit Tests:\n\n
                                                \tTotal: ${value['total']}\n
                                                \tPassing: ${value['passing']}\n
                                                \tFailing: ${value['failing']}\n
                                                \tSkipped: ${value['skipped']}\n\n"""}
                    }
                    if ('Integration Tests' in failedStages) {
                        failureMessage += "Integration Test Results:\n"
                        failureMessage += "\t Total: ${integrationTestSummary['total']}\n"
                        failureMessage += "\t Passing: ${integrationTestSummary['passing']}\n"
                        failureMessage += "\t Failing: ${integrationTestSummary['failing']}\n"
                        failureMessage += "\t Skipped: ${integrationTestSummary['skipped']}\n\n"
                    }

                    failureMessage += "Build #${env.BUILD_NUMBER} (<${env.BUILD_URL}|Open>)"
                    slackSend(
                        channel: "#ep-controlplane",
                        color: '#FF0000',
                        message: failureMessage
                    )
                }
            }
        }
        cleanup {
            cleanWs()
        }
        always {
            script {
                // Post GitHub PR comment if this is a pull request
                if (env.CHANGE_ID) {
                    postGitHubPRComment(
                        testStagesRun,
                        testStagesStatus,
                        unitTestSummary,
                        integrationTestSummary,
                        failedStages,
                        unstableStages
                    )
                }
            }
        }
    }
}

def String buildTestResultsMarkdown(stagesRun, stagesStatus, unitSummary, integrationSummary, failed, unstable) {
    def markdown = "## 🧪 Test Results Summary\n\n"

    // Overall status
    def overallStatus = currentBuild.result ?: 'SUCCESS'
    def statusEmoji = overallStatus == 'SUCCESS' ? '✅' : (overallStatus == 'UNSTABLE' ? '⚠️' : '❌')
    markdown += "**Overall Status:** ${statusEmoji} ${overallStatus}\n\n"

    // Build info
    markdown += "**Build:** [#${env.BUILD_NUMBER}](${env.BUILD_URL})\n"
    markdown += "**Branch:** `${env.BRANCH_NAME}`\n\n"

    // Test stages summary table
    if (stagesRun.size() > 0) {
        markdown += "### Test Stages\n\n"
        markdown += "| Stage | Status | Details |\n"
        markdown += "|-------|--------|----------|\n"

        stagesRun.each { stageName, wasRun ->
            if (wasRun) {
                def status = stagesStatus[stageName] ?: 'UNKNOWN'
                def statusIcon = status == 'PASSED' ? '✅' : '❌'
                def detailsLink = "[View Logs](${env.BUILD_URL}console)"
                markdown += "| ${stageName} | ${statusIcon} ${status} | ${detailsLink} |\n"
            }
        }
        markdown += "\n"
    }

    // Unit Tests Details
    if (stagesRun['Unit Tests']) {
        markdown += "<details>\n"
        markdown += "<summary><b>📊 Unit Tests Details</b></summary>\n\n"

        if (stagesStatus['Unit Tests'] == 'FAILED') {
            // Show failed versions
            markdown += "#### Failed Python Versions\n\n"
            markdown += "| Python Version | Total | Passing | Failing | Skipped |\n"
            markdown += "|----------------|-------|---------|---------|----------|\n"
            unitSummary.each { version, results ->
                markdown += "| ${version} | ${results.total} | ${results.passing} | **${results.failing}** | ${results.skipped} |\n"
            }
        } else if (stagesStatus['Unit Tests'] == 'PASSED') {
            markdown += "All unit tests passed across all Python versions! 🎉\n"
        }

        markdown += "\n[View Full Unit Test Results](${env.BUILD_URL}testReport/)\n"
        markdown += "</details>\n\n"
    }

    // Integration Tests Details
    if (stagesRun['Integration Tests']) {
        markdown += "<details>\n"
        markdown += "<summary><b>🔗 Integration Tests Details</b></summary>\n\n"

        if (integrationSummary.size() > 0) {
            markdown += "| Metric | Count |\n"
            markdown += "|--------|-------|\n"
            markdown += "| Total | ${integrationSummary.total} |\n"
            markdown += "| Passing | ${integrationSummary.passing} |\n"

            if (integrationSummary.failing > 0) {
                markdown += "| **Failing** | **${integrationSummary.failing}** |\n"
            } else {
                markdown += "| Failing | ${integrationSummary.failing} |\n"
            }

            markdown += "| Skipped | ${integrationSummary.skipped} |\n"
        }

        markdown += "\n[View Full Integration Test Results](${env.BUILD_URL}testReport/)\n"
        markdown += "</details>\n\n"
    }

    // Failed stages summary
    if (failed.size() > 0) {
        markdown += "### ❌ Failed Stages\n\n"
        failed.each { stage ->
            markdown += "- **${stage}** - [View Logs](${env.BUILD_URL}console)\n"
        }
        markdown += "\n"
    }

    // Unstable stages summary
    if (unstable.size() > 0) {
        markdown += "### ⚠️ Unstable Stages\n\n"
        unstable.each { stage ->
            markdown += "- **${stage}** - [View Logs](${env.BUILD_URL}console)\n"
        }
        markdown += "\n"
    }

    markdown += "---\n"
    markdown += "*Generated by Jenkins CI*"

    return markdown
}

def void postGitHubPRComment(stagesRun, stagesStatus, unitSummary, integrationSummary, failed, unstable) {
    try {
        def commentBody = buildTestResultsMarkdown(
            stagesRun,
            stagesStatus,
            unitSummary,
            integrationSummary,
            failed,
            unstable
        )

        publishChecks(
            name: 'Test Results',
            title: 'Test Execution Summary',
            text: commentBody,
            conclusion: currentBuild.result == 'SUCCESS' ? 'SUCCESS' : (currentBuild.result == 'UNSTABLE' ? 'NEUTRAL' : 'FAILURE'),
            detailsURL: "${env.BUILD_URL}"
        )

        echo "Posted test results to GitHub PR #${env.CHANGE_ID}"
    } catch (Exception e) {
        echo "Failed to post GitHub PR comment: ${e.message}"
        echo "This is non-fatal, continuing..."
    }
}

def boolean hasChangesIn(String module) {
    def compareAgainstMasterCommand = "git diff --name-only origin/master HEAD '${module}' | cat"
    def compareAgainstPreviousCommitCommand = "git diff-tree --no-commit-id --name-only -r ${env.GIT_COMMIT} -- '${module}'"

    def outputAgainstMaster = sh(script: compareAgainstMasterCommand, returnStdout: true).trim()
    def outputAgainstPrevious = sh(script: compareAgainstPreviousCommitCommand, returnStdout: true).trim()

    if (outputAgainstMaster || outputAgainstPrevious) {
        echo "Changes detected in module: '${module}'"
        return true
    } else {
        return false
    }
}
