Jenkins「鸿蒙打包」环境配置教程(linux环境为例)
Jenkins「鸿蒙打包」环境配置教程
以 192.168..(root / Jenkins#123)上的 Jenkins 任务 「鸿蒙打包」 为准整理
(job 地址:http://192.0.0.168/..:8080/job/鸿蒙打包/ ,完整 config.xml 见harmony-cicd/job_鸿蒙打包_config.xml)。
适用场景:已有原生鸿蒙工程(含hvigorfile.ts),上传 zip → ohpm + hvigor 构建 → 产出 har / app / hap。
1. 方案总览
| 项 | 内容 |
|---|---|
| 打包方式 | 纯命令行工具链(ohpm + hvigorw),不依赖 HBuilderX / DevEco IDE |
| 输入 | 用户在构建页上传鸿蒙工程压缩包 harmonyProject.zip(需含 hvigorfile.ts) |
| 输出 | $WORKSPACE/output/ 下的 *.har、*.app、*.hap,由 Jenkins ArtifactArchiver 归档 |
| 构建类型 | 参数 AppBuildType:release(默认)/ debug |
| SDK | OpenHarmony SDK 5.0.3.135(/opt/harmonyos/sdk),向下兼容 5.0.0(12) 工程 |
| JDK | OpenJDK 21(/usr/lib/jvm/java-21-openjdk-amd64,Jenkins 2.555.2 运行于其上) |
| Node | /opt/nodejs(ohpm/hvigor 依赖) |
| 服务器 | 192.168.*.*(Ubuntu,Jenkins 直装 8080 端口,非容器) |
流程示意:
浏览器构建页 ──上传 harmonyProject.zip──▶ Jenkins 任务「鸿蒙打包」
│
▼
构建脚本(8 步,详见第 5 节)
解压 → 定位工程根 → 修 json5 → ohpm install
→ assembleHar(软性) → assembleApp(硬性)
│
▼
$WORKSPACE/output/*.app|har|hap → 页面下载
2. 第一步:安装基础软件
# 1) JDK 21(Jenkins 与 hvigor 都用它)
apt install -y openjdk-21-jdk
java -version # 应显示 openjdk 21.0.7
# 2) Node.js(ohpm/hvigor 运行时)
mkdir /opt/nodejs && tar -xzf node-vXX.tar.xz -C /opt/nodejs --strip-components=1
# 3) Jenkins 2.555.2(war 直装,监听 8080;升级原因:Java 21 兼容)
# /var/lib/jenkins 为 JENKINS_HOME
3. 第二步:安装 DevEco commandline-tools 与 SDK
# 目录布局
/opt/harmonyos/
├── commandline-tools/ # DevEco 命令行工具 5.0.3.135(bin/ 下有 hvigorw、ohpm、codelinter 等)
│ ├── bin/hvigorw
│ ├── ohpm/bin/ohpm
│ └── hvigor/bin/
├── sdk/ # OpenHarmony SDK 5.0.3.135
└── projects/ # 构建工作区(harmony-app 为本任务的解压目录)
⚠️ 若工具链版本校验报错,可把 commandline-tools 内
version.txt改写为高版本号伪装
(244 现状:5.0.3.135 伪装成 5.1.0.849 绕过版本检查)。仅原生工程打包时通常不需要。
4. 第三步:配置环境变量 /etc/profile.d/harmonyos.sh
构建脚本第一步就 source 它,内容必须与下面一致:
# HarmonyOS / OpenHarmony Build Environment
export HARMONY_HOME=/opt/harmonyos
export OHOS_BASE_SDK_HOME=/opt/harmonyos/sdk
export OHOS_HOME=/opt/harmonyos/commandline-tools
export DEVECO_SDK_HOME=/opt/harmonyos/sdk
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
export NODE_HOME=/opt/nodejs
export PATH=/opt/nodejs/bin:/opt/harmonyos/commandline-tools/bin:/opt/harmonyos/commandline-tools/ohpm/bin:/opt/harmonyos/commandline-tools/hvigor/bin:$PATH
验证:su - jenkins -c '. /etc/profile.d/harmonyos.sh && which ohpm hvigorw' 两个都能找到即通过。
(ohpm 也可以先 ohpm config set registry <内网镜像> 按需配置。)
5. 第四步:创建 Jenkins 任务「鸿蒙打包」
Jenkins → 新建 Item → Freestyle project → 命名 鸿蒙打包,按以下配置:
5.1 参数化构建(2 个参数)
| 类型 | 名称 | 说明 |
|---|---|---|
| File Parameter | harmonyProject.zip | 鸿蒙工程压缩包(需包含 hvigorfile.ts,兼容 5.0.0(12) 及以上) |
| Choice Parameter | AppBuildType | 构建类型:release / debug,默认 release |
任务描述(description)建议写:
鸿蒙 HarmonyOS 打包(基于 5.0.0(12),SDK 5.0.3 向下兼容):上传鸿蒙工程压缩包,解压到 /opt/harmonyos/projects 后用 ohpm + hvigor 构建,产出 har 与 app 包
其他设置:SCM = None;不配触发器(手动触发);禁止并发构建 勾选(构建会占用 /opt/harmonyos/projects/harmony-app 固定目录)。
5.2 构建步骤(Execute Shell,完整脚本)
set -e
echo "====== 鸿蒙打包构建 ======"
echo "Build Type: ${AppBuildType}"
echo "Workspace: ${WORKSPACE}"
# 加载鸿蒙环境变量
. /etc/profile.d/harmonyos.sh
export HOME=/var/lib/jenkins
ZIP_NAME="harmonyProject.zip"
TARGET_DIR="/opt/harmonyos/projects/harmony-app"
# 1. 定位上传的压缩包(优先 workspace 副本,兜底搜 build 目录与 /tmp)
ZIP_FILE="${WORKSPACE}/${ZIP_NAME}"
if [ ! -f "${ZIP_FILE}" ]; then
ZIP_FILE=$(find "${JENKINS_HOME}/jobs" -path "*fileParameters*" -name "${ZIP_NAME}" -mmin -60 2>/dev/null | head -1)
fi
if [ ! -f "${ZIP_FILE}" ]; then
ZIP_FILE=$(find /tmp -maxdepth 3 -name "${ZIP_NAME}" -mmin -60 2>/dev/null | head -1)
fi
if [ ! -f "${ZIP_FILE}" ]; then
echo "ERROR: 未找到上传的 ${ZIP_NAME}"
exit 1
fi
echo "压缩包: ${ZIP_FILE} ($(du -h "${ZIP_FILE}" | cut -f1))"
# 2. 清理并解压到鸿蒙目录(剔除 Mac 压缩包的 __MACOSX/.DS_Store 垃圾)
echo "=== 解压到 ${TARGET_DIR} ==="
rm -rf "${TARGET_DIR}"
mkdir -p "${TARGET_DIR}"
unzip -q "${ZIP_FILE}" -d "${TARGET_DIR}" -x "__MACOSX/*" "*.DS_Store"
# 3. 定位工程根目录(根工程的 build-profile.json5 必含 "modules" 数组;取层级最浅者,
# 避免误判 entry 等模块目录——它们也有 hvigorfile.ts 但没有 modules 数组)
HV_FILE=$(find "${TARGET_DIR}" -name hvigorfile.ts -not -path "*__MACOSX*" -type f | while read -r f; do
d=$(dirname "${f}")
if [ -f "${d}/build-profile.json5" ] && grep -q '"modules"' "${d}/build-profile.json5"; then
printf '%s\t%s\n' "$(echo "${d#${TARGET_DIR}}" | tr -cd '/' | wc -c)" "${f}"
fi
done | sort -n | head -1 | cut -f2)
if [ -z "${HV_FILE}" ]; then
echo "ERROR: 压缩包中未找到有效的鸿蒙工程根目录(hvigorfile.ts + 含 modules 的 build-profile.json5)"
exit 1
fi
PROJ_ROOT=$(dirname "${HV_FILE}")
echo "工程根目录: ${PROJ_ROOT}"
cd "${PROJ_ROOT}"
# 3.5 修复 Mac 导出工程常见问题:modules 数组中同名仅大小写不同的重复条目
# (Linux 文件系统大小写敏感,hvigor 报 Duplicated modules)。
# 用括号深度跟踪定位每个模块条目的行区间,仅删除重复条目行,其余原样保留。
python3 - "${PROJ_ROOT}/build-profile.json5" <<'PYEOF'
import re, sys
p = sys.argv[1]
lines = open(p, encoding='utf-8').read().splitlines(True)
mi = None
for i, l in enumerate(lines):
if '"modules"' in l and '[' in l:
mi = i
break
if mi is None:
sys.exit(0)
def net(l):
return l.count('[') - l.count(']') + l.count('{') - l.count('}')
depth = net(lines[mi])
blocks, cur, i = [], None, mi + 1
while i < len(lines):
l = lines[i]
s = l.strip()
d = depth
depth += net(l)
if cur is None and s == '{' and d == 1:
cur = i
elif cur is not None and s in ('}', '},') and depth == 1:
blk = ''.join(lines[cur:i + 1])
m = re.search(r'"name"\s*:\s*"([^"]+)"', blk)
blocks.append((cur, i + 1, m.group(1) if m else None))
cur = None
if depth <= 0:
break
i += 1
if not blocks:
sys.exit(0)
seen, drop = set(), []
for a, b, nm in blocks:
if not nm:
continue
k = nm.lower()
if k in seen:
drop.append((a, b, nm))
else:
seen.add(k)
if drop:
dropset = set()
for a, b, nm in drop:
dropset.update(range(a, b))
print("FIXED: removed duplicated module entry %s" % nm)
open(p, 'w', encoding='utf-8').write(''.join(
l for idx, l in enumerate(lines) if idx not in dropset))
print("FIXED: modules %d -> %d" % (len(blocks), len(blocks) - len(drop)))
PYEOF
# 4. 补齐缺失的 hvigor 配置(基于 5.0.0(12) 模型)
if [ ! -f hvigor/hvigor-config.json5 ]; then
echo "WARN: 缺少 hvigor/hvigor-config.json5,生成默认配置 (modelVersion 5.0.0)"
mkdir -p hvigor
printf '{\n "modelVersion": "5.0.0",\n "dependencies": {},\n "execution": {},\n "logging": {},\n "debugging": {},\n "nodeOptions": {}\n}\n' > hvigor/hvigor-config.json5
fi
# 5. 定位 hvigorw:优先项目自带,否则用全局命令行工具
if [ -f ./hvigorw ]; then
chmod +x ./hvigorw
HVIGOR_CMD=./hvigorw
else
HVIGOR_CMD=/opt/harmonyos/commandline-tools/bin/hvigorw
fi
echo "hvigorw: ${HVIGOR_CMD}"
# 6. 安装 ohpm 依赖(含所有模块)
echo "=== ohpm install --all ==="
ohpm install --all
# 7. 构建 har + app 包(工程可能不含 har 模块,assembleHar 失败不中断;assembleApp 为硬性要求)
echo "=== assembleHar (${AppBuildType}) ==="
if ! "${HVIGOR_CMD}" assembleHar --mode module -p product=default -p buildMode=${AppBuildType} --no-daemon; then
echo "WARN: assembleHar 失败或工程无 har 类型模块,继续打包 app"
fi
echo "=== assembleApp (${AppBuildType}) ==="
# assembleApp 为工程级任务(打包所有 hap 为 .app),必须用 --mode project
"${HVIGOR_CMD}" assembleApp --mode project -p product=default -p buildMode=${AppBuildType} --no-daemon
# 8. 收集构建产物(仅收集 build 输出目录,避免混入依赖包)
echo "=== 收集构建产物 ==="
OUT_DIR="${WORKSPACE}/output"
rm -rf "${OUT_DIR}"
mkdir -p "${OUT_DIR}"
find "${PROJ_ROOT}" -path "*/build/*" -not -path "*/cache/*" \( -name "*.har" -o -name "*.app" -o -name "*.hap" \) -type f | while read -r f; do
echo "Found: ${f}"
cp -f "${f}" "${OUT_DIR}/"
done
echo "=== 产物列表 ==="
ls -lh "${OUT_DIR}"
if [ -z "$(ls "${OUT_DIR}" 2>/dev/null)" ]; then
echo "ERROR: 未生成任何 har/app 产物"
exit 1
fi
echo "====== 鸿蒙打包完成 ======"
5.3 构建后操作(归档产物)
- Archive the artifacts:
output/*.har,output/*.app,output/*.hap - 留空"只有在构建成功时归档"以外的选项,保持默认即可。
6. 脚本设计要点(为什么这么写)
set -e+ 兜底查找:File Parameter 的上传文件不一定落在 workspace(Jenkins 会存到$JENKINS_HOME/jobs/<job>/builds/<n>/fileParameters/或/tmp),所以脚本按
workspace → jobs 目录 → /tmp 三级查找,且都限定mmin -60(只认 1 小时内的文件,防误用旧包)。- Mac zip 防御:macOS 压缩的 zip 带
__MACOSX/、.DS_Store,解压时-x排除,
否则 find 定位工程根目录时会被垃圾目录干扰。 - 工程根目录判定:模块目录(entry 等)也有
hvigorfile.ts,但只有根工程的build-profile.json5含"modules"数组;候选多个时取层级最浅的。 - 大小写重复模块自动修复:Mac(大小写不敏感文件系统)导出的工程可能 modules 里有
Entry/entry两个条目,Linux 上 hvigor 直接报Duplicated modules。脚本用括号深度
跟踪行区间,只删重复条目、不动其他内容,构建日志会打FIXED: removed duplicated module entry xxx。 - hvigor 配置补齐:DevEco 导出的工程可能缺
hvigor/hvigor-config.json5,按 modelVersion
5.0.0 生成默认值。 - assembleHar 软失败 / assembleApp 硬失败:普通 app 工程没有 har 类型模块,assembleHar
失败属正常;assembleApp(--mode project)才是把所有 hap 合成.app的工程级任务,失败即失败。 - **产物收集只认
*/build/***:-not -path "*/cache/*"排除 ohpm 缓存里的第三方 har。
7. 使用方式
7.1 页面手动构建
- 打开 http://192.168.1.244:8080/job/%E9%B8%BF%E8%92%99%E6%89%93%E5%8C%85/ → Build with Parameters
- 选择
AppBuildType,点击文件框上传harmonyProject.zip - 点 Build,构建页看控制台输出
- 构建成功后在构建详情页 Build Artifacts 下载
output/*.app/*.har/*.hap
7.2 API 触发(可选)
带 File Parameter 的 API 触发需要 crumb + multipart 表单:
# 1) 取 crumb(需会话 cookie,否则 403)
CRUMB=$(curl -s -c /tmp/jk.cookie -u <user>:<apiToken> \
"http://192.168.1.244:8080/crumbIssuer/api/json" | grep -oE '"crumb":"[^"]+"' | cut -d'"' -f4)
# 2) 触发构建(zip 走 -F 上传,AppBuildType 走 -F 表单字段)
curl -b /tmp/jk.cookie -u <user>:<apiToken> \
-H "Jenkins-Crumb: ${CRUMB}" \
-F "harmonyProject.zip=@./my-harmony-project.zip" \
-F "AppBuildType=release" \
"http://192.168.1.244:8080/job/鸿蒙打包/buildWithParameters"
注意:API Token 在 Jenkins → 用户 → Security 里生成,不能用登录密码(新版 Jenkins 默认禁用)。
8. 已知坑与排查
| 症状 | 根因 | 处理 |
|---|---|---|
ERROR: 未找到上传的 harmonyProject.zip | 上传文件落在 fileParameters 目录且超 60 分钟,或确实没传 | 重新构建并确认选择文件后再点 Build;必要时放宽 mmin |
Duplicated modules | Mac 导出工程 modules 大小写重复 | 脚本 3.5 自动修复;若仍报错检查手工改动过的 build-profile.json5 |
Duplicated files found in module entry(uni-app x 工程才有) | release product 缺 nativeLib.filter.enableOverride | python3 /opt/harmonyos/patch_release_nativelib.py <工程>/harmony-configs/build-profile.json5 release |
| ohpm install 拉包失败 | 内网无法访问 ohpm 官方源 | ohpm config set registry 指向内网/镜像源 |
| hvigorw 报 JDK 版本不兼容 | JAVA_HOME 指向错误 | 确认 /etc/profile.d/harmonyos.sh 里 JAVA_HOME=JDK21 |
| 构建成功但 Artifacts 为空 | 工程产物在非 build 目录或后缀不同 | 看 === 产物列表 === 与 Found: 行,调整 find 条件 |
| 上传大 zip 后 403 / 连接重置 | 反代或 Tomcat 上传限制 | 该任务是 Jenkins 直装 8080 无反代;如经 nginx 需调 client_max_body_size |
更多推荐
所有评论(0)