Build APK / build (push) Canceled after 7m12s
- .gitea/workflows/build.yml:打 tag 触发构建。代码取当前 ref,素材目录单独从 full-assets 分支叠加 (main 保持 ~8MB 不追踪 assets_hq/assets_small/assets_net,缺它们构建出来的包宠物是空白的)。 用 secret 注入的固定 keystore 签 release 包,构建后发布 APK + version.json 到 Gitea Release。 - app/build.gradle.kts:签名改为从环境变量读取(KEYSTORE_PATH / KEYSTORE_PASSWORD / KEY_ALIAS / KEY_PASSWORD),本地无 keystore 时回退 debug 签名;versionCode 支持 CI 注入,保证单调递增 (更新机制要求新包 versionCode 严格大于已装版本,否则系统拒绝覆盖安装)。 - UpdateChecker.kt:读 Gitea Release API 按 versionCode 判版本 → 下载并校验 SHA256 → 经自带 ApkProvider(content://)拉起系统安装器。项目零第三方依赖,用不了 androidx FileProvider, 故自带一个只读 ContentProvider(只暴露 cacheDir/update 下的 .apk,路径取 basename 防穿越)。 - MainActivity 设置页新增「关于 / 检查更新」入口,下载前先检查「安装未知来源应用」授权。 - AndroidManifest 新增 REQUEST_INSTALL_PACKAGES 与 ApkProvider 声明。 - .gitignore 增加 *.jks / *.keystore / keystore.properties,防止签名文件误入库。
87 lines
3.4 KiB
Kotlin
87 lines
3.4 KiB
Kotlin
plugins {
|
||
id("com.android.application")
|
||
id("org.jetbrains.kotlin.android")
|
||
}
|
||
|
||
android {
|
||
namespace = "com.dsh.petfloat"
|
||
compileSdk = 34
|
||
|
||
defaultConfig {
|
||
applicationId = "com.dsh.petfloat"
|
||
minSdk = 26
|
||
targetSdk = 34
|
||
// 版本号:CI 注入 VERSION_CODE(单调递增),本地缺省 1000。
|
||
// 更新机制要求新包 versionCode **严格大于**已装版本,否则系统会拒绝覆盖安装。
|
||
val injectedCode = System.getenv("VERSION_CODE")?.toIntOrNull()
|
||
versionCode = injectedCode ?: 1000
|
||
versionName = System.getenv("VERSION_NAME") ?: "1.0.$versionCode"
|
||
}
|
||
|
||
// 发布签名:CI 通过 secret 注入 keystore(见 .gitea/workflows/build.yml)。
|
||
// 本地没有 keystore 时不创建 release signingConfig,release 变体退回 debug 签名——
|
||
// 仅用于本地验证,**不可用于分发**(debug 签名每台机器不同,无法覆盖安装)。
|
||
val keystorePath = System.getenv("KEYSTORE_PATH")
|
||
val hasReleaseKeystore = !keystorePath.isNullOrBlank() && file(keystorePath).exists()
|
||
|
||
signingConfigs {
|
||
if (hasReleaseKeystore) {
|
||
create("release") {
|
||
storeFile = file(keystorePath!!)
|
||
storePassword = System.getenv("KEYSTORE_PASSWORD")
|
||
keyAlias = System.getenv("KEY_ALIAS") ?: "petfloat"
|
||
keyPassword = System.getenv("KEY_PASSWORD") ?: System.getenv("KEYSTORE_PASSWORD")
|
||
}
|
||
}
|
||
}
|
||
|
||
buildTypes {
|
||
release {
|
||
isMinifyEnabled = false
|
||
if (hasReleaseKeystore) {
|
||
signingConfig = signingConfigs.getByName("release")
|
||
}
|
||
}
|
||
}
|
||
|
||
// 生成 BuildConfig:QA-004 的 debug 故障注入必须靠 `BuildConfig.DEBUG` 做编译期闸门
|
||
//(release 里它是常量 false,注入开关整段不可达)。AGP 8 默认不生成,这里显式打开。
|
||
buildFeatures {
|
||
buildConfig = true
|
||
}
|
||
|
||
compileOptions {
|
||
sourceCompatibility = JavaVersion.VERSION_17
|
||
targetCompatibility = JavaVersion.VERSION_17
|
||
}
|
||
|
||
kotlinOptions {
|
||
jvmTarget = "17"
|
||
}
|
||
|
||
// 单测(纯 JVM,不需要设备):判定内核 ReconcileEngine 不碰 Context,可直接跑。
|
||
testOptions {
|
||
unitTests.isReturnDefaultValues = true // android.jar stub 方法返回默认值而不是抛异常
|
||
unitTests.all { it.testLogging { showStandardStreams = true } } // 让 benchmark 数字打到控制台
|
||
}
|
||
|
||
// 素材两套:默认小体积(pilot 128MB 上限内可装),-Phq 用 640×360 原画质
|
||
val hqBuild = project.hasProperty("hq")
|
||
sourceSets["main"].assets.setSrcDirs(
|
||
listOf(
|
||
file("src/main/assets_common"),
|
||
file("src/main/assets_net"),
|
||
file(if (hqBuild) "src/main/assets_hq" else "src/main/assets_small")
|
||
)
|
||
)
|
||
}
|
||
|
||
dependencies {
|
||
// 纯平台 API(android.* / java.* / 平台自带 org.json),无需第三方依赖。
|
||
// 以下**仅测试依赖,不进 APK**:
|
||
testImplementation("junit:junit:4.13.2")
|
||
// JVM 版 org.json:android.jar 里的 org.json 只是 stub,JVM 单测调用会抛异常;
|
||
// 加它才能在单测里实测「600 条全量 JSON 解析」的真实耗时(Android 平台的 org.json
|
||
// 与此实现同源,属同一量级,作为代理指标使用,见交付说明)。
|
||
testImplementation("org.json:json:20240303")
|
||
} |