ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

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 文件都纳入检查。~~~tstype DependencyMap Recordstring, stringtype DependencyCheckResult {name: stringdeclared: stringlocked?: stringok: booleanreason?: 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 太晚了。依赖漂移应该在构建前就暴露。~~~tsfunction 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。~~~tstype OldApiResult {title: string}type NewApiResult {title?: string}function normalizeTitle(result: NewApiResult): string {return result.title?.trim() || 未命名内容}~~~依赖升级后不能只看构建是否通过还要看关键调用是否有兼容层。对业务入口多的项目我会把三方库调用包一层 adapter。~~~tsclass ThirdPartyAdapter {parseTitle(result: NewApiResult): string {return normalizeTitle(result)}assertRuntimeCompatible(version: string): void {if (!version.startsWith(2.)) {throw new Error(unsupported dependency version: version)}}}~~~这样后面库再升级影响点集中在 adapter不会散落到页面里。本地验证脚本先用假数据验证依赖检查能不能挡住问题。~~~tsfunction 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.4example-utils 因为声明了 ^2.0.0需要看团队规则是否允许浮动。如果团队追求完全可复现也可以把 ^ 依赖一起拦掉。我会采用的规则规则原因核心运行依赖写精确版本减少线上行为变化lock 文件必须提交保证团队和 CI 一致CI 构建前先查依赖让版本问题提前失败三方库调用包 adapter升级影响集中处理升级依赖要有回归清单避免只看能不能编译回归清单检查项通过标准清缓存安装干净环境能安装成功依赖锁定lock 文件和声明依赖一致CI 构建构建失败能定位到依赖阶段类型变化adapter 层能处理 undefined 等变化关键页面升级后核心页面可打开、可返回、可保存小结HarmonyOS 7 / API 26 工程里oh-package 依赖管理不能只靠本地缓存。直接依赖、lock 文件、CI 检查和 adapter 兼容层要一起看。这样遇到“本地能跑、流水线失败”时先查依赖锁定而不是一上来怀疑业务代码。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进