pipeline {
    agent any

    // 定义版本参数
    // 注意：如果 Jenkins 后台已设置默认值，不设置 defaultValue 可以保留后台的值
    // trim: true 保留后台的 "Trim the string" 设置
    // parameters {
    //     // string(name: 'branch', defaultValue: 'v3.0.0', description: '分支或标签，如 main / v1.2.3')
    //     string(name: 'branch', description: '分支或标签，如 main / v1.2.3', trim: true)
    // }

    environment {
        WORKSPACE_BASE_NAME = "<myproject 需要替换>"
        PROJECT_NAME = "<server1 需要替换>"
        // 这个目录是基于当前 jenkins 任务工作目录的相对目录
        // 比如 jenkins 当前的任务是 <myproject 需要替换>/<server1 需要替换>
        // 那么,实际的工作目录是 <myproject 需要替换>/<server1 需要替换>/<myproject 需要替换>/<server1 需要替换>
        WORKSPACE_DIR = "${WORKSPACE_BASE_NAME}/${PROJECT_NAME}"
        JENKINS_HOME = "/var/lib/jenkins"

        REPO_URL = "<gitlab 仓库地址>/<server1 需要替换>.git"  // 替换为实际的仓库URL
        credentialsId = "<jenkins 链接 gitlab 的凭证 ID 需要替换>"
        DOCKER_IMG  = 'maven:3.9.6-eclipse-temurin-8'
        // 抓取代码的目录
        GIT_DIR = 'app'
        CONFIG_DIR = 'config'
        SCRIPTS_DIR = 'scripts'
        BUILD_SCRIPT = "${SCRIPTS_DIR}/build.sh"
        PACKAGE_SCRIPT = "${SCRIPTS_DIR}/package.sh"
        SEND_SCRIPT = "${SCRIPTS_DIR}/send.sh"
        PUBLISH_SCRIPT = "${SCRIPTS_DIR}/publish.sh"

        // 编译环境, prod / dev / test
        BUILD_ENV = '<test 需要替换>'
        // 编译后的文件目录
        BUILD_DIR = 'target'
        BUILD_TARGET_DIR = "${GIT_DIR}/${BUILD_DIR}"
        // 编译后的jar文件
        BUILD_FILE   = "<server1 需要替换>.jar"
        // 编译后的静态资源目录
        STATIC_DIR = "src/main/resources/static"
        // 打包后的文件名
        TAR_NAME = "${PROJECT_NAME}.tar.gz"
        TAR_FILE = "${BUILD_TARGET_DIR}/${TAR_NAME}"

        // 远端服务器配置
        // 服务器地址
        REMOTE_HOST = '<远端服务器地址 需要替换>'
        // 服务器端口
        REMOTE_PORT = '22'
        // jenkins 凭证 ID
        SSH_CRED   = '<jenkins 链接到远端服务器的 凭证 ID 需要替换>'   // Jenkins Credentials ID

        // 远端服务器工作目录
        REMOTE_WORK_PATH = "/data/${WORKSPACE_BASE_NAME}"
        // 远端服务器应用目录
        REMOTE_APP_RELATIVE_PATH = "apps"
        REMOTE_APP_PATH = "${REMOTE_WORK_PATH}/${REMOTE_APP_RELATIVE_PATH}"
        REMOTE_PROJECT_NAME = "${PROJECT_NAME}"
        REMOTE_TOOLS_DIR = "tools"
        REMOTE_TOOLS_PATH = "${REMOTE_WORK_PATH}/${REMOTE_TOOLS_DIR}"
    }

    stages {
        stage('Prepare') {
            steps {
                // 设置工作目录
                dir("${WORKSPACE_DIR}") {
                    script {
                        echo "Prepare ..."

                        // 检查 config 目录是否存在
                        if (!fileExists("${CONFIG_DIR}")) {
                            error("${CONFIG_DIR} 目录不存在!")
                        }

                        echo "${CONFIG_DIR} 检查通过"

                        // 检查构建脚本文件
                        if (!fileExists("${BUILD_SCRIPT}")) {
                            error("脚本文件不存在: ${BUILD_SCRIPT}")
                        }
                        echo "${BUILD_SCRIPT} 检查通过"

                        // 检查打包脚本文件
                        if (!fileExists("${PACKAGE_SCRIPT}")) {
                            error("脚本文件不存在: ${PACKAGE_SCRIPT}")
                        }
                        echo "${PACKAGE_SCRIPT} 检查通过"

                        // 检查发送脚本文件
                        if (!fileExists("${SEND_SCRIPT}")) {
                            error("脚本文件不存在: ${SEND_SCRIPT}")
                        }
                        echo "${SEND_SCRIPT} 检查通过"

                        // 检查发布脚本文件
                        if (!fileExists("${PUBLISH_SCRIPT}")) {
                            error("脚本文件不存在: ${PUBLISH_SCRIPT}")
                        }
                        echo "${PUBLISH_SCRIPT} 检查通过"
                    }
                }
            }
        }


        stage('Clean Files') {
            steps {
                // 清理之前打包的文件
                script {
                    echo "Clean Files ..."

                    sh """
                        chmod +x ${CLEAN_SCRIPT}
                        GIT_DIR='${GIT_DIR}' \\
                        BUILD_ENV='${BUILD_ENV}' \\
                        BUILD_TARGET_DIR='${BUILD_TARGET_DIR}' \\
                        TAR_FILE='${TAR_FILE}' \\
                        bash ${CLEAN_SCRIPT}
                    """
                }
            }
        }

        stage('Clone Repository') {
            // 设置 stage 级别的超时时间，防止一直卡住
            // 超时时间：1 分钟（可根据实际情况调整）
            options {
                timeout(time: 1, unit: 'MINUTES')
            }
            steps {
                dir("${WORKSPACE_DIR}") {
                    script {
                        echo "Clone Repository ..."
                        try {
                            echo "正在拉取分支/标签: ${BRANCH}"
                            //获取主代码
                            checkout(
                                [
                                    $class: 'GitSCM',
                                    branches: [
                                        [name: "${BRANCH}"]
                                    ],
                                    doGenerateSubmoduleConfigurations: false,
                                    extensions: [
                                        [
                                            $class: 'RelativeTargetDirectory',
                                            relativeTargetDir: "${GIT_DIR}"
                                        ]
                                    ],
                                    submoduleCfg: [],
                                    userRemoteConfigs: [
                                        [
                                            credentialsId: env.credentialsId,
                                            url: env.REPO_URL
                                        ]
                                    ]
                                ]
                            )
                            // 验证代码是否成功拉取（使用 sh 命令，避免 new File() 导致沙箱限制）
                            def gitDir = "${GIT_DIR}"

                            // 检查目录是否存在
                            def dirExists = sh(
                                script: "test -d ${gitDir} && echo 'exists' || echo 'not_exists'",
                                returnStdout: true
                            ).trim()

                            if (dirExists != 'exists') {
                                error("代码拉取失败，${gitDir} 目录不存在。请检查分支/标签 '${BRANCH}' 是否存在")
                            }

                            // 检查目录是否为空
                            def isEmpty = sh(
                                script: "test -z \"\$(ls -A ${gitDir} 2>/dev/null)\" && echo 'empty' || echo 'not_empty'",
                                returnStdout: true
                            ).trim()

                            if (isEmpty == 'empty') {
                                error("代码拉取失败，${gitDir} 目录为空。请检查分支/标签 '${BRANCH}' 是否存在")
                            }

                            echo "代码拉取成功: ${gitDir}"
                        } catch (Exception e) {
                            error("拉取分支/标签 '${BRANCH}' 失败: ${e.getMessage()}")
                        }
                    }
                }
            }
        }

        stage('Check') {
            steps {
                // 设置工作目录
                dir("${WORKSPACE_DIR}") {
                    script {
                        echo "Check ..."


                    }
                }
            }
        }

        stage('Build') {
            steps {
                dir("${WORKSPACE_DIR}") {
                    script {
                        echo "Build ..."
                        // 执行构建脚本，并传递环境变量
                        sh """
                            chmod +x ${BUILD_SCRIPT}
                            PROJECT_NAME='${PROJECT_NAME}' \\
                            CONFIG_DIR='${CONFIG_DIR}' \\
                            GIT_DIR='${GIT_DIR}' \\
                            BUILD_ENV='${BUILD_ENV}' \\
                            BUILD_DIR='${BUILD_DIR}' \\
                            DOCKER_IMG='${DOCKER_IMG}' \\
                            BUILD_TARGET_DIR='${BUILD_TARGET_DIR}' \\
                            BUILD_FILE='${BUILD_FILE}' \\
                            bash ${BUILD_SCRIPT}
                        """
                    }
                }
            }
        }

        stage('Package') {
            steps {
                dir("${WORKSPACE_DIR}") {
                    script {
                        echo "Package ..."
                        // 执行打包脚本，并传递环境变量
                        // 使用 try-catch 安全地访问可能未定义的变量
                        def dbAwdbDir = ""
                        def dbIpdbDir = ""
                        try {
                            dbAwdbDir = env.DB_AWDB_DIR ?: ""
                            dbIpdbDir = env.DB_SOURCE_DIR ?: ""
                        } catch (Exception e) {
                            dbAwdbDir = ""
                            dbIpdbDir = ""
                        }

                        sh """
                            chmod +x ${PACKAGE_SCRIPT}
                            BUILD_TARGET_DIR='${BUILD_TARGET_DIR}' \\
                            CONFIG_DIR='${CONFIG_DIR}' \\
                            STATIC_DIR='${STATIC_DIR}' \\
                            DB_SOURCE_DIR='${dbIpdbDir}' \\
                            DB_AWDB_DIR='${dbAwdbDir}' \\
                            TAR_NAME='${TAR_NAME}' \\
                            bash ${PACKAGE_SCRIPT}
                        """
                    }
                }
            }
        }

        stage('Send') {
            steps {
                dir("${WORKSPACE_DIR}") {
                    script {
                        echo "Send ..."
                    }
                    // 将打包好的文件上传到服务器
                    //用法 keyFileVariable: 'SSH_IDENTITY',表示将 钥匙串中的 私钥文件存储在 SSH_IDENTITY 环境变量中
                    withCredentials(
                        [sshUserPrivateKey(credentialsId: "${SSH_CRED}",
                        keyFileVariable: 'SSH_IDENTITY',
                        passphraseVariable: '',
                        usernameVariable: 'SSH_USER_NAME')]
                    ) {
                        script {
                            // 验证打包文件是否存在
                            def tarFile = "${TAR_FILE}"
                            if (!fileExists(tarFile)) {
                                error("打包文件不存在: ${tarFile}")
                            }

                            def sshServer = "${SSH_USER_NAME}@${REMOTE_HOST}"

                            echo "--- 上传打包文件到远端 ---"
                            echo "源文件: ${tarFile}"
                            echo "目标服务器: ${sshServer}:${REMOTE_APP_PATH}"

                            // 执行上传脚本，并传递环境变量
                            // 注意：SSH_IDENTITY 和 SSH_USER_NAME 来自 withCredentials
                            sh """
                                chmod +x ${SEND_SCRIPT}
                                SSH_IDENTITY=\${SSH_IDENTITY} \\
                                REMOTE_PORT='${REMOTE_PORT}' \\
                                SSH_USER_NAME=\${SSH_USER_NAME} \\
                                REMOTE_HOST='${REMOTE_HOST}' \\
                                REMOTE_APP_PATH='${REMOTE_APP_PATH}' \\
                                TAR_FILE='${tarFile}' \\
                                bash ${SEND_SCRIPT}
                            """

                            echo "--- 上传打包完成 ---"
                        }
                    }
                }
            }
        }

        stage('Publish') {
            steps {
                dir("${WORKSPACE_DIR}") {
                    script {
                        echo "Publish ..."
                    }
                    // 执行远端重启脚本
                    //用法 keyFileVariable: 'SSH_IDENTITY',表示将 钥匙串中的 私钥文件存储在 SSH_IDENTITY 环境变量中
                    withCredentials(
                        [sshUserPrivateKey(credentialsId: "${SSH_CRED}",
                        keyFileVariable: 'SSH_IDENTITY',
                        passphraseVariable: '',
                        usernameVariable: 'SSH_USER_NAME')]
                    ) {
                        script {
                            echo "--- 执行远端脚本 ---"

                            // 在 Groovy 中定义 sshServer，这样可以在 sh """ 中使用
                            def sshServer = "${SSH_USER_NAME}@${REMOTE_HOST}"

                            // 上传脚本到远程服务器并执行
                            // 注意：shell 脚本可以通过环境变量引用，需要在执行时传递环境变量
                            sh """
                                # 设置私钥权限
                                chmod 600 \${SSH_IDENTITY}

                                # 上传脚本文件到远程服务器
                                scp -i \${SSH_IDENTITY} \\
                                    -o StrictHostKeyChecking=no \\
                                    -o UserKnownHostsFile=/dev/null \\
                                    -P ${REMOTE_PORT} \\
                                    ${PUBLISH_SCRIPT} \\
                                    ${sshServer}:${REMOTE_APP_PATH}/

                                # 执行远程脚本，并传递环境变量
                                # 方式：在命令前设置环境变量，shell 脚本中的 \${VAR} 会被替换
                                # 使用单引号包裹变量值，避免特殊字符问题
                                ssh -i \${SSH_IDENTITY} \\
                                    -o StrictHostKeyChecking=no \\
                                    -o UserKnownHostsFile=/dev/null \\
                                    -p ${REMOTE_PORT} \\
                                    ${sshServer} \\
                                    "REMOTE_APP_PATH='${REMOTE_APP_PATH}' \\
                                     REMOTE_PROJECT_NAME='${REMOTE_PROJECT_NAME}' \\
                                     TAR_NAME='${TAR_NAME}' \\
                                     REMOTE_WORK_PATH='${REMOTE_WORK_PATH}' \\
                                     REMOTE_TOOLS_PATH='${REMOTE_TOOLS_PATH}' \\
                                     bash ${REMOTE_APP_PATH}/publish.sh"

                                # 清理远程脚本文件（可选）
                                ssh -i \${SSH_IDENTITY} \\
                                    -o StrictHostKeyChecking=no \\
                                    -o UserKnownHostsFile=/dev/null \\
                                    -p ${REMOTE_PORT} \\
                                    ${sshServer} \\
                                    "rm -f ${REMOTE_APP_PATH}/publish.sh" || true
                            """

                            echo "--- 执行脚本结束 ---"
                        }
                    }
                }
            }
        }
    }

}
