01 - 发布基础设施与配置详解
维度:静态盘点发布体系的文件结构、Gradle 配置、签名、坐标、调用链 关注点:技术栈、各 gradle 文件职责、任务依赖、死代码识别
一、技术栈
| 项 | 内容 | 来源 |
|---|---|---|
| 发布插件 | com.jfrog.artifactory 4.24.0 | build.gradle:56 classpath org.jfrog.buildinfo:build-info-extractor-gradle:4.24.0 |
| 制品插件 | maven-publish(Gradle 内置) | build.gradle:141、app/build.gradle:173 |
| 目标仓库 | 小米内部 Artifactory | https://pkgs.d.xiaomi.net/artifactory |
| 发布物 | release APK(非 AAR/源码) | app/build/outputs/apk/{flavor}/release/*.apk |
| 仓库类型 | releases / snapshots,Ivy 布局 mavenCompatible=true | build.gradle:147、app/upload.gradle:14 |
这是「把 APK 当制品传到 Artifactory 供 CI/OTA 拉取」,不是发布依赖库供别人 import。
二、文件清单
| 文件 | 角色 | 生效? |
|---|---|---|
project_publish_xcd_app.sh / dcd / global | 发布入口脚本(3 个 flavor 各一) | ✅ |
build.gradle | 根级 artifactory 配置 + loadBuildConfig + updateApkAssemble* 任务 | ✅ |
app/build.gradle | publishing(三 flavor publication)+ artifactoryPublish(按 flavor 选 publication) | ✅ |
settings.gradle | 拉取依赖用的 Artifactory 仓库列表 | ✅(仅拉取) |
app/publish.gradle | 旧的上传配置(app publication, latest-SNAPSHOT) | ❌ 死代码 |
app/upload.gradle | 旧的 artifactory 配置 + loadArtifactoryAccountInfo(读 local.properties) | ❌ 死代码 |
project.properties | 版本号(versionName/versionCode)+ 可放账号 | ✅(被 git 跟踪) |
local.properties | sdk.dir + autoBindDisable + 可放账号 | ✅(在 .gitignore) |
gradle.properties | JVM/AndroidX 等全局设置 | ✅ |
platform.keystore | release 签名密钥 | ✅ |
三、根 build.gradle 关键配置
3.1 根级 artifactory(build.gradle:139-162)
apply plugin: "com.jfrog.artifactory"
apply plugin: "maven-publish"
artifactory {
clientConfig.setIncludeEnvVars(true)
contextUrl = 'https://pkgs.d.xiaomi.net/artifactory'
publish {
repository {
repoKey = 'releases' // ← 发布到 releases 仓库
username = rootProject.ext.find("artifactory_user") // ← 账号来自 loadBuildConfig
password = rootProject.ext.find("artifactory_password")
ivy {
ivyLayout = '[organization]/[module]/ivy-[revision].xml'
artifactLayout = '[organization]/[module]/[revision]/[module]-[revision](-[classifier]).[ext]'
mavenCompatible = true // ← Maven 兼容路径
}
}
defaults {
publishBuildInfo = false
publishArtifacts = true
publishPom = true
}
}
}注意:根级 repoKey 是 releases。app 模块没有覆盖 repository,所以实际发布走根级配置 → releases 仓库。
3.2 updateApk 任务(build.gradle:235-242)
task updateApkAssembleXcdRelease(dependsOn: ['app:artifactoryPublish']) {}
task updateApkAssembleDcdRelease(dependsOn: ['app:artifactoryPublish']) {}
task updateApkAssembleGlobalRelease(dependsOn: ['app:artifactoryPublish']) {}三个根 task,都依赖 app:artifactoryPublish。区别由 app/build.gradle 里 getCurrentFlavor() 根据启动命令里的 assemble task 名识别(见 §4.2)。
3.3 拉取依赖仓库(settings.gradle:20-49)
gradle-plugins-remote / google-mvn / jcenter / aliyun-maven-central / aliyun-maven-google
maven-{snapshot,release,remote}-virtual ← 通用虚拟仓库
micockpit-{snapshot,release} ← 座舱专用
四、app/build.gradle 发布配置
4.1 三 flavor publication(app/build.gradle:172-197)
apply plugin: "com.jfrog.artifactory"
apply plugin: "maven-publish"
//artifactoryPublish.skip = true // ← :174 历史遗留,已注释
publishing {
publications {
dcdApp (MavenPublication) { groupId='com.mi.car'; artifactId='MiCarSettings'; version=versionName+"-dcd"; artifact("$buildDir/.../app-dcd-release.apk") }
xcdApp (MavenPublication) { ... version=versionName+"-xcd" ... }
globalApp (MavenPublication) { ... version=versionName+"-global" ... }
}
}坐标:com.mi.car:MiCarSettings:{versionName}-{flavor},versionName 来自 project.properties。
4.2 按 flavor 选择 publication(app/build.gradle:199-219)
artifactoryPublish {
def getCurrentFlavor = {
def taskNames = gradle.startParameter.taskNames
for (taskName in taskNames) {
if (taskName.toLowerCase().contains("assemble")) return taskName
}
return ""
}
def currentFlavor = getCurrentFlavor()
if (currentFlavor.contains("Dcd")) { dependsOn('assembleDcdRelease'); publications(publishing.publications.dcdApp) }
else if (currentFlavor.contains("Xcd")) { dependsOn('assembleXcdRelease'); publications(publishing.publications.xcdApp) }
else if (currentFlavor.contains("Global")) { dependsOn('assembleGlobalRelease'); publications(publishing.publications.globalApp) }
}关键:getCurrentFlavor 扫描命令行 task 名里的 assemble... 字样来判断 flavor。所以必须通过含 assemble{Flavor}Release 的 task 链触发(updateApkAssembleXcdRelease → app:artifactoryPublish → dependsOn assembleXcdRelease),直接跑 app:artifactoryPublish 不带 assemble 上下文可能选不到 publication。
五、签名配置(app/build.gradle:26-36, 94)
signingConfigs {
release {
storeFile file('../platform.keystore') // ← 根目录 platform.keystore
keyAlias 'platform_key'
// storePassword / keyPassword 在块内(省略)
}
debug { storeFile file('../platform.keystore'); keyAlias 'platform_key'; ... }
}
android {
buildTypes {
release { signingConfig signingConfigs.release } // ← :94 release 用平台签名
debug { signingConfig signingConfigs.debug }
}
}特点:release/debug 共用 platform.keystore(系统应用平台签名),platform.keystore 在仓库根目录。
六、完整调用链
sequenceDiagram participant Dev as 开发者 participant Sh as project_publish_xcd_app.sh participant Gradle as Gradle participant AppTask as app:artifactoryPublish participant Assemble as assembleXcdRelease participant Deploy as artifactoryDeploy participant Art as Artifactory Dev->>Sh: ./project_publish_xcd_app.sh Sh->>Gradle: ./gradlew clean Gradle-->>Sh: cleaned Sh->>Gradle: ./gradlew updateApkAssembleXcdRelease Gradle->>AppTask: updateApkAssembleXcdRelease(dependsOn) AppTask->>AppTask: getCurrentFlavor() = Xcd AppTask->>Assemble: dependsOn assembleXcdRelease Note over Assemble: 全量编译十几个模块<br/>+ 签名 + 打包 Assemble-->>AppTask: app-xcd-release.apk (371MB) AppTask->>AppTask: generatePomFileForXcdAppPublication AppTask->>Deploy: artifactoryDeploy Deploy->>Art: PUT /releases/com/mi/car/MiCarSettings/版本-xcd/*.apk Art-->>Deploy: 201 Created (需认证) Deploy-->>Gradle: success Gradle-->>Sh: BUILD SUCCESSFUL
七、死代码标注(易误导)
app/publish.gradle(:1)
apply from: "upload.gradle" //for upload maven ← publish.gradle 自己没人 applygrep 全仓 apply from,没有任何文件 apply publish.gradle。即 publish.gradle + 它引用的 upload.gradle 都不生效。
app/upload.gradle(:31-48)误导点
def loadArtifactoryAccountInfo() {
Properties properties = new Properties();
File file = rootProject.file("local.properties") // ← 声称从 local.properties 读账号
...
properties.get("artifactory_user")
}这就是最大的误导:死代码声称账号从 local.properties 读,开发者照此把账号配进 local.properties,但生效的 loadBuildConfig(build.gradle)读的是 project.properties/环境变量 → 401。
建议:删除
app/publish.gradle和app/upload.gradle(确认无引用后),消除误导。详见 03-发布实战与排查手册 §死代码清理。
八、关键源码行号索引
| 内容 | 文件:行号 |
|---|---|
| jfrog 插件 classpath | build.gradle:56 |
| 根 artifactory 配置(releases) | build.gradle:139-162 |
| loadBuildConfig(账号读取) | build.gradle:164-222 |
| updateApk 任务 | build.gradle:235-242 |
| 拉取仓库列表 | settings.gradle:20-49 |
| app apply artifactory | app/build.gradle:172-173 |
| skip 历史遗留(已注释) | app/build.gradle:174 |
| 三 flavor publication | app/build.gradle:175-197 |
| flavor 选择逻辑 | app/build.gradle:199-219 |
| 签名 signingConfigs | app/build.gradle:26-36 |
| release 签名绑定 | app/build.gradle:94 |
| 死代码 publish.gradle | app/publish.gradle:1 |
| 死代码 upload.gradle | app/upload.gradle:1-48 |