From 3efb049948bf424f94af354a8cb647e052c568ef Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Fri, 11 Sep 2026 15:42:35 +0800 Subject: [PATCH] ci: add overseas PDA H5 test deployment --- Jenkinsfile | 97 +++++++++++++++++++++++++++++++++++++++ README.md | 2 +- tests/deployment.test.mjs | 23 ++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 Jenkinsfile create mode 100644 tests/deployment.test.mjs diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..4a6a5cc --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,97 @@ +pipeline { + agent { label 'java-test' } + + environment { + NODE_BASE_IMAGE = '172.31.198.7:10082/java-project/node:22-alpine' + NGINX_BASE_IMAGE = '172.31.198.7:10082/java-project/nginx:1.27-alpine' + TEST_SITE_ROOT = '/www/wwwroot/test-pda-global.buggoglobal.com' + TEST_SITE_URL = 'https://test-pda-global.buggoglobal.com/' + } + + options { + timestamps() + disableConcurrentBuilds() + } + + stages { + stage('确认提交版本') { + steps { + script { + env.SOURCE_TAG = sh(script: 'git rev-parse --short=12 HEAD', returnStdout: true).trim() + } + sh 'git log -1 --oneline' + } + } + + stage('测试并构建 H5 验收站') { + steps { + sh ''' + set -eu + docker run --rm --pull=never \ + -u "$(id -u):$(id -g)" \ + -e HOME=/tmp \ + -v "$WORKSPACE:/workspace" \ + -w /workspace \ + "$NODE_BASE_IMAGE" \ + sh -lc 'npm ci && npm test && npm run build:h5' + + test -f dist/build/h5/index.html + grep -R -F '当前绑定仓没有匹配的待签收或签收中退货单' dist/build/h5/assets >/dev/null + ''' + } + } + + stage('发布测试站') { + steps { + sh ''' + set -eu + target="$(readlink -f "$TEST_SITE_ROOT")" + test "$target" = '/www/wwwroot/test-pda-global.buggoglobal.com' + + docker run --rm --pull=never \ + -e SOURCE_TAG="$SOURCE_TAG" \ + -v "$WORKSPACE/dist/build/h5:/source:ro" \ + -v "$target:/deploy" \ + "$NGINX_BASE_IMAGE" \ + sh -eu -c ' + next="/deploy/.codex-next-$SOURCE_TAG" + previous="/deploy/.codex-previous-$SOURCE_TAG" + rm -rf "$next" "$previous" + mkdir -p "$next" "$previous" + cp -a /source/. "$next/" + test -f "$next/index.html" + + rollback() { + for entry in index.html assets static; do + rm -rf "/deploy/$entry" + if [ -e "$previous/$entry" ]; then mv "$previous/$entry" "/deploy/$entry"; fi + done + } + committed=0 + trap '\''if [ "$committed" -eq 0 ]; then rollback; fi'\'' EXIT HUP INT TERM + + for entry in index.html assets static; do + if [ -e "/deploy/$entry" ]; then mv "/deploy/$entry" "$previous/$entry"; fi + done + for entry in assets static; do + if [ -e "$next/$entry" ]; then mv "$next/$entry" "/deploy/$entry"; fi + done + mv "$next/index.html" /deploy/index.html + committed=1 + trap - EXIT HUP INT TERM + rm -rf "$next" "$previous" + ' + ''' + } + } + + stage('发布检查') { + steps { + sh ''' + set -eu + curl -fsS "$TEST_SITE_URL" | grep -F '' >/dev/null + ''' + } + } + } +} diff --git a/README.md b/README.md index b10f9b2..b724f5b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 柏悦物流海外版 PDA -独立的 uni-app Vue 3 App 工程,正式产品形态是安装在海外仓手持 PDA 上的 App。工程不依赖国内 PDA 源码或仓库外共享目录,可独立安装依赖、运行、编译和发布;H5 只用于浏览器调试和编译门禁,不是最终交付形态。 +独立的 uni-app Vue 3 App 工程,正式产品形态是安装在海外仓手持 PDA 上的 App。工程不依赖国内 PDA 源码或仓库外共享目录,可独立安装依赖、运行、编译和发布;H5 只用于浏览器调试、测试环境验收和编译门禁,不是最终交付形态。测试环境 H5 由仓库根 `Jenkinsfile` 发布到独立站点,发布时保留站点目录中的 `.well-known`、`.user.ini`、`.htaccess` 和 `404.html`。 工作台提供柜号级真实入仓、托盘管理、排车拣货、出库任务、一件代发出库、任务完成和发车入口;派送单、派送状态与客户回访由 Admin/Client 承载。登录使用国内 Auth 统一签发的双 Token;海外业务请求统一经 `gateway-overseas`,不得请求国内 Firstleg/Warehouse 业务接口。 diff --git a/tests/deployment.test.mjs b/tests/deployment.test.mjs new file mode 100644 index 0000000..0b711dc --- /dev/null +++ b/tests/deployment.test.mjs @@ -0,0 +1,23 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const pipeline = readFileSync(new URL('../Jenkinsfile', import.meta.url), 'utf8') + +test('test H5 deployment validates the exact overseas PDA site root', () => { + assert.match(pipeline, /TEST_SITE_ROOT = '\/www\/wwwroot\/test-pda-global\.buggoglobal\.com'/) + assert.match(pipeline, /test "\$target" = '\/www\/wwwroot\/test-pda-global\.buggoglobal\.com'/) +}) + +test('test H5 deployment replaces only generated assets and keeps host files', () => { + assert.match(pipeline, /for entry in index\.html assets static/) + assert.doesNotMatch(pipeline, /rm -rf "?\$target"?\/\*/) + for (const preserved of ['.well-known', '.user.ini', '.htaccess', '404.html']) { + assert.doesNotMatch(pipeline, new RegExp(`rm -rf [^\\n]*${preserved.replace('.', '\\.')}`)) + } +}) + +test('test H5 deployment runs tests and asserts the neutral empty-result copy before publishing', () => { + assert.match(pipeline, /npm ci && npm test && npm run build:h5/) + assert.match(pipeline, /当前绑定仓没有匹配的待签收或签收中退货单/) +})