01 - 发布基础设施与配置详解

维度:静态盘点发布体系的文件结构、Gradle 配置、签名、坐标、调用链 关注点:技术栈、各 gradle 文件职责、任务依赖、死代码识别


一、技术栈

内容来源
发布插件com.jfrog.artifactory 4.24.0build.gradle:56 classpath org.jfrog.buildinfo:build-info-extractor-gradle:4.24.0
制品插件maven-publish(Gradle 内置)build.gradle:141app/build.gradle:173
目标仓库小米内部 Artifactoryhttps://pkgs.d.xiaomi.net/artifactory
发布物release APK(非 AAR/源码)app/build/outputs/apk/{flavor}/release/*.apk
仓库类型releases / snapshots,Ivy 布局 mavenCompatible=truebuild.gradle:147app/upload.gradle:14

这是「把 APK 当制品传到 Artifactory 供 CI/OTA 拉取」,不是发布依赖库供别人 import。


二、文件清单

文件角色生效?
project_publish_xcd_app.sh / dcd / global发布入口脚本(3 个 flavor 各一)
build.gradle根级 artifactory 配置 + loadBuildConfig + updateApkAssemble* 任务
app/build.gradlepublishing(三 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.propertiessdk.dir + autoBindDisable + 可放账号✅(在 .gitignore)
gradle.propertiesJVM/AndroidX 等全局设置
platform.keystorerelease 签名密钥

三、根 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.gradlegetCurrentFlavor() 根据启动命令里的 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 链触发(updateApkAssembleXcdReleaseapp:artifactoryPublishdependsOn 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 自己没人 apply

grep 全仓 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.gradleapp/upload.gradle(确认无引用后),消除误导。详见 03-发布实战与排查手册 §死代码清理。


八、关键源码行号索引

内容文件:行号
jfrog 插件 classpathbuild.gradle:56
根 artifactory 配置(releases)build.gradle:139-162
loadBuildConfig(账号读取)build.gradle:164-222
updateApk 任务build.gradle:235-242
拉取仓库列表settings.gradle:20-49
app apply artifactoryapp/build.gradle:172-173
skip 历史遗留(已注释)app/build.gradle:174
三 flavor publicationapp/build.gradle:175-197
flavor 选择逻辑app/build.gradle:199-219
签名 signingConfigsapp/build.gradle:26-36
release 签名绑定app/build.gradle:94
死代码 publish.gradleapp/publish.gradle:1
死代码 upload.gradleapp/upload.gradle:1-48