From edbad9259217ce6f1a59fc207da58cbeb80f3c33 Mon Sep 17 00:00:00 2001 From: qishiyao Date: Fri, 28 Jul 2023 13:26:07 +0800 Subject: [PATCH] * Update Jenkinsfile for build image and zbox --- Jenkinsfile | 287 +++++++++++++++++++++++++++++++++++---- misc/gen_build_report.sh | 20 +-- 2 files changed, 271 insertions(+), 36 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 2dc3c95e41..a60cb6b5ad 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -18,6 +18,8 @@ pipeline { SRC_ZDOOEXT_PATH = "${WORKSPACE}/zdooext" SRC_ZENTAOEXT_PATH = "${WORKSPACE}/zentaoext" + + MIRROR = "true" } stages { @@ -106,6 +108,10 @@ pipeline { stages { stage("make ciCommon") { steps { + container('xuanimbot') { + sh 'git config --global --add safe.directory $(pwd)' + sh '/usr/local/bin/xuanimbot --groups 84be4c6e-02e3-4fdc-b081-318c0c1eca02 --groups 31a0008b-6e3e-4b7f-9b7b-396a46b1f8f4 --title "Start build zentaopms" --url "${BUILD_URL}" --content "Build by Tag ${TAG_NAME}" --debug --custom' + } withCredentials([gitUsernamePassword(credentialsId: 'git-zcorp-cc-jenkins-bot-http',gitToolName: 'git-tool')]) { container('package') { sh 'mkdir ${ZENTAO_RELEASE_PATH} && chown 1000:1000 ${ZENTAO_RELEASE_PATH}' @@ -181,6 +187,7 @@ pipeline { stages { stage("Merge and Upload") { + matrix { agent { kubernetes { @@ -190,10 +197,11 @@ pipeline { command "sleep" args "99d" } - inheritFrom "publish-qiniu" } } - options { skipDefaultCheckout() } + options { + skipDefaultCheckout() + } axes { axis { @@ -219,11 +227,15 @@ pipeline { } stages { - stage("frame") { + stage("ZIP") { environment { ARTIFACT_NAME = """${sh( returnStdout: true, - script: 'test ${ZLANG} = cn && echo -n zentaopms || echo -n zentaoalm' + script: 'test ${ZLANG} = cn && echo -n ZenTaoPMS || echo -n ZenTaoALM' + ).trim()}""" + INT_FLAG = """${sh( + returnStdout: true, + script: 'test ${ZLANG} = cn && echo -n "int." || echo -n ""' ).trim()}""" } @@ -296,11 +308,11 @@ pipeline { ) sh 'mkdir ${OUTPUT_PKG_PATH}/${PMS_VERSION} ${OUTPUT_PKG_PATH}/${BIZ_VERSION} ${OUTPUT_PKG_PATH}/${MAX_VERSION}' - sh 'cp ${ZENTAO_RELEASE_PATH}/base.zip ${OUTPUT_PKG_PATH}/${PMS_VERSION}/${ARTIFACT_NAME}-${PMS_VERSION}-${PHPVERSION}.zip' - sh 'mv ${ZENTAO_RELEASE_PATH}/biz.zip ${OUTPUT_PKG_PATH}/${BIZ_VERSION}/${ARTIFACT_NAME}-${BIZ_VERSION}-${PHPVERSION}.zip' - sh 'mv ${ZENTAO_RELEASE_PATH}/biz.update.zip ${OUTPUT_PKG_PATH}/${BIZ_VERSION}/${ARTIFACT_NAME}.update-${BIZ_VERSION}-${PHPVERSION}.zip' - sh 'mv ${ZENTAO_RELEASE_PATH}/max.zip ${OUTPUT_PKG_PATH}/${MAX_VERSION}/${ARTIFACT_NAME}-${MAX_VERSION}-${PHPVERSION}.zip' - sh 'mv ${ZENTAO_RELEASE_PATH}/max.update.zip ${OUTPUT_PKG_PATH}/${MAX_VERSION}/${ARTIFACT_NAME}.update-${MAX_VERSION}-${PHPVERSION}.zip' + sh 'cp ${ZENTAO_RELEASE_PATH}/base.zip ${OUTPUT_PKG_PATH}/${PMS_VERSION}/${ARTIFACT_NAME}.${PMS_VERSION}.${INT_FLAG}${PHPVERSION}.zip' + sh 'mv ${ZENTAO_RELEASE_PATH}/biz.zip ${OUTPUT_PKG_PATH}/${BIZ_VERSION}/${ARTIFACT_NAME}.${BIZ_VERSION}.${INT_FLAG}${PHPVERSION}.zip' + sh 'mv ${ZENTAO_RELEASE_PATH}/biz.update.zip ${OUTPUT_PKG_PATH}/${BIZ_VERSION}/${ARTIFACT_NAME}.${BIZ_VERSION}.${INT_FLAG}update.${PHPVERSION}.zip' + sh 'mv ${ZENTAO_RELEASE_PATH}/max.zip ${OUTPUT_PKG_PATH}/${MAX_VERSION}/${ARTIFACT_NAME}.${MAX_VERSION}.${INT_FLAG}${PHPVERSION}.zip' + sh 'mv ${ZENTAO_RELEASE_PATH}/max.update.zip ${OUTPUT_PKG_PATH}/${MAX_VERSION}/${ARTIFACT_NAME}.${MAX_VERSION}.${INT_FLAG}update.${PHPVERSION}.zip' } } // End upload zip @@ -324,8 +336,8 @@ pipeline { } steps { - sh 'mv ${ZENTAO_RELEASE_PATH}/zentao.rpm ${OUTPUT_PKG_PATH}/${PMS_VERSION}/${ARTIFACT_NAME}-${PMS_VERSION}-${PHPVERSION}-1.noarch.rpm' - sh 'mv ${ZENTAO_RELEASE_PATH}/zentao.deb ${OUTPUT_PKG_PATH}/${PMS_VERSION}/${ARTIFACT_NAME}-${PMS_VERSION}-${PHPVERSION}.1.all.deb' + sh 'mv ${ZENTAO_RELEASE_PATH}/zentao.rpm ${OUTPUT_PKG_PATH}/${PMS_VERSION}/${ARTIFACT_NAME}.${PMS_VERSION}.${PHPVERSION}.1.noarch.rpm' + sh 'mv ${ZENTAO_RELEASE_PATH}/zentao.deb ${OUTPUT_PKG_PATH}/${PMS_VERSION}/${ARTIFACT_NAME}.${PMS_VERSION}.${PHPVERSION}.1.all.deb' } } // End upload syspack cn @@ -335,7 +347,7 @@ pipeline { } environment { - QINIU_BUCKET = "qisytest" + QINIU_BUCKET = "download" OBJECT_KEY_PREFIX = "zentao/" QINIU_ACCESS_KEY = credentials('qiniu-upload-ak') QINIU_SECRET_KEY = credentials('qiniu-upload-sk') @@ -343,10 +355,10 @@ pipeline { steps { sh 'ls -l ${OUTPUT_PKG_PATH}' - container('qiniu') { - sh "qshell account $QINIU_ACCESS_KEY $QINIU_SECRET_KEY uploader" - sh "qshell qupload2 --bucket ${QINIU_BUCKET} --overwrite --src-dir ${OUTPUT_PKG_PATH} --key-prefix ${OBJECT_KEY_PREFIX}" - } + container('jnlp') { + sh 'qshell account $QINIU_ACCESS_KEY $QINIU_SECRET_KEY uploader' + sh 'qshell qupload2 --bucket $QINIU_BUCKET --overwrite --src-dir $OUTPUT_PKG_PATH --key-prefix $OBJECT_KEY_PREFIX' + } } } @@ -357,22 +369,245 @@ pipeline { } // End matrix } // End Merge and Upload Max + + stage("Notice ZIP") { + steps { + container('xuanimbot') { + sh 'env | grep GIT' + sh 'git config --global --add safe.directory $(pwd)' + sh './misc/gen_build_report.sh > /tmp/success.md' + sh '/usr/local/bin/xuanimbot --groups 84be4c6e-02e3-4fdc-b081-318c0c1eca02 --groups 31a0008b-6e3e-4b7f-9b7b-396a46b1f8f4 --title "zentaopms package success" --url "${RUN_DISPLAY_URL}" --content-file /tmp/success.md --debug --custom' + } + } + } + + stage("Docker Image") { + agent { + kubernetes { + inheritFrom "dind xuanim" + } + } + stages() { + stage("prepare") { + steps { + checkout scmGit(branches: [[name: "master"]], + extensions: [cloneOption(depth: 2, noTags: false, reference: '', shallow: true)], + userRemoteConfigs: [[credentialsId: 'git-zcorp-cc-jenkins-bot-http', url: 'https://git.zcorp.cc/app/zentao.git']] + ) + container('docker') { + sh "sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories" + sh "apk --no-cache add make bash jq git" + sh "docker run --privileged --rm tonistiigi/binfmt --install all" + sh "docker buildx create --name mybuilder --driver docker-container --bootstrap" + sh "docker buildx use mybuilder" + sh "make markdown-init" + } + } + } + stage("docker pms") { + environment { + ZENTAO_URL = """${sh( + returnStdout: true, + script: "echo ${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/repository/${ARTIFACT_REPOSITORY}/zentao/pmsPack/`echo ${GIT_TAG_BUILD_GROUP} | tr . /`/ZenTaoPMS" + ).trim()}""" + } + steps { + container('docker') { + sh 'env | grep ZENTAO' + sh 'make build' + } + } + } + + stage("docker biz") { + environment { + ZENTAO_URL = """${sh( + returnStdout: true, + script: "echo ${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/repository/${ARTIFACT_REPOSITORY}/zentao/bizPack/`echo ${GIT_TAG_BUILD_GROUP} | tr . /`/ZenTaoPMS" + ).trim()}""" + } + steps { + container('docker') { + sh 'make build-biz' + sh 'make build-biz-k8s' + } + } + } + + stage("docker max") { + environment { + ZENTAO_URL = """${sh( + returnStdout: true, + script: "echo ${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/repository/${ARTIFACT_REPOSITORY}/zentao/maxPack/`echo ${GIT_TAG_BUILD_GROUP} | tr . /`/ZenTaoPMS" + ).trim()}""" + } + steps { + container('docker') { + sh 'make build-max' + sh 'make build-max-k8s' + } + } + } + + stage("Image Notice") { + steps { + container('docker') { + sh 'make markdown-render > ./report.md' + } + container('xuanimbot') { + sh 'git config --global --add safe.directory $(pwd)' + sh '/usr/local/bin/xuanimbot --groups 84be4c6e-02e3-4fdc-b081-318c0c1eca02 --groups 31a0008b-6e3e-4b7f-9b7b-396a46b1f8f4 --title "zentaopms build image success" --url "${RUN_DISPLAY_URL}" --content-file ./report.md --debug --custom' + } + } + } + + } + } // End Docker Image + + stage("Zbox") { + agent { + kubernetes { + containerTemplate { + name "docker" + image "docker:23.0.6-dind-alpine3.17" + command "sleep" + args "99d" + } + inheritFrom "dind xuanim" + } + } + + stages() { + stage("Prepare") { + steps { + checkout scmGit(branches: [[name: "main"]], + extensions: [cloneOption(depth: 2, noTags: false, reference: '', shallow: true)], + userRemoteConfigs: [[credentialsId: 'git-zcorp-cc-jenkins-bot-http', url: 'https://git.zcorp.cc/devops/zbox-builder.git']] + ) + container('docker') { + sh "sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories" + sh "apk --no-cache add make bash jq git curl wget libarchive-tools p7zip mariadb-client" + sh "docker run --privileged --rm tonistiigi/binfmt --install all" + sh "docker buildx create --name mybuilder --driver docker-container --bootstrap" + sh "docker buildx use mybuilder" + sh 'curl https://pkg.qucheng.com/files/stacksmith/render-template-1.0.1-10-debian-11-amd64.tar.gz | tar zxf - -C /' + } + } + } + + stage("Build Zbox") { + environment { + // printf "$PKG_URL_FORMATTER" pmsPack ZenTaoPMS 18.5 ZenTaoPMS-18.5-php8.1.zip + PKG_URL_FORMATTER = """${sh( + returnStdout: true, + script: "echo ${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/repository/${ARTIFACT_REPOSITORY}/zentao/%s/`echo ${GIT_TAG_BUILD_GROUP} | tr . /`/%s/%s/%s" + ).trim()}""" + } + steps { + container('docker') { + sh 'bash build-zbox.sh zh-cn linux $PMS_VERSION $BIZ_VERSION $MAX_VERSION ipd1.0' + sh 'bash build-zbox.sh en linux $PMS_VERSION $BIZ_VERSION $MAX_VERSION ipd1.0' + sh 'ls -l release' + } + + nexusArtifactUploader( + nexusVersion: 'nexus3', + protocol: env.ARTIFACT_PROTOCOL, + nexusUrl: env.ARTIFACT_HOST, + groupId: 'zentao.pmsPack' + '.' + env.GIT_TAG_BUILD_GROUP, + version: env.PMS_VERSION, + repository: env.ARTIFACT_REPOSITORY, + credentialsId: env.ARTIFACT_CRED_ID, + artifacts: [ + [artifactId: 'ZenTaoPMS', + classifier: 'zbox_64', + file: './release/zh-cn/' + PMS_VERSION + '/ZenTaoPMS.' + PMS_VERSION + '.zbox_64.tar.gz', + type: 'tar.gz'], + [artifactId: 'ZenTaoALM', + classifier: 'zbox_64', + file: './release/en/' + PMS_VERSION + '/ZenTaoALM.' + PMS_VERSION + '.int.zbox_64.tar.gz', + type: 'tar.gz'] + ] + ) + + nexusArtifactUploader( + nexusVersion: 'nexus3', + protocol: env.ARTIFACT_PROTOCOL, + nexusUrl: env.ARTIFACT_HOST, + groupId: 'zentao.bizPack' + '.' + env.GIT_TAG_BUILD_GROUP, + version: env.BIZ_VERSION, + repository: env.ARTIFACT_REPOSITORY, + credentialsId: env.ARTIFACT_CRED_ID, + artifacts: [ + [artifactId: 'ZenTaoPMS', + classifier: 'zbox_64', + file: './release/zh-cn/' + BIZ_VERSION + '/ZenTaoPMS.' + BIZ_VERSION + '.zbox_64.tar.gz', + type: 'tar.gz'], + [artifactId: 'ZenTaoALM', + classifier: 'zbox_64', + file: './release/en/' + BIZ_VERSION + '/ZenTaoALM.' + BIZ_VERSION + '.int.zbox_64.tar.gz', + type: 'tar.gz'] + ] + ) + + nexusArtifactUploader( + nexusVersion: 'nexus3', + protocol: env.ARTIFACT_PROTOCOL, + nexusUrl: env.ARTIFACT_HOST, + groupId: 'zentao.maxPack' + '.' + env.GIT_TAG_BUILD_GROUP, + version: env.MAX_VERSION, + repository: env.ARTIFACT_REPOSITORY, + credentialsId: env.ARTIFACT_CRED_ID, + artifacts: [ + [artifactId: 'ZenTaoPMS', + classifier: 'zbox_64', + file: './release/zh-cn/' + MAX_VERSION + '/ZenTaoPMS.' + MAX_VERSION + '.zbox_64.tar.gz', + type: 'tar.gz'], + [artifactId: 'ZenTaoALM', + classifier: 'zbox_64', + file: './release/en/' + MAX_VERSION + '/ZenTaoALM.' + MAX_VERSION + '.int.zbox_64.tar.gz', + type: 'tar.gz'] + ] + ) + + sh 'script/lib/gen_report.sh > zbox-success.md' + container('xuanimbot') { + sh 'git config --global --add safe.directory $(pwd)' + sh '/usr/local/bin/xuanimbot --groups 84be4c6e-02e3-4fdc-b081-318c0c1eca02 --groups 31a0008b-6e3e-4b7f-9b7b-396a46b1f8f4 --title "zentaopms build zbox success" --url "${RUN_DISPLAY_URL}" --content-file zbox-success.md --debug --custom' + } + } + } + + stage("Upload Zbox to Qiniu") { + when { + environment name: 'GIT_TAG_BUILD_TYPE', value: 'release' + } + + environment { + QINIU_BUCKET = "download" + OBJECT_KEY_PREFIX = "zentao/" + QINIU_ACCESS_KEY = credentials('qiniu-upload-ak') + QINIU_SECRET_KEY = credentials('qiniu-upload-sk') + } + + steps { + container('jnlp') { + sh 'qshell account $QINIU_ACCESS_KEY $QINIU_SECRET_KEY uploader' + sh 'qshell qupload2 --bucket $QINIU_BUCKET --overwrite --src-dir ./release/zh-cn --key-prefix $OBJECT_KEY_PREFIX' + sh 'qshell qupload2 --bucket $QINIU_BUCKET --overwrite --src-dir ./release/en --key-prefix $OBJECT_KEY_PREFIX' + } + } + } // End Upload Zbox to Qiniu + } + } // End Zbox + } post { - success { - container('xuanimbot') { - sh 'env | grep GIT' - sh 'git config --global --add safe.directory $(pwd)' - sh './misc/gen_build_report.sh > /tmp/success.md' - sh '/usr/local/bin/xuanimbot --users "qishiyao" --title "zentaopms build success" --url "${RUN_DISPLAY_URL}" --content-file /tmp/success.md --debug --custom' - sh '/usr/local/bin/xuanimbot --groups "84be4c6e-02e3-4fdc-b081-318c0c1eca02" --title "zentaopms build success" --url "${RUN_DISPLAY_URL}" --content-file /tmp/success.md --debug --custom' - } - } failure { container('xuanimbot') { sh 'git config --global --add safe.directory $(pwd)' - sh '/usr/local/bin/xuanimbot --users qishiyao --users ${GIT_TAGGER_NAME} --title "zentaopms build failed" --url "${BUILD_URL}" --content "" --debug --custom' + sh '/usr/local/bin/xuanimbot --users "${GIT_TAGGER_NAME}" --groups 31a0008b-6e3e-4b7f-9b7b-396a46b1f8f4 --title "zentaopms build failed" --url "${BUILD_URL}" --content "" --debug --custom' } } } diff --git a/misc/gen_build_report.sh b/misc/gen_build_report.sh index eebbf068e2..ab41b24784 100755 --- a/misc/gen_build_report.sh +++ b/misc/gen_build_report.sh @@ -33,29 +33,29 @@ subGroupPath=`echo $GIT_TAG_BUILD_GROUP | sed 's/\./\//'` outputLine "#### 下载地址" outputLine "##### 开源版" -outputFrame "[${PMS_VERSION} (中文)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/pmsPack/$subGroupPath/zentaopms/${PMS_VERSION}/)" +outputFrame "[${PMS_VERSION} (中文)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/pmsPack/$subGroupPath/ZenTaoPMS/${PMS_VERSION}/)" outputFrame "\t\t" -outputLine "[${PMS_VERSION} (English)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/pmsPack/$subGroupPath/zentaoalm/${PMS_VERSION}/)" +outputLine "[${PMS_VERSION} (English)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/pmsPack/$subGroupPath/ZenTaoALM/${PMS_VERSION}/)" outputLine "##### 企业版" -outputFrame "[${BIZ_VERSION:3} (中文)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/bizPack/$subGroupPath/zentaopms/${BIZ_VERSION}/)" +outputFrame "[${BIZ_VERSION:3} (中文)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/bizPack/$subGroupPath/ZenTaoPMS/${BIZ_VERSION}/)" outputFrame "\t\t" -outputLine "[${BIZ_VERSION:3} 升级包 (中文)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/bizPack/$subGroupPath/zentaopms.update/${BIZ_VERSION}/)" +outputLine "[${BIZ_VERSION:3} 升级包 (中文)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/bizPack/$subGroupPath/ZenTaoPMS.update/${BIZ_VERSION}/)" -outputFrame "[${BIZ_VERSION:3} (English)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/bizPack/$subGroupPath/zentaoalm/${BIZ_VERSION}/)" +outputFrame "[${BIZ_VERSION:3} (English)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/bizPack/$subGroupPath/ZenTaoALM/${BIZ_VERSION}/)" outputFrame "\t" -outputLine "[${BIZ_VERSION:3} 升级包 (English)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/bizPack/$subGroupPath/zentaoalm.update/${BIZ_VERSION}/)" +outputLine "[${BIZ_VERSION:3} 升级包 (English)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/bizPack/$subGroupPath/ZenTaoALM.update/${BIZ_VERSION}/)" outputLine "##### 旗舰版" -outputFrame "[${MAX_VERSION:3} (中文)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/maxPack/$subGroupPath/zentaopms/${MAX_VERSION}/)" +outputFrame "[${MAX_VERSION:3} (中文)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/maxPack/$subGroupPath/ZenTaoPMS/${MAX_VERSION}/)" outputFrame "\t\t" -outputLine "[${MAX_VERSION:3} 升级包 (中文)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/maxPack/$subGroupPath/zentaopms.update/${MAX_VERSION}/)" +outputLine "[${MAX_VERSION:3} 升级包 (中文)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/maxPack/$subGroupPath/ZenTaoPMS.update/${MAX_VERSION}/)" -outputFrame "[${MAX_VERSION:3} (English)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/maxPack/$subGroupPath/zentaoalm/${MAX_VERSION}/)" +outputFrame "[${MAX_VERSION:3} (English)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/maxPack/$subGroupPath/ZenTaoALM/${MAX_VERSION}/)" outputFrame "\t" -outputLine "[${MAX_VERSION:3} 升级包 (English)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/maxPack/$subGroupPath/zentaoalm.update/${MAX_VERSION}/)" +outputLine "[${MAX_VERSION:3} 升级包 (English)](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/service/rest/repository/browse/${ARTIFACT_REPOSITORY}/zentao/maxPack/$subGroupPath/ZenTaoALM.update/${MAX_VERSION}/)" outputLine "##### 历史版本" outputLine "[查看历史版本](${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/#browse/browse:easycorp-snapshot)"