HBuilderX 目前仅支持 Windows 和 macOS,这给发布流水线造成不小的阻碍,我们最初的做法是在一台 Windows 上安装 HBuilderX、Jenkins(下文称之为HXJenkins),HXJenkins 使用 HBuilder cli 命令行打包,主 Jenkins 调用 HXJenkins API 进行构建。我们实践之后,发现不少问题:

  • HXJenkins 打包 h5,我们要制作成容器镜像,打包后的 dist 需要回传给 Linux 服务器,Linux 服务器再制作容器镜像。流程非常复杂,还需要考虑文件如何回传等问题;
  • HBuilder cli 时不时会出现打不开项目的问题,需要重启 HBuilderX
  • 有时打包会特别的慢,且容易报错
  • 主 Jenkins 调用 HXJenkins API 进行构建,HXJenkins 构建报错上游无法知道,增加心智负担
  • ……

我们希望能实现基于容器的 uniapp 打包方案,经过在 DCloud 官网及搜索一番了解后,主要的方案有两个:

  1. HBuilderX 项目转为 vue cli 项目
  2. 底层都是 node,HBuilderX 只是把操作封装罢了,因此研究 HBuilderX 的打包原理,通过一些技术手段分析 HBuilderX 最终是执行的什么命令来打包的

方案一相对简单,也是本文所探讨的方案,但是可能在你的项目上实践不会轻而易举就能成功,一般会在依赖包以及依赖包的版本上会踩坑。

方案二相对复杂,但是通用性应该更好,方案二已经有大佬写了博客以及在 github 上维护了源码,有兴趣的读者请参考 漫谈Uniapp App热更新包-Jenkins CI/CD打包工具链的搭建_uniapp jenkins-CSDN博客

创建项目脚手架

官方文档有详细的使用 cli 脚手架创建 uni-app 项目的教程,uni-app官网

我的是 Vue3/Vite 以 javascript 开发的工程,因此执行以下命令创建项目脚手架:

npx degit dcloudio/uni-preset-vue#vite my-vue3-project

将原项目的代码复制到脚手架项目src目录

HBuilderX 项目的目录结构是源码直接放在顶层目录下的,而 cli 项目的源码是放在 src 目录下,因此 HBuilderX 项目转 cli 项目需要将 HBuilderX 项目的所有文件复制到 cli 项目的 src 目录。

cd my-vue3-project
git clone https://git.com/my-name/my-uni-app-project.git
cp -R my-uni-app-project/* src

改造 package.json

以我这个项目为例,原项目 package.json 内容如下:

{
  "dependencies": {
    "axios": "^1.4.0",
    "dayjs": "^1.11.10",
    "esdk-obs-browserjs": "^3.23.5",
    "jsencrypt": "^3.3.2",
    "mimetype": "^0.0.8",
    "moment": "^2.29.4",
    "mp-html": "^2.4.2",
    "pinia": "2.0.33",
    "pinia-plugin-persist-uni": "1.2.0",
    "postal-mime": "^1.0.16",
    "vant": "^4.6.4",
    "vue": "^3.4.15",
    "watermark-js-plus": "^1.3.18"
  },
  "devDependencies": {
    "@dcloudio/types": "^3.4.0",
    "@dcloudio/uni-app": "3.0.0-3090620231104002",
    "@types/html5plus": "^1.0.3",
    "@types/uni-app": "^1.4.5",
    "@types/vue": "^2.0.0",
    "unplugin-vue-components": "^0.25.1",
    "vite": "^4.4.11"
  }
}

改造后的 package.json 内容如下:

{
  "dependencies": {
    "@dcloudio/uni-app": "3.0.0-3090620231104002",
    "@dcloudio/uni-app-plus": "3.0.0-3090620231104002",
    "@dcloudio/uni-components": "3.0.0-3090620231104002",
    "@dcloudio/uni-h5": "3.0.0-3090620231104002",
    "@dcloudio/uni-mp-alipay": "3.0.0-3090620231104002",
    "@dcloudio/uni-mp-baidu": "3.0.0-3090620231104002",
    "@dcloudio/uni-mp-jd": "3.0.0-3090620231104002",
    "@dcloudio/uni-mp-kuaishou": "3.0.0-3090620231104002",
    "@dcloudio/uni-mp-lark": "3.0.0-3090620231104002",
    "@dcloudio/uni-mp-qq": "3.0.0-3090620231104002",
    "@dcloudio/uni-mp-toutiao": "3.0.0-3090620231104002",
    "@dcloudio/uni-mp-weixin": "3.0.0-3090620231104002",
    "@dcloudio/uni-mp-xhs": "3.0.0-3090620231104002",
    "@dcloudio/uni-quickapp-webview": "3.0.0-3090620231104002",
    "axios": "^1.4.0",
    "dayjs": "^1.11.10",
    "esdk-obs-browserjs": "^3.23.5",
    "jsencrypt": "^3.3.2",
    "mimetype": "^0.0.8",
    "moment": "^2.29.4",
    "mp-html": "^2.4.2",
    "pinia": "2.0.33",
    "pinia-plugin-persist-uni": "1.2.0",
    "postal-mime": "^1.0.16",
    "vant": "^4.6.4",
    "vue": "^3.4.15",
    "watermark-js-plus": "^1.3.18"
  },
  "devDependencies": {
    "@dcloudio/types": "^3.4.0",
    "@dcloudio/uni-app": "3.0.0-3090620231104002",
    "@dcloudio/uni-automator": "3.0.0-3090620231104002",
    "@dcloudio/uni-cli-shared": "3.0.0-3090620231104002",
    "@dcloudio/uni-stacktracey": "3.0.0-3090620231104002",
    "@dcloudio/vite-plugin-uni": "3.0.0-3090620231104002",
    "@types/html5plus": "^1.0.3",
    "@types/uni-app": "^1.4.5",
    "@types/vue": "^2.0.0",
    "@vue/runtime-core": "^3.3.11",
    "less": "^4.2.0",
    "sass": "^1.74.1",
    "unplugin-vue-components": "^0.25.1",
    "vite": "^4.4.11"
  },
  "scripts": {
    "dev:app": "uni -p app",
    "dev:app-android": "uni -p app-android",
    "dev:app-ios": "uni -p app-ios",
    "dev:custom": "uni -p",
    "dev:h5": "uni",
    "dev:h5:ssr": "uni --ssr",
    "dev:mp-alipay": "uni -p mp-alipay",
    "dev:mp-baidu": "uni -p mp-baidu",
    "dev:mp-jd": "uni -p mp-jd",
    "dev:mp-kuaishou": "uni -p mp-kuaishou",
    "dev:mp-lark": "uni -p mp-lark",
    "dev:mp-qq": "uni -p mp-qq",
    "dev:mp-toutiao": "uni -p mp-toutiao",
    "dev:mp-weixin": "uni -p mp-weixin",
    "dev:mp-xhs": "uni -p mp-xhs",
    "dev:quickapp-webview": "uni -p quickapp-webview",
    "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
    "dev:quickapp-webview-union": "uni -p quickapp-webview-union",
    "build:app": "uni build -p app",
    "build:app-android": "uni build -p app-android",
    "build:app-ios": "uni build -p app-ios",
    "build:custom": "uni build -p",
    "build:h5": "uni build",
    "build:h5:ssr": "uni build --ssr",
    "build:mp-alipay": "uni build -p mp-alipay",
    "build:mp-baidu": "uni build -p mp-baidu",
    "build:mp-jd": "uni build -p mp-jd",
    "build:mp-kuaishou": "uni build -p mp-kuaishou",
    "build:mp-lark": "uni build -p mp-lark",
    "build:mp-qq": "uni build -p mp-qq",
    "build:mp-toutiao": "uni build -p mp-toutiao",
    "build:mp-weixin": "uni build -p mp-weixin",
    "build:mp-xhs": "uni build -p mp-xhs",
    "build:quickapp-webview": "uni build -p quickapp-webview",
    "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
    "build:quickapp-webview-union": "uni build -p quickapp-webview-union"
  }
}

HBuilderX 项目转 cli 项目步骤简单,前两个步骤是固定的,改造 package.json 可能会遇到很多报错,总体的改造思路如下:

以原项目的 package.json 为基础,加上脚手架的 package.json 中的 uni-* 等依赖,注意版本要和原项目的版本保持一致,否则可能会编译报错。如遇到报错,根据报错信息处理即可。

Jenkins pipeline

def GIT_AUTH = "xxx"
def HARBOR = "xxx"

pipeline {
    agent {
        kubernetes {
            yaml """
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: nodejs
    image: ${HARBOR}/library/node:18-alpine
    stdin: true
    tty: true
    resources:
      requests:
        cpu: "1"
        memory: 4Gi
      limits:
        cpu: "2"
    volumeMounts:
    - name: deployyaml
      subPath: deployment.yaml
      mountPath: /home/deployment.yaml
    - name: data
      mountPath: /home/jenkins/agent
  - name: kubectl
    image: ${HARBOR}/library/kubectl:1.24.7
    stdin: true
    tty: true
    volumeMounts:
    - name: deployyaml
      subPath: vue-deploy.yaml
      mountPath: /home/vue-deploy.yaml
    - name: data
      mountPath: /home/jenkins/agent
  - name: jq
    image: ${HARBOR}/library/jq:1.7.1
    stdin: true
    tty: true
    volumeMounts:
    - name: data
      mountPath: /home/jenkins/agent
  - name: jnlp
    image: ${HARBOR}/library/jenkins-slave:jdk17
    imagePullPolicy: Always
    volumeMounts:
    - name: docker-sock
      mountPath: /var/run/docker.sock
    - name: docker
      mountPath: /usr/bin/docker
    - name: data
      mountPath: /home/jenkins/agent
  volumes:
  - name: docker-sock
    hostPath:
      path: /var/run/docker.sock
  - name: docker
    hostPath:
      path: /usr/bin/docker
  - name: config
    configMap:
      name: mvn-cm
  - name: deployyaml
    configMap:
      name: vue-deploy
  - name: data
    persistentVolumeClaim:
      claimName: jenkins

"""
            defaultContainer 'jnlp'
        }
    }
    stages {
        stage('拉取代码') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: "vite"]], userRemoteConfigs: [[credentialsId: "${GIT_AUTH}", url: "https://git.domain/uni-preset-vue.git"]]])
                checkout([$class: 'GitSCM', extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'xxx']], branches: [[name: "${BRANCH}"]], userRemoteConfigs: [[credentialsId: "${GIT_AUTH}", url: "https://git.domain/xxx.git"]]])
            }
        }
        stage('代码编译') {
            steps {
                container('jq') {
                    sh """
                    cp package.json package.json.back
                    jq '{"dependencies": .dependencies, "devDependencies": .devDependencies, "scripts": input.scripts}' xxx/package.json package.json.back > package.json
                    sed -i '/dependencies/a"@dcloudio/uni-app": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-app-plus": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-components": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-h5": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-mp-alipay": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-mp-baidu": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-mp-jd": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-mp-kuaishou": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-mp-lark": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-mp-qq": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-mp-toutiao": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-mp-weixin": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-mp-xhs": "3.0.0-3090620231104002",\\n    "@dcloudio/uni-quickapp-webview": "3.0.0-3090620231104002",' package.json
                    sed -i '/devDependencies/a"@dcloudio/uni-automator": "3.0.0-3090620231104002",\\n"@dcloudio/uni-cli-shared": "3.0.0-3090620231104002",\\n"@dcloudio/uni-stacktracey": "3.0.0-3090620231104002",\\n"@dcloudio/vite-plugin-uni": "3.0.0-3090620231104002",\\n"@vue/runtime-core": "^3.3.11",' package.json
                    """
                }
                container('nodejs') {
                    sh """
                    npm install
                    npm i sass less -D
                    cp -R xxx/* src
                    npm run build:h5
                    """                    
                }
            }
        }
        stage('构建镜像') {
            steps {
                ...
            }
        }
        stage('部署'){
            steps{
                ...
            }
        }
    }
}

标签: none

添加新评论