指南

Magic Deploy 配置 — Google Play

LingCode 把 Android 应用上传到 Google Play 所需的 Service Account JSON、软件包名、密钥库与轨道——都在哪里获取。

你在填什么

在 LingCode 中点击 Magic Deploy 并选择 Google Play 时,最多会看到七个字段。前三个为必填;若 build.gradle 已配置正式发布签名,则后面四个密钥库字段可以不填。

字段含义去哪获取
Service account JSON创建 Service Account 时 Google 生成的私钥文件。Play Console → 设置 → API 访问 → 创建 Service Account → 下载 JSON
软件包名应用的 applicationId,例如 com.example.app必须与 app/build.gradle 中的 applicationId 完全一致。
轨道(Track)构建发布到哪条轨道:internalalphabetaproduction在 LingCode 内选择。默认为 internal
Keystore 文件用于签署正式发布 AAB 的 .jks.keystorekeytool 生成一次(或项目中已有)。
Keystore 密码密钥库文件本身的密码。运行 keytool -genkeypair 时设置。
Key alias密钥库内该条密钥的名称,例如 release运行 keytool -genkeypair 时设置。
Key 密码该条密钥的密码。运行 keytool -genkeypair 时设置(常与 Keystore 密码相同)。

应用内两处快捷方式能省很多时间。 Magic Deploy 会把 Service Account JSON 做一次 预检——若文件损坏或缺少 client_email / private_key,立刻可见,而不必等到 OAuth 阶段才莫名其妙失败。字段旁的 Auto-bump versionCode 会在每次构建前改写 app/build.gradle 里的 versionCode,消除最常见的第二次部署失败(「Version code X has already been used」)。默认关闭——首次部署时勾选即可。

为什么 Google 用 Service Account 而不是 API Key? 普通 API Key 是单一密钥,持有者能做账号允许的一切——泄露只能轮换并祈祷。Google 的 Service Account 是 Google Cloud IAM 里的真实身份:你可授予窄权限(例如「仅向该应用的内测轨道上传构建」),并独立吊销或轮换密钥。LingCode 与 Google 之间运行的 JWTOAuth 2.0 流程会把 JWT 换成约一小时有效的访问令牌——即便令牌泄露也会很快过期。术语不熟?见 术语表

开始之前——一次性 Play Console 配置

在 Service Account 能上传任何东西之前,Google Play Console 里必须先有三样东西。省略任一项都会在后面遇到令人困惑的错误。

  1. Google Play 开发者账号。play.google.com/console/signup 注册。需一次性支付 25 美元。请使用你打算长期持有该应用的 Google 账号——日后转移所有权可行但很麻烦。
  2. Play Console 中的应用记录。 在 Play Console 点击 创建应用,填写名称、默认语言、应用或游戏、免费或付费等。此处填写的 软件包名 必须与 app/build.gradle 中的 applicationId 完全一致,且之后不可更改
  3. 至少手动上传过一次 AAB。 在人工上传过至少一个签名构建之前,Play 不允许仅凭 Service Account 上传。本地构建签名 AAB(./gradlew bundleRelease),拖入 Play Console → 测试 → 内部测试 → 创建版本,一次即可。不必正式发布——只要上传即解锁 API 路径。

跳过首次手动上传是「一切正常但 Console 里什么都没有」的头号原因。 API 可能照常接受上传并提交编辑,你在 Play Console 仍看不到构建。请先手动上传一次。

Android 工程必须具备这些设置

Play Console 侧配置正确,但若 ./gradlew bundleRelease 产不出 LingCode 期望的 AAB,仍然无法部署。首次部署前请逐项核对。

app/build.gradle 中的 applicationId

这相当于 Android 的 Bundle Identifier,必须与你在 Play Console 输入的软件包名完全一致。应用上架后,该值不可更改——Google 会把不同的 applicationId 视为完全不同的应用。

android {
    defaultConfig {
        applicationId "com.example.app"
        // …
    }
}

versionCodeversionName

二者都在 defaultConfig 中。versionCode 是 Play 用来排序上传的整数;versionName 是面向用户的版本号字符串。

defaultConfig {
    versionCode 42        // must be greater than every previous upload
    versionName "1.2.3"   // shown to users in the Play Store
}

每次上传都必须增大 versionCode 重复使用同一 versionCode 是第二次部署 silent 失败的最常见原因——Play 在服务端拒绝并报「Version code X has already been used」。

minSdkVersioncompileSdkVersion

Play Console 对 targetSdkVersion 有逐年提高的底线(目前通常为 API 33+)。若项目的 targetSdkVersion 低于当前底线,上传可能被接受但发布无法铺开。当前最低要求见 developer.android.com/google/play/requirements/target-sdk

bundleRelease Gradle 任务

LingCode 会运行 ./gradlew bundleRelease,并期望产物位于 app/build/outputs/bundle/release/app-release.aab。标准 Android Studio 工程通常开箱即用。若你重命名了 app 模块、使用了 product flavor,或多模块工程中可上传的 AAB 在别处产出,需要自行调整:

Gradle Wrapper(gradlew

LingCode 优先使用 ./gradlew,而非系统的 gradle。若工程没有 wrapper,请添加:

gradle wrapper --gradle-version 8.5

(版本号选你项目已知可构建的版本。)使用 wrapper 可避免 PATH 上任意版本的 Gradle,并与 CI / 同事环境一致。

步骤 1 — 创建 Service Account 并下载 JSON 密钥

与 Google Play 通信时,LingCode 会以 Service Account 身份认证。它在 Play Console 内创建——不是在单独的 Google Cloud Console 里从零开始,尽管 Service Account 本身是 Cloud 概念。

  1. 登录 play.google.com/console
  2. 左侧边栏打开 设置 → API 访问
  3. Service accounts 下点击 Create new service account。Play Console 会弹出对话框链接到 Google Cloud;按指引创建 Service Account(名称任意——「LingCode Magic Deploy」 即可),回到 Play Console 标签页点击 Done
  4. 回到 API 访问 页面,在列表中找到新建的 Service Account,点击 Manage Play Console permissions(或 Grant access)。
  5. App permissions 标签点击 Add app,选择此前注册的应用。
  6. Account permissions 标签至少勾选 Release apps to testing tracksRelease apps to production。保存。
  7. 回到 Google Cloud Console,打开该 Service Account 的 Keys 标签,Add key → Create new key → JSON,下载文件并妥善备份。

在 LingCode 中点击 Service account JSON 旁的 Browse…,选择下载的文件。

JSON 密钥文件相当于密码。 不要提交到 Git,不要粘贴到 Slack。若泄露,在 Cloud Console 删除该密钥——Service Account 仍可继续用,只需换新密钥。

步骤 2 — 选择发布轨道

轨道(Track) 下拉框决定 Play 把上传放到哪里。LingCode 默认为 internal——在试用 Magic Deploy 本身时最快、最安全。

之后可随时在 Play Console 内把构建从 internal 提升到更高轨道,无需重新上传。

步骤 3 — 配置正式发布签名

Google Play 只接受已签名的 AAB。有两种常见做法——按你现有流程选择。

方案 A — 在 build.gradle 内签名(若已有密钥库推荐)

app/build.gradle 中添加 signingConfigs.release 块:

android {
    signingConfigs {
        release {
            storeFile file("/absolute/path/to/release.keystore")
            storePassword "…"
            keyAlias "release"
            keyPassword "…"
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
            // …
        }
    }
}

若采用此方案,请将 LingCode 中四个密钥库字段留空。LingCode 只运行 ./gradlew bundleRelease,由 Gradle 从构建文件读取签名配置。

方案 B — 由 LingCode 在构建时注入密钥库

若不想把密码硬编码进 build.gradle,让构建文件从 Gradle 属性读取:

android {
    signingConfigs {
        release {
            storeFile file(project.findProperty("RELEASE_STORE_FILE") ?: "release.keystore")
            storePassword project.findProperty("RELEASE_STORE_PASSWORD") ?: ""
            keyAlias project.findProperty("RELEASE_KEY_ALIAS") ?: ""
            keyPassword project.findProperty("RELEASE_KEY_PASSWORD") ?: ""
        }
    }
    buildTypes {
        release { signingConfig signingConfigs.release }
    }
}

然后在 LingCode 中填写全部四个密钥库字段。构建时 LingCode 会以 -PRELEASE_STORE_FILE=…-PRELEASE_STORE_PASSWORD=…-PRELEASE_KEY_ALIAS=…-PRELEASE_KEY_PASSWORD=… 传给 Gradle。除本地 gradle 进程参数外,这些值不会离开你的 Mac。

还没有密钥库?

用 JDK 自带的 keytool 生成:

keytool -genkeypair -v \
  -keystore release.keystore \
  -alias release \
  -keyalg RSA -keysize 2048 \
  -validity 10000

把文件保存在有备份的位置。若丢失此密钥库,你将无法再向 Play 更新该应用——Google 用签名证明新版本确实来自你。请至少在两个地方备份。

步骤 4 — 点击 Deploy

Service Account JSON、软件包名与签名就绪后,Deploy to Google Play 按钮会变为可用。随后 LingCode 会:

成功后约一两分钟内(Google 处理时间,非 LingCode)构建会出现在 Play Console → 测试 → 你的轨道 → 版本。随后可铺开、提升到其他轨道或提交审核。

阅读进度日志

Magic Deploy 运行时,右侧面板会流式输出 gradlew 的实时日志以及 LingCode 对 Google 服务器的每次 HTTP 调用。前缀为 [Google Play] 的行是 LingCode 标记;其余为工具原始输出。

一次成功运行大致如下:

[Google Play] Building signed AAB...
> Task :app:bundleRelease
BUILD SUCCESSFUL in 45s
[Google Play] Getting access token...
[Google Play] Creating edit...
[Google Play] Uploading AAB...
[Google Play] Committing edit...
[Google Play] Upload complete. Check Google Play Console for the new build.

失败时请滚动到日志底部。 LingCode 的短报错(如「Build failed. Check errors above.」)只是指针;真正原因通常在原始输出的最后二十行——往往是 FAILURE: 开头的 Gradle 行,或 HTTP 状态码(Google API)。

分阶段排错

失败按阶段划分较清晰,各自对策不同。

构建阶段(gradlew bundleRelease

日志含原因处理
No Android project found LingCode 在打开文件夹根目录未找到 build.gradlebuild.gradle.kts 打开包含 gradlew 的文件夹,而非其父目录。若在 monorepo 中,可临时直接打开 Android 子目录。
Permission denied: ./gradlew Wrapper 脚本不可执行。 在项目根目录执行 chmod +x gradlew
Could not find method signingConfig() Gradle 语法混用——把 Groovy 与 Kotlin DSL 片段混在了一起。 按文件扩展名使用匹配语法:build.gradle 为 Groovy,build.gradle.kts 为 Kotlin。
Keystore file '…' not found Magic Deploy 中的密钥库路径,或 RELEASE_STORE_FILE 指向的路径,在磁盘上解析失败。 使用指向 .jks.keystore绝对路径。相对路径相对 Gradle 模块目录解析,容易踩坑。
Failed to read key <alias> from store: Keystore was tampered with, or password was incorrect Keystore 密码或 Key 密码错误。 在终端运行 keytool -list -keystore release.keystore,按提示输入 Keystore 密码以验证。
No key with alias "…" found 密钥库中不存在该 alias。 keytool -list -keystore release.keystore 会列出全部 alias,将正确名称填入 Key alias 字段。
AAB not found at app/build/outputs/bundle/release/app-release.aab Gradle 构建成功但未在预期路径产出 AAB——常见原因是 app 模块不叫 app,或 product flavor 改变了输出文件名。 将模块改名为 app,或用 Gradle Copy 任务把 AAB 放到该路径。若按 flavor 构建,可暂时手动运行 ./gradlew bundle<Flavor>Release
Execution failed for task ':app:lintVitalRelease' Gradle 配置将某条 lint 标为错误,阻塞正式发布构建。 修复 lint,或临时加入 android.lintOptions.checkReleaseBuilds false 以解锁(再排期彻底修复)。
Java heap space / OutOfMemoryError Gradle Daemon 堆内存对你的项目过小。 gradle.properties 中加入 org.gradle.jvmargs=-Xmx4g

OAuth 阶段(JWT → access token)

日志含原因处理
OAuth failed: invalid_grant LingCode 用 Service Account 私钥签署的 JWT 被拒绝。常见为 JSON 损坏或来自错误项目。 在 Service Account 的 Keys 标签重新下载 JSON。勿手工编辑——部分编辑器会插入不可见字符。
OAuth failed: invalid_scope 该 Google Cloud 项目未启用 androidpublisher API。 Cloud Console → API 和服务 → 库,搜索并启用 Google Play Android Developer API
OAuth failed: 401 Unauthorized Service Account 私钥已被吊销或删除。 在 Keys 标签生成新 JSON 密钥,并在 Magic Deploy 中重新附加。

Create Edit 阶段

日志含原因处理
Create edit failed: 404 applicationNotFound Play Console 中不存在该 packageName——可能是拼写错误,或尚未创建应用。 先在 Play Console 创建应用记录(Create app),软件包名与 build.gradleapplicationId 完全一致
Create edit failed: 403 developerDoesNotOwnApplication Service Account 尚未与此应用在 Play Console 关联。 Play Console → 设置 → API 访问 → 你的 Service Account → Manage Play Console permissions → App permissions → Add app,添加该应用。
Create edit failed: 403 insufficientScopes Service Account 的 账号权限过窄。 同一页 Account permissions 标签——至少勾选「Release apps to testing tracks」。管理员角色亦可。

上传 AAB 阶段

日志含原因处理
Upload failed: 403 apkNotificationMessageKeyUpgradeVersionConflict / versionCodeTooLow AAB 中 versionCode 未大于 Play 上已有版本。 提高 app/build.gradle 中的 versionCode 并重试——或勾选面板中的 Auto-bump versionCode,由 LingCode 每次部署自动递增。
Upload failed: 400 apkUploadApkNotSigned AAB 未签名(或使用 debug 密钥签名)。 要么在 Magic Deploy 填齐四个密钥库字段,要么在 build.gradle 配置 signingConfigs.release。不要两套同时硬顶——可能冲突。
Upload failed: 400 apkSignedWithDifferentCertificate 签名证书与 Google 为该应用记录的证书不一致。 使用原密钥库签名;若已丢失,通过 Play Console → 设置 → App 签名 → 申请重置上传密钥(由 Google 人工审核)。
Upload failed: 413 Request Entity Too Large AAB 超过 150 MB 上限。 启用 R8/ProGuard 压缩,精简大资源,或使用 Play Asset Delivery。
Upload failed: network error / timed out 网络慢或不稳定;LingCode 对上传请求有 600 秒超时。 重试。若多次上传同一 AAB,Play 去重常会快速返回。

Commit Edit 阶段

日志含原因处理
Commit failed: 400 validationError 验证阶段 Play 拒绝了编辑——常见为应用记录缺少必填项(内容分级、隐私政策网址、目标受众等)。 在 Play Console 打开应用,完成 政策与计划应用内容 中带红点的部分。上传已成功;修复后可于 Console 手动提交编辑。
Commit failed: 409 editAlreadyCommitted 上一次尝试已提交该编辑;当前重试无事可做。 通常可忽略。查看 Play Console——构建多半已在。

上传成功后会发生什么

Magic Deploy 完成后,构建会出现在 Play Console → 你的应用 → 测试 → <你的轨道> → 版本。与 App Store Connect 不同,AAB 通常没有单独的「处理中」状态——约一两分钟内即可用。随后:

可在 Play Console 内将构建从低轨道提升到高轨道而无需重新上传——打开目标轨道的版本页,点击 Create new release → Add from library

速查:出问题该去哪改

现象去哪处理
Deploy 按钮一直灰缺少软件包名或 Service Account JSON。两者齐全后 LingCode 才会启用按钮。
"No Android project found"打开含 gradlew / build.gradle 的文件夹,而非父目录。
构建阶段失败在终端用相同 -P 参数运行 Magic Deploy 日志里打印的 ./gradlew bundleRelease。Gradle 报错与 LingCode 内一致,终端里更易调试。
AAB 已构建但找不到模块不叫 app,或 flavor 产出不同文件名。重命名或符号链接到 app/build/outputs/bundle/release/app-release.aab
OAuth / 401重新下载 Service Account JSON 并在 Magic Deploy 中附加。
403 developerDoesNotOwnApplicationPlay Console → 设置 → API 访问 → 你的 Service Account → App permissions → 添加该应用。
versionCodeTooLow增大 app/build.gradle 中的 versionCode,任意更大整数均可。
上传成功但 Console 无构建尚未完成首次手动上传。本地构建签名 AAB,拖入 Play Console → 内部测试 → 创建新版本,一次即可。
Commit 报 validationError打开 Play Console,处理 政策与计划 / 应用内容 红点,再手动提交编辑(文件已上传)。
仍卡住把日志中的完整 gradle 命令复制到终端执行;若是 API 错误,用 curl 带上 access token 调同一端点查看完整响应体。

常见错误(速查清单)

安全须知

Service Account JSON 与密钥库均为凭据。LingCode 仅在你触发的部署过程中把它们发送到 oauth2.googleapis.comandroidpublisher.googleapis.com(即 Google),否则文件只保留在你的 Mac。

若设备丢失或人员离职,请轮换 Service Account 密钥(Cloud Console → IAM → Service accounts → Keys,删除旧钥并生成新钥)。若怀疑密钥库备份泄露,请停止用它发新版——在未通过 Play App Signing 官方流程的情况下,Google 不允许随意更换应用签名密钥。

延伸阅读

本页概念的一手资料,来自 Google:

准备好交付了吗?

还要发 iOS 或 Mac?见 App Store Connect API Key 指南。或浏览 完整 Magic Deploy 功能

下载 LingCode Mac 版