docs: add Apple App Store + Google Play Store release guides
- APPLE/README.md: API key details, App Store Connect setup, TestFlight workflow, submission checklist, provisioning, screenshots/metadata requirements - GOOGLE/README.md: Play Console setup, service account config, signing keys, AAB upload, testing tracks, data safety, content rating, submission checklist
This commit is contained in:
parent
05594a334f
commit
a762c5b07f
283
docs/_MobileApps/APPLE/README.md
Normal file
283
docs/_MobileApps/APPLE/README.md
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
# Apple Developer — App Store & TestFlight Guide
|
||||||
|
|
||||||
|
## API Key
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
| --------- | -------------------------------------- |
|
||||||
|
| Key ID | `PPATU9GL73` |
|
||||||
|
| Issuer ID | `1dbc2980-1621-4fb9-940b-e28257e6322c` |
|
||||||
|
| Role | App Manager |
|
||||||
|
| File | `AuthKey_PPATU9GL73.p8` |
|
||||||
|
|
||||||
|
> **⚠️ The `.p8` file is NOT checked into git** (blocked by secret scanner). It lives at `~/.appstoreconnect/private_keys/AuthKey_PPATU9GL73.p8` on each machine. Copy it manually between machines or re-download from App Store Connect (one-time download only — keep a backup).
|
||||||
|
|
||||||
|
### Setup on any machine
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.appstoreconnect/private_keys
|
||||||
|
# Copy the .p8 from another machine or your backup
|
||||||
|
cp /path/to/AuthKey_PPATU9GL73.p8 ~/.appstoreconnect/private_keys/
|
||||||
|
chmod 600 ~/.appstoreconnect/private_keys/AuthKey_PPATU9GL73.p8
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Apple Developer Account
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
| ----------------- | ------------------------------------------------------------------ |
|
||||||
|
| Team ID | `748N7QPX7J` |
|
||||||
|
| Apple ID | `saravanakumardb1@gmail.com` |
|
||||||
|
| Portal | [developer.apple.com/account](https://developer.apple.com/account) |
|
||||||
|
| App Store Connect | [appstoreconnect.apple.com](https://appstoreconnect.apple.com) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Registered Apps
|
||||||
|
|
||||||
|
| Product | Bundle ID | App Store Connect | SKU | Status |
|
||||||
|
| ------------------ | --------------------------------- | ----------------- | ---------- | ------------------------------------------ |
|
||||||
|
| LysnrAI | `com.bytelyst.LysnrAI` | ✅ Created | lysnrai | In TestFlight (build 59) |
|
||||||
|
| ChronoMind | `com.saravana.chronomind` | ❌ Needs creation | chronomind | Bundle ID registered, pending app creation |
|
||||||
|
| ChronoMind Widgets | `com.saravana.chronomind.widgets` | N/A (extension) | — | Bundle ID registered |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## App Store Connect — Creating a New App
|
||||||
|
|
||||||
|
1. Go to [App Store Connect → My Apps](https://appstoreconnect.apple.com/apps)
|
||||||
|
2. Click **"+"** → **"New App"**
|
||||||
|
3. Fill in:
|
||||||
|
- **Platforms:** iOS
|
||||||
|
- **Name:** Product display name (e.g., `ChronoMind`)
|
||||||
|
- **Primary Language:** English (U.S.)
|
||||||
|
- **Bundle ID:** Select from dropdown (must be registered first — see below)
|
||||||
|
- **SKU:** Lowercase product ID (e.g., `chronomind`)
|
||||||
|
- **User Access:** Full Access
|
||||||
|
4. Click **Create**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Registering a New Bundle ID
|
||||||
|
|
||||||
|
### Via API (preferred)
|
||||||
|
|
||||||
|
```python
|
||||||
|
import jwt, time, json, urllib.request
|
||||||
|
|
||||||
|
with open('AuthKey_PPATU9GL73.p8', 'r') as f:
|
||||||
|
key = f.read()
|
||||||
|
|
||||||
|
token = jwt.encode(
|
||||||
|
{'iss': '1dbc2980-1621-4fb9-940b-e28257e6322c', 'iat': int(time.time()),
|
||||||
|
'exp': int(time.time()) + 1200, 'aud': 'appstoreconnect-v1'},
|
||||||
|
key, algorithm='ES256', headers={'kid': 'PPATU9GL73', 'typ': 'JWT'}
|
||||||
|
)
|
||||||
|
|
||||||
|
data = json.dumps({'data': {'type': 'bundleIds', 'attributes': {
|
||||||
|
'identifier': 'com.saravana.PRODUCT',
|
||||||
|
'name': 'ProductName',
|
||||||
|
'platform': 'IOS'
|
||||||
|
}}}).encode()
|
||||||
|
|
||||||
|
req = urllib.request.Request(
|
||||||
|
'https://api.appstoreconnect.apple.com/v1/bundleIds',
|
||||||
|
data=data,
|
||||||
|
headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'},
|
||||||
|
method='POST'
|
||||||
|
)
|
||||||
|
resp = urllib.request.urlopen(req)
|
||||||
|
print(json.loads(resp.read())['data']['id'])
|
||||||
|
```
|
||||||
|
|
||||||
|
### Via Portal
|
||||||
|
|
||||||
|
1. Go to [Certificates, Identifiers & Profiles → Identifiers](https://developer.apple.com/account/resources/identifiers/list)
|
||||||
|
2. Click **"+"** → **App IDs** → **App**
|
||||||
|
3. Enter description + bundle ID
|
||||||
|
4. Enable capabilities (App Groups, Push Notifications, etc.)
|
||||||
|
5. Click **Register**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TestFlight — Uploading a Build
|
||||||
|
|
||||||
|
### Automated (recommended)
|
||||||
|
|
||||||
|
Each product repo has a `release-testflight.sh` script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# LysnrAI
|
||||||
|
cd learning_voice_ai_agent/mobile_app/ios
|
||||||
|
# (uses manual pbxproj — see .windsurf/workflows/release-testflight.md)
|
||||||
|
|
||||||
|
# ChronoMind
|
||||||
|
cd learning_ai_clock/ios
|
||||||
|
bash release-testflight.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual CLI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Archive
|
||||||
|
xcodebuild archive \
|
||||||
|
-project MyApp.xcodeproj \
|
||||||
|
-scheme MyApp \
|
||||||
|
-configuration Release \
|
||||||
|
-archivePath /tmp/MyApp.xcarchive \
|
||||||
|
-destination 'generic/platform=iOS'
|
||||||
|
|
||||||
|
# 2. Export + Upload
|
||||||
|
xcodebuild -exportArchive \
|
||||||
|
-archivePath /tmp/MyApp.xcarchive \
|
||||||
|
-exportPath /tmp/MyApp_export \
|
||||||
|
-exportOptionsPlist ExportOptions.plist \
|
||||||
|
-allowProvisioningUpdates \
|
||||||
|
-authenticationKeyPath ~/.appstoreconnect/private_keys/AuthKey_PPATU9GL73.p8 \
|
||||||
|
-authenticationKeyID PPATU9GL73 \
|
||||||
|
-authenticationKeyIssuerID 1dbc2980-1621-4fb9-940b-e28257e6322c
|
||||||
|
```
|
||||||
|
|
||||||
|
### ExportOptions.plist (template)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>method</key>
|
||||||
|
<string>app-store-connect</string>
|
||||||
|
<key>teamID</key>
|
||||||
|
<string>748N7QPX7J</string>
|
||||||
|
<key>destination</key>
|
||||||
|
<string>upload</string>
|
||||||
|
<key>signingStyle</key>
|
||||||
|
<string>automatic</string>
|
||||||
|
<key>uploadSymbols</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## App Store Submission Checklist
|
||||||
|
|
||||||
|
Before submitting for App Store review, ensure:
|
||||||
|
|
||||||
|
### Required Assets
|
||||||
|
|
||||||
|
| Asset | Spec | Notes |
|
||||||
|
| ------------------ | ----------------------- | ---------------------------------------- |
|
||||||
|
| App Icon | 1024×1024 PNG | No alpha channel, no rounded corners |
|
||||||
|
| Screenshots (6.7") | 1290×2796 | iPhone 15 Pro Max — required |
|
||||||
|
| Screenshots (6.5") | 1284×2778 | iPhone 14 Plus — required |
|
||||||
|
| Screenshots (5.5") | 1242×2208 | iPhone 8 Plus — optional but recommended |
|
||||||
|
| iPad Screenshots | 2048×2732 | Required if app supports iPad |
|
||||||
|
| App Preview Video | 1920×1080 or device res | Optional, up to 30 seconds |
|
||||||
|
|
||||||
|
### Required Metadata
|
||||||
|
|
||||||
|
| Field | Notes |
|
||||||
|
| ------------------ | ------------------------------------------------ |
|
||||||
|
| App Name | Max 30 characters |
|
||||||
|
| Subtitle | Max 30 characters |
|
||||||
|
| Description | Required, no max (but first 3 lines matter most) |
|
||||||
|
| Keywords | Max 100 characters, comma-separated |
|
||||||
|
| Support URL | Must be a valid, reachable URL |
|
||||||
|
| Privacy Policy URL | **Required** — must be a valid, reachable URL |
|
||||||
|
| Category | Primary + optional secondary |
|
||||||
|
| Age Rating | Answer the questionnaire honestly |
|
||||||
|
| Copyright | e.g., `© 2026 ByteLyst` |
|
||||||
|
|
||||||
|
### Required for Review
|
||||||
|
|
||||||
|
| Requirement | Details |
|
||||||
|
| ---------------------- | --------------------------------------------------------------- |
|
||||||
|
| Privacy Policy URL | Must be publicly accessible |
|
||||||
|
| App Review Information | Contact info (name, phone, email) for reviewer |
|
||||||
|
| Demo Account | If app requires login, provide test credentials |
|
||||||
|
| Notes for Review | Explain any non-obvious features or permissions |
|
||||||
|
| Content Rights | Confirm you have rights to all content |
|
||||||
|
| IDFA Declaration | If using AdSupport framework |
|
||||||
|
| Export Compliance | Encryption usage (most apps: standard encryption = YES, exempt) |
|
||||||
|
|
||||||
|
### Privacy & Permissions
|
||||||
|
|
||||||
|
For each permission your app uses, you must:
|
||||||
|
|
||||||
|
1. Add a usage description string in Info.plist (e.g., `NSMicrophoneUsageDescription`)
|
||||||
|
2. Declare it in App Store Connect → App Privacy
|
||||||
|
|
||||||
|
Common permissions for ByteLyst apps:
|
||||||
|
|
||||||
|
| Permission | Info.plist Key | Apps |
|
||||||
|
| ------------------ | ------------------------------------- | ----------------- |
|
||||||
|
| Microphone | `NSMicrophoneUsageDescription` | LysnrAI |
|
||||||
|
| Speech Recognition | `NSSpeechRecognitionUsageDescription` | LysnrAI |
|
||||||
|
| Location | `NSLocationWhenInUseUsageDescription` | PeakPulse |
|
||||||
|
| Health | `NSHealthShareUsageDescription` | PeakPulse, NomGap |
|
||||||
|
| Notifications | `NSUserNotificationsUsageDescription` | All |
|
||||||
|
| Camera | `NSCameraUsageDescription` | MindLyst |
|
||||||
|
|
||||||
|
### App Privacy (Data Collection)
|
||||||
|
|
||||||
|
In App Store Connect → App Privacy, declare:
|
||||||
|
|
||||||
|
- What data types your app collects
|
||||||
|
- Whether data is linked to user identity
|
||||||
|
- Whether data is used for tracking
|
||||||
|
- Purpose of each data type
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TestFlight Beta Testing
|
||||||
|
|
||||||
|
### Internal Testing (up to 100 testers)
|
||||||
|
|
||||||
|
1. Upload build (see above)
|
||||||
|
2. Wait ~15-30 min for processing
|
||||||
|
3. Go to App Store Connect → TestFlight → Internal Testing
|
||||||
|
4. Add testers by Apple ID email
|
||||||
|
5. Testers install via TestFlight app
|
||||||
|
|
||||||
|
### External Testing (up to 10,000 testers)
|
||||||
|
|
||||||
|
1. Create a beta group in TestFlight
|
||||||
|
2. Add a build to the group
|
||||||
|
3. Submit for **Beta App Review** (required for external testers)
|
||||||
|
4. Review takes ~24-48 hours
|
||||||
|
5. Once approved, distribute via link or email
|
||||||
|
|
||||||
|
### Beta App Review Requirements
|
||||||
|
|
||||||
|
- Same as App Store review but slightly more lenient
|
||||||
|
- Still needs: privacy policy URL, app description, contact info
|
||||||
|
- Crashes or obvious bugs will be rejected
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Certificates & Signing
|
||||||
|
|
||||||
|
| Type | Purpose | Managed By |
|
||||||
|
| ------------------ | ----------------------------- | ------------------------------ |
|
||||||
|
| Apple Development | Debug builds, simulator | Xcode (automatic) |
|
||||||
|
| Apple Distribution | App Store / TestFlight builds | Xcode (automatic with API key) |
|
||||||
|
|
||||||
|
With `-allowProvisioningUpdates` and the API key, Xcode automatically manages certificates and provisioning profiles. No manual certificate management needed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Error | Fix |
|
||||||
|
| ------------------------------------------- | -------------------------------------------------------- |
|
||||||
|
| `No signing certificate "iOS Distribution"` | Use `-allowProvisioningUpdates` with API key |
|
||||||
|
| `Authentication failed` | Check .p8 key path, Key ID, Issuer ID |
|
||||||
|
| `Error Downloading App Information` | Create app record in App Store Connect first |
|
||||||
|
| `App Groups capability` | Register App Group in developer portal |
|
||||||
|
| `FORBIDDEN_ERROR` on app creation | API key needs Admin role (App Manager can't create apps) |
|
||||||
|
| `Bundle ID not available` | Someone else registered it — use a different one |
|
||||||
|
| `Missing compliance` | Set Export Compliance in App Store Connect |
|
||||||
|
| `Build processing stuck` | Wait up to 1 hour; check email for processing errors |
|
||||||
331
docs/_MobileApps/GOOGLE/README.md
Normal file
331
docs/_MobileApps/GOOGLE/README.md
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
# Google Play Store — Android App Distribution Guide
|
||||||
|
|
||||||
|
## Google Play Console
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
| ----------------- | ---------------------------------------------------------- |
|
||||||
|
| Console URL | [play.google.com/console](https://play.google.com/console) |
|
||||||
|
| Developer Account | ByteLyst / saravanakumardb1@gmail.com |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Registered Apps
|
||||||
|
|
||||||
|
| Product | Package Name | Play Console | Status |
|
||||||
|
| ---------- | ---------------------- | ----------------- | -------------------------------- |
|
||||||
|
| LysnrAI | `com.bytelyst.lysnrai` | ❌ Needs creation | Gradle builds, pending setup |
|
||||||
|
| ChronoMind | `com.chronomind.app` | ❌ Needs creation | Gradle builds, pending setup |
|
||||||
|
| MindLyst | `com.mindlyst.android` | ❌ Needs creation | KMP shared module, pending setup |
|
||||||
|
| JarvisJr | `com.jarvisjr.app` | ❌ Needs creation | Jetpack Compose, pending setup |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## One-Time Setup
|
||||||
|
|
||||||
|
### 1. Google Play Console Account
|
||||||
|
|
||||||
|
If you don't have one yet:
|
||||||
|
|
||||||
|
1. Go to [play.google.com/console/signup](https://play.google.com/console/signup)
|
||||||
|
2. Pay the one-time $25 registration fee
|
||||||
|
3. Complete identity verification (can take 48 hours)
|
||||||
|
|
||||||
|
### 2. Create a Service Account (for CLI uploads)
|
||||||
|
|
||||||
|
1. Go to [Google Cloud Console → IAM → Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts)
|
||||||
|
2. Select or create a project (e.g., `bytelyst-releases`)
|
||||||
|
3. Click **"+ Create Service Account"**
|
||||||
|
- Name: `play-upload`
|
||||||
|
- Role: (skip — we'll grant access in Play Console)
|
||||||
|
4. Click **"..."** → **"Manage keys"** → **"Add Key"** → **"Create new key"** → **JSON**
|
||||||
|
5. Save the JSON key file to this directory and `~/.google-play/`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.google-play
|
||||||
|
cp service-account-key.json ~/.google-play/
|
||||||
|
chmod 600 ~/.google-play/service-account-key.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Link Service Account to Play Console
|
||||||
|
|
||||||
|
1. Go to [Play Console → Settings → API access](https://play.google.com/console/developers/api-access)
|
||||||
|
2. Click **"Link"** next to Google Cloud project
|
||||||
|
3. Under Service Accounts, find `play-upload` → **"Grant access"**
|
||||||
|
4. Set permissions: **Release manager** (or **Admin** for full control)
|
||||||
|
5. Add the apps it can manage
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Creating a New App in Play Console
|
||||||
|
|
||||||
|
1. Go to [Play Console → All apps](https://play.google.com/console/developers)
|
||||||
|
2. Click **"Create app"**
|
||||||
|
3. Fill in:
|
||||||
|
- **App name:** Product name (e.g., `ChronoMind`)
|
||||||
|
- **Default language:** English (United States)
|
||||||
|
- **App or Game:** App
|
||||||
|
- **Free or Paid:** Free
|
||||||
|
4. Accept declarations and click **"Create app"**
|
||||||
|
|
||||||
|
### After Creation — Complete the Store Listing
|
||||||
|
|
||||||
|
Navigate through the **Dashboard** checklist:
|
||||||
|
|
||||||
|
| Section | Required For |
|
||||||
|
| --------------- | ------------------------------------------------ |
|
||||||
|
| App access | If app requires login → provide test credentials |
|
||||||
|
| Ads | Declare if app contains ads |
|
||||||
|
| Content rating | Complete IARC questionnaire |
|
||||||
|
| Target audience | Select age groups |
|
||||||
|
| News apps | Declare if it's a news app |
|
||||||
|
| COVID-19 apps | Declare if related |
|
||||||
|
| Data safety | Privacy questionnaire (like Apple's App Privacy) |
|
||||||
|
| Government apps | Declare if government-related |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Building an Android Release
|
||||||
|
|
||||||
|
### Signing Key Setup (one-time per app)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate an upload keystore
|
||||||
|
keytool -genkey -v \
|
||||||
|
-keystore ~/keystores/chronomind-upload.jks \
|
||||||
|
-keyalg RSA -keysize 2048 -validity 10000 \
|
||||||
|
-alias upload \
|
||||||
|
-storepass <PASSWORD> \
|
||||||
|
-keypass <PASSWORD> \
|
||||||
|
-dname "CN=ByteLyst, O=ByteLyst, L=Dallas, ST=TX, C=US"
|
||||||
|
```
|
||||||
|
|
||||||
|
> **⚠️ CRITICAL:** Back up the keystore file and passwords. If you lose them, you cannot update the app. Store a copy in this directory.
|
||||||
|
|
||||||
|
### Configure signing in the app
|
||||||
|
|
||||||
|
Create or edit `android/keystore.properties` (gitignored):
|
||||||
|
|
||||||
|
```properties
|
||||||
|
storePassword=<PASSWORD>
|
||||||
|
keyPassword=<PASSWORD>
|
||||||
|
keyAlias=upload
|
||||||
|
storeFile=/Users/<you>/keystores/chronomind-upload.jks
|
||||||
|
```
|
||||||
|
|
||||||
|
Reference in `android/app/build.gradle.kts`:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val keystoreProperties = Properties()
|
||||||
|
val keystoreFile = rootProject.file("keystore.properties")
|
||||||
|
if (keystoreFile.exists()) keystoreProperties.load(keystoreFile.inputStream())
|
||||||
|
|
||||||
|
android {
|
||||||
|
signingConfigs {
|
||||||
|
create("release") {
|
||||||
|
keyAlias = keystoreProperties["keyAlias"] as? String
|
||||||
|
keyPassword = keystoreProperties["keyPassword"] as? String
|
||||||
|
storeFile = file(keystoreProperties["storeFile"] as? String ?: "")
|
||||||
|
storePassword = keystoreProperties["storePassword"] as? String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
signingConfig = signingConfigs.getByName("release")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build the Release AAB
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd android
|
||||||
|
./gradlew :app:bundleRelease
|
||||||
|
# Output: app/build/outputs/bundle/release/app-release.aab
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build a Release APK (for direct install)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :app:assembleRelease
|
||||||
|
# Output: app/build/outputs/apk/release/app-release.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Uploading to Play Console
|
||||||
|
|
||||||
|
### Manual Upload (Play Console UI)
|
||||||
|
|
||||||
|
1. Go to app → **Production** (or **Internal testing** / **Closed testing**)
|
||||||
|
2. Click **"Create new release"**
|
||||||
|
3. Upload the `.aab` file
|
||||||
|
4. Add release notes
|
||||||
|
5. Click **"Review release"** → **"Start rollout"**
|
||||||
|
|
||||||
|
### CLI Upload via bundletool + Google API
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install google-api-python-client google-auth
|
||||||
|
|
||||||
|
python3 -c "
|
||||||
|
from googleapiclient.discovery import build
|
||||||
|
from google.oauth2 import service_account
|
||||||
|
|
||||||
|
creds = service_account.Credentials.from_service_account_file(
|
||||||
|
'$HOME/.google-play/service-account-key.json',
|
||||||
|
scopes=['https://www.googleapis.com/auth/androidpublisher']
|
||||||
|
)
|
||||||
|
|
||||||
|
service = build('androidpublisher', 'v3', credentials=creds)
|
||||||
|
package = 'com.chronomind.app'
|
||||||
|
|
||||||
|
edit = service.edits().insert(body={}, packageName=package).execute()
|
||||||
|
edit_id = edit['id']
|
||||||
|
|
||||||
|
service.edits().bundles().upload(
|
||||||
|
editId=edit_id, packageName=package,
|
||||||
|
media_body='app/build/outputs/bundle/release/app-release.aab',
|
||||||
|
media_mime_type='application/octet-stream'
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
service.edits().tracks().update(
|
||||||
|
editId=edit_id, packageName=package, track='internal',
|
||||||
|
body={'releases': [{'status': 'completed', 'versionCodes': ['1']}]}
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
service.edits().commit(editId=edit_id, packageName=package).execute()
|
||||||
|
print('Upload complete!')
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Gradle Play Publisher Plugin (recommended for CI)
|
||||||
|
|
||||||
|
Add to `android/build.gradle.kts`:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
plugins {
|
||||||
|
id("com.github.triplet.play") version "3.11.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
play {
|
||||||
|
serviceAccountCredentials.set(file("$HOME/.google-play/service-account-key.json"))
|
||||||
|
track.set("internal") // internal, alpha, beta, production
|
||||||
|
defaultToAppBundles.set(true)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew publishBundle # Upload AAB to configured track
|
||||||
|
./gradlew publishInternalBundle # Upload to internal testing
|
||||||
|
./gradlew publishAlphaBundle # Upload to closed testing
|
||||||
|
./gradlew publishBundle --track production # Upload to production
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Tracks
|
||||||
|
|
||||||
|
| Track | Testers | Review Required | Purpose |
|
||||||
|
| ---------------------- | -------------------- | -------------------- | ----------------- |
|
||||||
|
| Internal testing | Up to 100 (by email) | No | Quick dev testing |
|
||||||
|
| Closed testing (Alpha) | Invite-only | No (first time: yes) | Wider beta |
|
||||||
|
| Open testing (Beta) | Anyone with link | Yes (first time) | Public beta |
|
||||||
|
| Production | Everyone | Yes | Full release |
|
||||||
|
|
||||||
|
### Internal Testing Setup
|
||||||
|
|
||||||
|
1. Go to app → **Testing** → **Internal testing**
|
||||||
|
2. Click **"Create new release"**
|
||||||
|
3. Upload `.aab`
|
||||||
|
4. Click **"Review release"** → **"Start rollout"**
|
||||||
|
5. Go to **Testers** tab → Create email list → Add tester emails
|
||||||
|
6. Share the opt-in link with testers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Play Store Submission Checklist
|
||||||
|
|
||||||
|
### Required Assets
|
||||||
|
|
||||||
|
| Asset | Spec | Notes |
|
||||||
|
| ------------------------ | ------------------------ | ----------------------------- |
|
||||||
|
| App Icon | 512×512 PNG | 32-bit, no alpha |
|
||||||
|
| Feature Graphic | 1024×500 PNG or JPG | Required for store listing |
|
||||||
|
| Screenshots (Phone) | 320-3840px, 16:9 or 9:16 | Min 2, max 8 per device type |
|
||||||
|
| Screenshots (7" Tablet) | Same as phone | Required if targeting tablets |
|
||||||
|
| Screenshots (10" Tablet) | Same as phone | Required if targeting tablets |
|
||||||
|
| Promo Video | YouTube URL | Optional |
|
||||||
|
|
||||||
|
### Required Metadata
|
||||||
|
|
||||||
|
| Field | Notes |
|
||||||
|
| ------------------- | --------------------------- |
|
||||||
|
| App name | Max 30 characters |
|
||||||
|
| Short description | Max 80 characters |
|
||||||
|
| Full description | Max 4000 characters |
|
||||||
|
| App category | Select from predefined list |
|
||||||
|
| Contact email | Required, public |
|
||||||
|
| Privacy Policy URL | **Required** |
|
||||||
|
| App type & category | App or Game + category |
|
||||||
|
|
||||||
|
### Data Safety Declaration
|
||||||
|
|
||||||
|
In Play Console → App content → Data safety:
|
||||||
|
|
||||||
|
- Declare all data types collected
|
||||||
|
- Whether data is encrypted in transit
|
||||||
|
- Whether users can request deletion
|
||||||
|
- Whether data is shared with third parties
|
||||||
|
- Purpose of each data type
|
||||||
|
|
||||||
|
### Content Rating (IARC)
|
||||||
|
|
||||||
|
1. Go to App content → Content rating
|
||||||
|
2. Complete the IARC questionnaire
|
||||||
|
3. Receive ratings for all regions automatically
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Corporate Network / Gradle Proxy
|
||||||
|
|
||||||
|
When building on the corporate network:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ensure NETWORK=corp is set (switch-network.sh)
|
||||||
|
echo $GRADLE_OPTS
|
||||||
|
# Should show -Djavax.net.ssl.trustStore=~/.gradle/ssl/gradle-cacerts.jks
|
||||||
|
|
||||||
|
# If not set:
|
||||||
|
export GRADLE_OPTS="-Djavax.net.ssl.trustStore=$HOME/.gradle/ssl/gradle-cacerts.jks -Djavax.net.ssl.trustStorePassword=changeit"
|
||||||
|
```
|
||||||
|
|
||||||
|
See `learning_ai_common_plat/AGENTS.md` § 9 for full proxy setup docs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Error | Fix |
|
||||||
|
| ----------------------------- | ------------------------------------------------------------ |
|
||||||
|
| `Keystore was tampered with` | Wrong password or corrupted keystore |
|
||||||
|
| `No key with alias 'upload'` | Check `keyAlias` in keystore.properties |
|
||||||
|
| `Version code already exists` | Bump `versionCode` in build.gradle.kts |
|
||||||
|
| `APK/AAB not signed` | Check signingConfig in release buildType |
|
||||||
|
| `Deobfuscation file missing` | Upload mapping.txt alongside the AAB |
|
||||||
|
| `Data safety form incomplete` | Complete all sections in App content → Data safety |
|
||||||
|
| `Target API level` | Google requires targeting latest Android API (currently 34+) |
|
||||||
|
| Gradle proxy failure | Set `GRADLE_OPTS` with truststore (see above) |
|
||||||
|
| `SDK not found` | Set `sdk.dir` in `local.properties` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
|
||||||
|
| File | Location | Purpose |
|
||||||
|
| ------------------- | ------------------------------------------ | -------------------- |
|
||||||
|
| Service Account Key | `~/.google-play/service-account-key.json` | CLI uploads |
|
||||||
|
| Upload Keystore | `~/keystores/<app>-upload.jks` | Release signing |
|
||||||
|
| keystore.properties | `android/keystore.properties` (gitignored) | Local signing config |
|
||||||
Loading…
Reference in New Issue
Block a user