ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

HarmonyOS 7 / API 26 oh-package 依赖锁定实战:三方库版本漂移、构建失败和 CI 校验一次验清

HarmonyOS 7 / API 26 oh-package 依赖锁定实战:三方库版本漂移、构建失败和 CI 校验一次验清

HarmonyOS 工程里,依赖问题经常不是代码写错,而是版本漂移。本地刚装完能跑,换一台机器、换一次 CI、或者清掉缓存后突然构建失败。这个时候如果只改业务代码,很可能越改越乱。

这篇按 HarmonyOS 7 / API 26 工程来讲 oh-package 依赖锁定。重点是三件事:依赖版本怎么写,lock 文件怎么守住,CI 里怎么提前发现版本漂移。

版本环境先写清楚

项目示例口径说明
HarmonyOS 目标HarmonyOS 7 / API 26当前文章讨论的新工程目标
工程依赖oh-package.json5记录直接依赖
锁定文件oh-package-lock.json5记录解析后的确定版本
CI 检查安装、构建、依赖差异检查防止本地和流水线不一致

这几项不说清楚,后面讨论依赖问题很容易变成“我这里可以”。工程协作里,“我这里可以”不是结论,CI 可复现才是结论。

直接依赖和锁定依赖不是一回事

oh-package.json5 里写的是工程想要什么,lock 文件里记录的是最终装到了什么。两者都重要。

~~~json

{

"dependencies": {

"@ohos/example-ui": "1.2.3",

"@ohos/example-utils": "^2.0.0"

},

"devDependencies": {

"@ohos/linter-rules": "0.8.0"

}

}

~~~

如果依赖写成 ^2.0.0,后面可能解析到 2.0.1、2.1.0。功能看似没变,但构建输出、类型定义、运行行为都可能变化。多人协作和 CI 里,我更倾向把核心依赖锁得更具体。

案例一:本地能跑,CI 构建失败

问题场景很典型:开发机已经有缓存,所以构建通过;CI 是干净环境,重新安装依赖后失败。

先写一个依赖检查脚本,把直接依赖和 lock 文件都纳入检查。

~~~ts

type DependencyMap = Record<string, string>

type DependencyCheckResult = {

name: string

declared: string

locked?: string

ok: boolean

reason?: string

}

function checkLockedDependencies(declared: DependencyMap, locked: DependencyMap): DependencyCheckResult[] {

return Object.keys(declared).map(name => {

const declaredVersion = declared[name]

const lockedVersion = locked[name]

if (!lockedVersion) {

return { name, declared: declaredVersion, ok: false, reason: 'missing in lock file' }

}

if (declaredVersion !== lockedVersion && !declaredVersion.startsWith('^')) {

return { name, declared: declaredVersion, locked: lockedVersion, ok: false, reason: 'version mismatch' }

}

return { name, declared: declaredVersion, locked: lockedVersion, ok: true }

})

}

~~~

这段代码不依赖具体包管理器输出,先把检查逻辑跑清楚。真正接入 CI 时,可以从 oh-package.json5 和 lock 文件里读取数据。

CI 里不要只跑构建

只跑 build 太晚了。依赖漂移应该在构建前就暴露。

~~~ts

function assertDependencyResult(results: DependencyCheckResult[]): void {

const failed = results.filter(item => !item.ok)

if (failed.length === 0) {

console.info('[dependency-check] passed')

return

}

for (const item of failed) {

console.error(

'[dependency-check] failed',

item.name,

'declared=' + item.declared,

'locked=' + (item.locked ?? 'none'),

item.reason ?? ''

)

}

throw new Error('dependency check failed')

}

~~~

CI 的目标不是把错误藏起来,而是让错误尽早、尽准地失败。依赖不一致就应该在依赖检查阶段失败,不要等到编译阶段出现一堆无关报错。

案例二:三方库升级后类型变化

第二类问题是依赖升级后 API 没报明显错误,但类型定义变了。比如之前返回 string,升级后可能返回 string | undefined。

~~~ts

type OldApiResult = {

title: string

}

type NewApiResult = {

title?: string

}

function normalizeTitle(result: NewApiResult): string {

return result.title?.trim() || '未命名内容'

}

~~~

依赖升级后,不能只看构建是否通过,还要看关键调用是否有兼容层。对业务入口多的项目,我会把三方库调用包一层 adapter。

~~~ts

class ThirdPartyAdapter {

parseTitle(result: NewApiResult): string {

return normalizeTitle(result)

}

assertRuntimeCompatible(version: string): void {

if (!version.startsWith('2.')) {

throw new Error('unsupported dependency version: ' + version)

}

}

}

~~~

这样后面库再升级,影响点集中在 adapter,不会散落到页面里。

本地验证脚本

先用假数据验证依赖检查能不能挡住问题。

~~~ts

function verifyDependencyCheck(): void {

const declared = {

'@ohos/example-ui': '1.2.3',

'@ohos/example-utils': '^2.0.0'

}

const locked = {

'@ohos/example-ui': '1.2.4',

'@ohos/example-utils': '2.1.0'

}

const result = checkLockedDependencies(declared, locked)

console.info('[verify-deps]', JSON.stringify(result))

}

~~~

预期结果是 example-ui 被拦住,因为声明 1.2.3,实际锁定 1.2.4;example-utils 因为声明了 ^2.0.0,需要看团队规则是否允许浮动。如果团队追求完全可复现,也可以把 ^ 依赖一起拦掉。

我会采用的规则

规则原因
核心运行依赖写精确版本减少线上行为变化
lock 文件必须提交保证团队和 CI 一致
CI 构建前先查依赖让版本问题提前失败
三方库调用包 adapter升级影响集中处理
升级依赖要有回归清单避免只看能不能编译

回归清单

检查项通过标准
清缓存安装干净环境能安装成功
依赖锁定lock 文件和声明依赖一致
CI 构建构建失败能定位到依赖阶段
类型变化adapter 层能处理 undefined 等变化
关键页面升级后核心页面可打开、可返回、可保存

小结

HarmonyOS 7 / API 26 工程里,oh-package 依赖管理不能只靠本地缓存。直接依赖、lock 文件、CI 检查和 adapter 兼容层要一起看。这样遇到“本地能跑、流水线失败”时,先查依赖锁定,而不是一上来怀疑业务代码。

返回列表