feat: implement secure APK signing for GitHub Actions - #279
Conversation
- Add comprehensive GitHub Secrets setup guide with keystore creation instructions - Configure conditional signing system with production keystore fallback to debug - Update build.gradle with secure credential loading and environment variable support - Add GitHub Actions workflow steps for keystore decoding and verification - Implement dual storage architecture for development and CI environments - Include troubleshooting guide and security best practices documentation - Fix APK installation issues by ensuring consistent signing certificates
|
Caution Review failedThe pull request is closed. WalkthroughAdds CI steps to provision, use, verify, and clean an Android keystore during builds; updates Gradle to support conditional release signing from key.properties or environment with a debug fallback; narrows .gitignore keystore rules; and adds docs plus a key.properties example. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant GH as GitHub Actions
participant Secrets as Repo Secrets
participant FS as Workspace FS
participant Build as Gradle/Flutter Build
participant Tools as Verification Tools
Note over GH: Build job (runs if SHOULD_BUILD)
GH->>Secrets: Read ANDROID_KEYSTORE_FILE (base64), ANDROID_KEYSTORE_PASSWORD, ANDROID_KEY_PASSWORD, ANDROID_KEY_ALIAS
GH->>FS: Decode keystore -> `android/app/upload-keystore.jks`
GH->>FS: Write `android/key.properties` with store/key values
GH->>Build: Run build with env: ANDROID_KEYSTORE_*, ANDROID_KEYSTORE_FILE
rect rgba(230,245,255,0.6)
note right of Build: Build APK / AAB (uses signing if hasKeystore)
Build->>FS: Emit APK / AAB artifacts
end
GH->>FS: Check APK exists
GH->>Tools: Run jarsigner (and apksigner if available) to verify signature (optional fingerprint check)
alt Signed
GH->>GH: Continue to release steps
else Unsigned / missing
GH->>GH: Fail verification
end
rect rgba(255,240,230,0.6)
Note over FS: Cleanup (always)
GH->>FS: Remove `android/app/upload-keystore.jks` and `android/key.properties`
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.gitignore (1)
43-45: Potentially dangerous pattern:*.gradleignores Gradle build scripts repo-wideWhile not part of this diff, keeping
*.gradlewill cause any newly-created Gradle scripts (e.g., build.gradle.kts or additional module .gradle files) to be ignored by Git. This can lead to missing files in future PRs. Suggest replacing it with directory-scoped ignores only (e.g.,.gradle/which you already have under Android).Apply this diff to tighten the pattern:
-*.gradle +.gradle/
🧹 Nitpick comments (7)
android/key.properties.example (1)
1-6: Tighten placeholders and add an explicit path hint for storeFileLooks good overall. Two small improvements:
- Prevent accidental copy of placeholders by using a clearly invalid alias and file name.
- Align storeFile with where the workflow writes the keystore (android/app/upload-keystore.jks).
-# Example key.properties for local development -# Copy this to key.properties and fill in your actual values -storePassword=your_store_password_here -keyPassword=your_key_password_here -keyAlias=your_key_alias_here -storeFile=your_keystore_file_name.jks +# Example key.properties for local development +# Copy this to android/key.properties and replace ALL placeholder values +storePassword=REPLACE_ME_STORE_PASSWORD +keyPassword=REPLACE_ME_KEY_PASSWORD +keyAlias=REPLACE_ME_KEY_ALIAS +# If your keystore is located at android/app/upload-keystore.jks (default), keep this: +storeFile=app/upload-keystore.jks +# Otherwise, set a correct relative path from android/ (e.g., app/my-release.jks)GITHUB_SECRETS_SETUP.md (4)
80-99: Cross‑platform base64 guidance
base64 -w 0works on GNU coreutils (Linux) but fails on macOS. Provide a macOS-friendly variant to reduce setup friction.-# Generate base64 (this creates one very long string) -base64 -w 0 android/app/upload-keystore.jks +# Generate base64 of the keystore as a single line +# Linux (GNU coreutils): +base64 -w 0 android/app/upload-keystore.jks +# macOS (BSD base64, no -w flag): +base64 android/app/upload-keystore.jks | tr -d '\n'
217-228: Add code fence language and avoid implying exact fingerprintsMarking code fences with a language fixes markdownlint MD040. Also, fingerprints shown here are examples; consider clarifying to prevent copy/paste mistakes.
-``` +```text Alias name: youraliasname -Creation date: Aug 13, 2025 +Creation date: <Aug 13, 2025> Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=Your App Name, OU=Organization, O=Company... Valid from: Wed Aug 13 18:47:47 UYT 2025 until: Sun Dec 29 18:47:47 UYT 2052 Certificate fingerprints: SHA1: FD:DB:64:E4:7C:60:4D:BD:27:9F:A7:C2:D7:16:AB:6B:40:74:9A:F3 SHA256: 67:6F:67:26:76:D3:59:3A:C5:2F:01:7C:58:1C:50:C0:8B...--- `429-435`: **Avoid encouraging weak passwords in troubleshooting** Suggest removing “Try common passwords” to avoid normalizing weak credential practices. Instead, direct users to check password managers/backups and original provisioning docs. ```diff -2. **Try common passwords**: `android`, `123456`, `password`, your app name -3. **Check your notes/password manager** for the correct password +2. **Check your notes/password manager** for the correct password +3. **Consult your team’s provisioning/backups** for the recorded credentials
371-377: Add language to fenced code block (markdownlint MD040) and mask secrets in examplesMark code block as text and avoid printing actual secret names inline to discourage copy/paste into logs.
-``` +```text Setting environment variables ANDROID_KEYSTORE_PASSWORD=*** ANDROID_KEY_PASSWORD=*** ANDROID_KEY_ALIAS=*** ANDROID_KEYSTORE_FILE=upload-keystore.jks</blockquote></details> <details> <summary>android/app/build.gradle (1)</summary><blockquote> `65-74`: **Make the fallback explicit and fail fast for CI if signing is expected** The warning is helpful for local dev, but CI should not unknowingly ship a debug-signed “release”. Consider failing the build in CI when hasKeystore=false. ```diff release { if (hasKeystore) { signingConfig signingConfigs.release } else { // Fallback to debug signing for development signingConfig signingConfigs.debug - logger.warn("Release build using debug signing - keystore not available") + logger.warn("Release build using debug signing - keystore not available") + if (System.getenv('CI')?.toBoolean()) { + throw new GradleException("CI release build requires a release keystore") + } } }.github/workflows/main.yml (1)
167-171: Also clean up key.properties; add newline at EOFRemove the temporary android/key.properties after the build (secrets hygiene) and satisfy “no newline at EOF”.
- - name: Cleanup Keystore + - name: Cleanup Keystore if: always() && steps.should_build.outputs.SHOULD_BUILD == 'true' run: | # Remove keystore file for security - rm -f android/app/upload-keystore.jks + rm -f android/app/upload-keystore.jks android/key.properties +
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
.github/workflows/main.yml(3 hunks).gitignore(1 hunks)GITHUB_SECRETS_SETUP.md(1 hunks)android/app/build.gradle(3 hunks)android/key.properties.example(1 hunks)
🧰 Additional context used
🪛 LanguageTool
GITHUB_SECRETS_SETUP.md
[grammar] ~7-~7: There might be a mistake here.
Context: ...Guide Achieves By the end, you'll have: - ✅ Properly signed APKs built in GitHub A...
(QB_NEW_EN)
[grammar] ~8-~8: There might be a mistake here.
Context: ...erly signed APKs built in GitHub Actions - ✅ Consistent signing certificates across...
(QB_NEW_EN)
[grammar] ~9-~9: There might be a mistake here.
Context: ...t signing certificates across all builds - ✅ Secure credential storage in GitHub Se...
(QB_NEW_EN)
[grammar] ~10-~10: There might be a mistake here.
Context: ...ure credential storage in GitHub Secrets - ✅ APK installation that works without un...
(QB_NEW_EN)
[grammar] ~21-~21: There might be a mistake here.
Context: ...hat You Need Before starting, you need: - ✅ Android keystore file (.jks or `.key...
(QB_NEW_EN)
[grammar] ~50-~50: There might be a mistake here.
Context: ...-key.jks` If you find keystore files: - ✅ You have a keystore → Skip to "Cre...
(QB_NEW_EN)
[grammar] ~53-~53: There might be a mistake here.
Context: ...found** → These won't work for release builds, create a new one **If no keystore files...
(QB_NEW_EN)
[grammar] ~53-~53: There might be a mistake here.
Context: ...ork for release builds, create a new one If no keystore files found: - ❌ **No ke...
(QB_NEW_EN)
[grammar] ~55-~55: There might be a mistake here.
Context: ...a new one If no keystore files found: - ❌ No keystore exists → Continue to "...
(QB_NEW_EN)
[grammar] ~66-~66: There might be a mistake here.
Context: ...ds (choose strong, memorable passwords):** - Store Password: Protects the keystore ...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...ssword**: Protects the signing key (can be same as store password) Key Alias:...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...: Protects the signing key (can be same as store password) Key Alias: A name ...
(QB_NEW_EN)
[grammar] ~72-~72: There might be a mistake here.
Context: ...tion (these identify your organization):** - CN (Common Name): Your app name or org...
(QB_NEW_EN)
[grammar] ~73-~73: There might be a mistake here.
Context: ...ganization (e.g., MyApp, My Company) - OU (Organizational Unit): Your departm...
(QB_NEW_EN)
[grammar] ~74-~74: There might be a mistake here.
Context: ...g., Mobile Development, Engineering) - O (Organization): Your company name (e...
(QB_NEW_EN)
[grammar] ~75-~75: There might be a mistake here.
Context: ...our company name (e.g., MyCompany Inc) - L (Locality): Your city (e.g., `San Fr...
(QB_NEW_EN)
[grammar] ~76-~76: There might be a mistake here.
Context: ...ty)**: Your city (e.g., San Francisco) - ST (State): Your state/province (e.g.,...
(QB_NEW_EN)
[grammar] ~77-~77: There might be a mistake here.
Context: ...Your state/province (e.g., California) - C (Country): Your country code (e.g., ...
(QB_NEW_EN)
[grammar] ~100-~100: There might be a mistake here.
Context: ...OUR_COUNTRY" ``` Command Explanation: - -keystore upload-keystore.jks: Creates file named `upload-keystore.jk...
(QB_NEW_EN)
[grammar] ~101-~101: There might be a mistake here.
Context: ...pload-keystore.jks: Creates file named upload-keystore.jks--keyalg RSA -keysize 2048`: Uses RSA encryption with 2048-bit key ...
(QB_NEW_EN)
[grammar] ~102-~102: There might be a mistake here.
Context: ...SA encryption with 2048-bit key (secure) - -validity 10000: Certificate valid for ~27 years - `-al...
(QB_NEW_EN)
[grammar] ~103-~103: There might be a mistake here.
Context: ... 10000: Certificate valid for ~27 years - -alias YOUR_CHOSEN_ALIAS`: Replace with your chosen alias name - ...
(QB_NEW_EN)
[grammar] ~104-~104: There might be a mistake here.
Context: ...AS: Replace with your chosen alias name - -storepass / -keypass: Replace with your chosen passwords - ...
(QB_NEW_EN)
[grammar] ~105-~105: There might be a mistake here.
Context: ...ass: Replace with your chosen passwords - -dname`: Replace with your organization informa...
(QB_NEW_EN)
[grammar] ~135-~135: There might be a mistake here.
Context: ... ``` Store this information securely: - Add to your password manager - Write in ...
(QB_NEW_EN)
[grammar] ~186-~186: There might be a mistake here.
Context: ...ad-keystore.jks ``` File explanation: - storePassword: The password that protects your keysto...
(QB_NEW_EN)
[grammar] ~187-~187: There might be a mistake here.
Context: ...assword that protects your keystore file - keyPassword: The password that protects your signin...
(QB_NEW_EN)
[grammar] ~188-~188: There might be a mistake here.
Context: ...assword that protects your signing key (often same as store password) - keyAlias: T...
(QB_NEW_EN)
[grammar] ~188-~188: There might be a mistake here.
Context: ...t protects your signing key (often same as store password) - keyAlias: The name ...
(QB_NEW_EN)
[grammar] ~188-~188: There might be a mistake here.
Context: ...gning key (often same as store password) - keyAlias: The name of your signing key within th...
(QB_NEW_EN)
[grammar] ~189-~189: There might be a mistake here.
Context: ... of your signing key within the keystore - storeFile: The filename of your keystore (should ...
(QB_NEW_EN)
[grammar] ~210-~210: There might be a mistake here.
Context: ...ASSWORD ``` Replace the placeholders: - YOUR_KEY_ALIAS: Use the value from your `key.propertie...
(QB_NEW_EN)
[grammar] ~230-~230: There might be a mistake here.
Context: ... GitHub builds use the same certificate. ### Common Errors and Solutions #### ❌ "keys...
(QB_NEW_EN)
[grammar] ~235-~235: There might be a mistake here.
Context: ...ur store password is wrong. Check these: 1. Verify the password in key.properties ...
(QB_NEW_EN)
[grammar] ~241-~241: There might be a mistake here.
Context: ...ist" Your alias name is wrong. Fix this: 1. List all aliases in your keystore: ...
(QB_NEW_EN)
[grammar] ~250-~250: There might be a mistake here.
Context: ... file not found" The file path is wrong: 1. Check the file exists: `ls -la app/upl...
(QB_NEW_EN)
[grammar] ~251-~251: There might be a mistake here.
Context: ...is wrong: 1. Check the file exists: ls -la app/upload-keystore.jks 2. Look for keystore files: `find . -name...
(QB_NEW_EN)
[grammar] ~252-~252: There might be a mistake here.
Context: ...re.jks2. **Look for keystore files**:find . -name ".jks" -o -name ".keystore"` 3. Update the storeFile path in key.prope...
(QB_NEW_EN)
[grammar] ~282-~282: There might be a mistake here.
Context: ... values** (you'll need them for GitHub): 1. Store Password: storePassword= value...
(QB_NEW_EN)
[grammar] ~283-~283: There might be a mistake here.
Context: ...Store Password**: storePassword= value 2. Key Password: keyPassword= value 3...
(QB_NEW_EN)
[grammar] ~284-~284: There might be a mistake here.
Context: .... Key Password: keyPassword= value 3. Key Alias: keyAlias= value 4. **Stor...
(QB_NEW_EN)
[grammar] ~285-~285: There might be a mistake here.
Context: ...ue 3. Key Alias: keyAlias= value 4. Store File: storeFile= value (usuall...
(QB_NEW_EN)
[grammar] ~335-~335: There might be a mistake here.
Context: ...secret**: #### 1. ANDROID_KEYSTORE_FILE - Name: ANDROID_KEYSTORE_FILE - **Valu...
(QB_NEW_EN)
[grammar] ~336-~336: There might be a mistake here.
Context: ...## 1. ANDROID_KEYSTORE_FILE - Name: ANDROID_KEYSTORE_FILE - Value: Your very long base64 string fr...
(QB_NEW_EN)
[grammar] ~339-~339: There might be a mistake here.
Context: ...tep 2 #### 2. ANDROID_KEYSTORE_PASSWORD - Name: ANDROID_KEYSTORE_PASSWORD - **...
(QB_NEW_EN)
[grammar] ~340-~340: There might be a mistake here.
Context: .... ANDROID_KEYSTORE_PASSWORD - Name: ANDROID_KEYSTORE_PASSWORD - Value: The storePassword value from ...
(QB_NEW_EN)
[grammar] ~343-~343: There might be a mistake here.
Context: ...properties #### 3. ANDROID_KEY_PASSWORD - Name: ANDROID_KEY_PASSWORD - **Val...
(QB_NEW_EN)
[grammar] ~344-~344: There might be a mistake here.
Context: ...### 3. ANDROID_KEY_PASSWORD - Name: ANDROID_KEY_PASSWORD - Value: The keyPassword value from yo...
(QB_NEW_EN)
[grammar] ~347-~347: There might be a mistake here.
Context: ...ey.properties #### 4. ANDROID_KEY_ALIAS - Name: ANDROID_KEY_ALIAS - Value:...
(QB_NEW_EN)
[grammar] ~348-~348: There might be a mistake here.
Context: ... #### 4. ANDROID_KEY_ALIAS - Name: ANDROID_KEY_ALIAS - Value: The keyAlias value from your ...
(QB_NEW_EN)
[grammar] ~369-~369: There might be a mistake here.
Context: ...ions log: #### ✅ Setup Android Keystore Should show: ``` Setting environment var...
(QB_NEW_EN)
[grammar] ~379-~379: There might be a mistake here.
Context: ...pload-keystore.jks ``` #### ✅ Build APK Should complete without errors about key...
(QB_NEW_EN)
[grammar] ~382-~382: There might be a mistake here.
Context: ...e or signing. #### ✅ Verify APK Signing Should show: ``` ✅ APK built successfull...
(QB_NEW_EN)
[grammar] ~398-~398: There might be a mistake here.
Context: ...ints" ``` GitHub Actions certificate: - Download the APK artifact from GitHub Ac...
(QB_NEW_EN)
[grammar] ~414-~414: There might be a mistake here.
Context: ...keystore.jks ``` The build system will: - Use your keystore when available locally...
(QB_NEW_EN)
[grammar] ~416-~416: There might be a mistake here.
Context: ...le locally - Fall back to debug signing if keystore is missing - Show warnings abo...
(QB_NEW_EN)
[grammar] ~423-~423: There might be a mistake here.
Context: ...❌ "keystore not found" in GitHub Actions Cause: Base64 keystore secret is wrong...
(QB_NEW_EN)
[grammar] ~425-~425: There might be a mistake here.
Context: ...secret is wrong or missing Solution: 1. Regenerate base64: `base64 -w 0 android/...
(QB_NEW_EN)
[grammar] ~426-~426: There might be a mistake here.
Context: ...ng Solution: 1. Regenerate base64: base64 -w 0 android/app/upload-keystore.jks 2. Update ANDROID_KEYSTORE_FILE secret wi...
(QB_NEW_EN)
[grammar] ~429-~429: There might be a mistake here.
Context: ...### ❌ "Wrong password" in GitHub Actions Cause: Password secrets don't match yo...
(QB_NEW_EN)
[grammar] ~431-~431: There might be a mistake here.
Context: ... don't match your keystore Solution: 1. Double-check the values in your local `k...
(QB_NEW_EN)
[grammar] ~433-~433: There might be a mistake here.
Context: ...ey.properties` 2. Update GitHub secrets with exact same values 3. Ensure no extra sp...
(QB_NEW_EN)
[style] ~433-~433: ‘exact same’ might be wordy. Consider a shorter alternative.
Context: ...operties` 2. Update GitHub secrets with exact same values 3. Ensure no extra spaces or cha...
(EN_WORDINESS_PREMIUM_EXACT_SAME)
[grammar] ~438-~438: There might be a mistake here.
Context: ...PK wasn't properly signed Solution: 1. Check that keystore file was created in ...
(QB_NEW_EN)
[grammar] ~439-~439: There might be a mistake here.
Context: ...properly signed Solution: 1. Check that keystore file was created in "Setup And...
(QB_NEW_EN)
[grammar] ~450-~450: There might be a mistake here.
Context: ...nt fingerprints between local and GitHub Cause: GitHub is using different keyst...
(QB_NEW_EN)
[grammar] ~452-~452: There might be a mistake here.
Context: ...nt keystore or credentials Solution: 1. Verify base64 keystore decodes to identi...
(QB_NEW_EN)
[grammar] ~453-~453: There might be a mistake here.
Context: ...on**: 1. Verify base64 keystore decodes to identical file 2. Check all GitHub secr...
(QB_NEW_EN)
[grammar] ~460-~460: There might be a mistake here.
Context: ...tices ### 🔐 Keep Credentials Secure - Never commit key.properties or keystore...
(QB_NEW_EN)
[grammar] ~461-~461: There might be a mistake here.
Context: ... or keystore files to your repository - Use GitHub Secrets for all sensitive valu...
(QB_NEW_EN)
[grammar] ~463-~463: There might be a mistake here.
Context: ...rets - Enable 2FA on your GitHub account ### 🛡️ Backup Your Keystore - Keep secure ba...
(QB_NEW_EN)
[grammar] ~466-~466: There might be a mistake here.
Context: ...account ### 🛡️ Backup Your Keystore - Keep secure backups of your keystore file...
(QB_NEW_EN)
[grammar] ~467-~467: There might be a mistake here.
Context: ... secure backups of your keystore file - Document your passwords in a password man...
(QB_NEW_EN)
[grammar] ~468-~468: There might be a mistake here.
Context: ...manager - Test your backups periodically ###
(QB_NEW_EN)
[grammar] ~470-~470: There might be a mistake here.
Context: ...kups periodically ###
(QB_NEW_EN)
[grammar] ~471-~471: There might be a mistake here.
Context: ...r keystore or passwords are compromised: 1. Immediately change GitHub secrets 2. Gen...
(QB_NEW_EN)
🪛 markdownlint-cli2 (0.17.2)
GITHUB_SECRETS_SETUP.md
128-128: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
217-217: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
371-371: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
384-384: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🪛 YAMLlint (1.37.1)
.github/workflows/main.yml
[error] 77-77: trailing spaces
(trailing-spaces)
[error] 80-80: trailing spaces
(trailing-spaces)
[error] 171-171: no new line character at the end of file
(new-line-at-end-of-file)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (2)
.gitignore (1)
39-41: Good: explicitly ignoring key.properties and CI keystore artifactExplicit paths for android/key.properties and android/app/upload-keystore.jks are correct and align with the new signing flow. This reduces accidental commits of secrets.
.github/workflows/main.yml (1)
115-123: Env block not used by Gradle; keep for future parity but rely on key.properties or env fallbackThe env block doesn’t affect Flutter/Gradle unless you read env vars in build.gradle. With the proposed Gradle fallback or key.properties write, this is fine. No change required, just calling it out.
Would you like me to open a follow-up PR to adopt only the env-based signing and drop the key.properties write in CI?
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…ly fall back to debug Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (6)
.github/workflows/main.yml (2)
140-158: Good move to real signature verification. Consider asserting the expected alias/cert hash.jarsigner + apksigner is the right approach. For stronger guarantees, optionally fail if the cert subject/sha256 digest doesn’t match the expected production key.
You can extend the step like this:
echo "Verifying with jarsigner..." jarsigner -verify -verbose -certs build/app/outputs/flutter-apk/app-release.apk | sed -n '1,120p' # Try apksigner if available to print certs/fingerprint APKSIGNER=$(find "$ANDROID_HOME"/build-tools -name apksigner -type f | sort -V | tail -1 || true) if [ -n "$APKSIGNER" ]; then echo "Verifying with apksigner..." - "$APKSIGNER" verify --print-certs build/app/outputs/flutter-apk/app-release.apk + "$APKSIGNER" verify --print-certs build/app/outputs/flutter-apk/app-release.apk + # Optional strict check: ensure cert digest matches expected + if [ -n "${{ secrets.ANDROID_SIGNING_CERT_SHA256 }}" ]; then + "$APKSIGNER" verify --print-certs build/app/outputs/flutter-apk/app-release.apk | \ + awk '/Signer #1 certificate SHA-256 digest:/ {print $NF}' | \ + grep -Fq "${{ secrets.ANDROID_SIGNING_CERT_SHA256 }}" || { + echo "❌ APK signed with unexpected certificate"; exit 1; + } + fi fi
181-185: Also remove key.properties; add trailing newline (linters).
- Clean up android/key.properties to avoid lingering secrets in the workspace.
- Add a newline at EOF to satisfy linters.
- name: Cleanup Keystore if: always() && steps.should_build.outputs.SHOULD_BUILD == 'true' run: | # Remove keystore file for security - rm -f android/app/upload-keystore.jks + rm -f android/app/upload-keystore.jks android/key.properties +android/app/build.gradle (4)
15-25: File-based detection looks good; ensure path convention is module-relative.Given the workflow writes android/key.properties and the keystore at android/app/upload-keystore.jks, ensure key.properties uses storeFile=upload-keystore.jks (module-relative). If users put app/upload-keystore.jks, file(...) from the app module resolves to android/app/app/..., which will fail. Align docs/examples accordingly.
27-39: Trim env values and reject placeholders for robustness.Minor hardening: trim env strings and optionally reject obvious placeholders or empty strings.
- hasKeystoreFromEnv = req.every { System.getenv(it) } + hasKeystoreFromEnv = req.every { (System.getenv(it) ?: "").trim() } if (hasKeystoreFromEnv) { envKeystore = [ - keyAlias : System.getenv('ANDROID_KEY_ALIAS'), - keyPassword : System.getenv('ANDROID_KEY_PASSWORD'), - storePassword: System.getenv('ANDROID_KEYSTORE_PASSWORD'), - storeFile : System.getenv('ANDROID_KEYSTORE_FILE'), + keyAlias : System.getenv('ANDROID_KEY_ALIAS')?.trim(), + keyPassword : System.getenv('ANDROID_KEY_PASSWORD')?.trim(), + storePassword: System.getenv('ANDROID_KEYSTORE_PASSWORD')?.trim(), + storeFile : System.getenv('ANDROID_KEYSTORE_FILE')?.trim(), ] + def invalids = ['REPLACE_ME_KEY_ALIAS','${ANDROID_KEY_ALIAS}'] as Set + hasKeystoreFromEnv = envKeystore.keyAlias && !invalids.contains(envKeystore.keyAlias) }
64-80: Signing config wiring is correct; double-check storeFile path semantics.
- For file-based path you use file(keystoreProperties['storeFile']) which expects a module-relative path. This will work if key.properties uses storeFile=upload-keystore.jks (see workflow fix).
- For env-based path, file(envKeystore.storeFile) likewise expects upload-keystore.jks.
If you prefer supporting rootProject-relative paths in key.properties (e.g., app/upload-keystore.jks), change only the file-based branch:
- storeFile file(keystoreProperties['storeFile']) + storeFile rootProject.file(keystoreProperties['storeFile'])Pick one convention and reflect it in documentation/sample.
94-100: Reasonable debug fallback; ensure releases aren’t published with it.The warning helps. As a guard, you can fail the job if this is a tagged release and hasKeystore=false to prevent accidental publishing with a debug cert.
release { if (hasKeystore) { signingConfig signingConfigs.release } else { // Fallback to debug signing for development signingConfig signingConfigs.debug logger.warn("Release build using debug signing - keystore not available") + if (System.getenv("GITHUB_REF")?.startsWith("refs/tags/")) { + throw new GradleException("Refusing to build a tagged release without a release keystore") + } } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
.github/workflows/main.yml(3 hunks)android/app/build.gradle(3 hunks)
🧰 Additional context used
🪛 actionlint (1.7.7)
.github/workflows/main.yml
86-86: could not parse as YAML: yaml: line 86: could not find expected ':'
(syntax-check)
🪛 YAMLlint (1.37.1)
.github/workflows/main.yml
[error] 87-87: syntax error: could not find expected ':'
(syntax)
[error] 185-185: no new line character at the end of file
(new-line-at-end-of-file)
🔇 Additional comments (3)
.github/workflows/main.yml (1)
125-139: Confirm release signing of the generated app bundleThe env block is correct and should remain using the module-relative path. These vars mirror GITHUB_ENV and provide Gradle with a fallback. Ensure ANDROID_KEYSTORE_FILE is still set to upload-keystore.jks to match your key.properties change above.
To validate that your app bundle is signed with the release keystore (rather than relying on an unset ANDROID_HOME), run this manually:
#!/bin/bash set -euo pipefail # Path to the built AAB AAB="build/app/outputs/bundle/release/app-release.aab" # Use the JDK’s jarsigner to verify the signature JARSIGNER=$(which jarsigner) if [[ -z "$JARSIGNER" ]]; then echo "jarsigner not found; ensure JDK is installed and on PATH" exit 1 fi # Verify and print certificate info "$JARSIGNER" -verify -verbose -certs "$AAB" | sed -n '1,120p'You should see your keystore alias and certificate fingerprint in the output—confirm these match your release key.
android/app/build.gradle (2)
8-14: Solid scaffolding for dual-source signing; names read well.The separation into hasKeystoreFromFile/env and envKeystore map is clear and maintainable.
41-41: LGTM.Clear boolean for downstream blocks.
| - name: Setup Android Keystore | ||
| if: steps.should_build.outputs.SHOULD_BUILD == 'true' | ||
| run: | | ||
| # Create the keystore file from the secret | ||
| echo "${{ secrets.ANDROID_KEYSTORE_FILE }}" | base64 --decode > android/app/upload-keystore.jks | ||
|
|
||
| # Set environment variables for signing | ||
| echo "ANDROID_KEYSTORE_PASSWORD=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" >> $GITHUB_ENV | ||
| echo "ANDROID_KEY_PASSWORD=${{ secrets.ANDROID_KEY_PASSWORD }}" >> $GITHUB_ENV | ||
| echo "ANDROID_KEY_ALIAS=${{ secrets.ANDROID_KEY_ALIAS }}" >> $GITHUB_ENV | ||
| echo "ANDROID_KEYSTORE_FILE=upload-keystore.jks" >> $GITHUB_ENV | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Avoid secrets appearing in command log; prefer shell vars over GitHub expressions.
Placing ${{ secrets.* }} inside the here-doc body means the literal secret values appear in the step’s echoed script. While GH masks them, using shell variables (export ...) keeps logs clean and reduces risk.
The diff in my previous comment switches to export + printf, which addresses this.
replaces the heredoc syntax (cat > file <<EOF) with individual echo commands. This avoids the YAML parsing issue because the content is no longer treated as nested YAML structure. Each line is now a simple shell command that the YAML parser can handle correctly.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
.github/workflows/main.yml (1)
72-88: Fix same-step env expansion; stop leaking secrets in echoed commands; write module-relative storeFile; harden file perms
- ${{ env.ANDROID_KEYSTORE_FILE }} won’t be available within the same step that writes to $GITHUB_ENV, so storeFile expands to an empty string, breaking Gradle’s lookup. Use shell exports for immediate use.
- Avoid embedding ${{ secrets.* }} directly in echo lines that appear in the step’s script; prefer shell vars to minimize the chance of secrets being revealed in logs.
- storeFile should be upload-keystore.jks (module-relative), not app/...
- Tighten permissions on the keystore and key.properties.
Apply:
- name: Setup Android Keystore if: steps.should_build.outputs.SHOULD_BUILD == 'true' run: | # Create the keystore file from the secret - echo "${{ secrets.ANDROID_KEYSTORE_FILE }}" | base64 --decode > android/app/upload-keystore.jks + echo "${{ secrets.ANDROID_KEYSTORE_FILE }}" | base64 --decode > android/app/upload-keystore.jks + chmod 600 android/app/upload-keystore.jks - - # Set environment variables for signing - echo "ANDROID_KEYSTORE_PASSWORD=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" >> $GITHUB_ENV - echo "ANDROID_KEY_PASSWORD=${{ secrets.ANDROID_KEY_PASSWORD }}" >> $GITHUB_ENV - echo "ANDROID_KEY_ALIAS=${{ secrets.ANDROID_KEY_ALIAS }}" >> $GITHUB_ENV - echo "ANDROID_KEYSTORE_FILE=upload-keystore.jks" >> $GITHUB_ENV - - # Write key.properties so Gradle can pick it up without extra config - echo "storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" > android/key.properties - echo "keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}" >> android/key.properties - echo "keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}" >> android/key.properties - echo "storeFile=app/${{ env.ANDROID_KEYSTORE_FILE }}" >> android/key.properties + # Export for immediate use and persist for subsequent steps + export ANDROID_KEYSTORE_PASSWORD='${{ secrets.ANDROID_KEYSTORE_PASSWORD }}' + export ANDROID_KEY_PASSWORD='${{ secrets.ANDROID_KEY_PASSWORD }}' + export ANDROID_KEY_ALIAS='${{ secrets.ANDROID_KEY_ALIAS }}' + export ANDROID_KEYSTORE_FILE='upload-keystore.jks' + echo "ANDROID_KEYSTORE_PASSWORD=$ANDROID_KEYSTORE_PASSWORD" >> $GITHUB_ENV + echo "ANDROID_KEY_PASSWORD=$ANDROID_KEY_PASSWORD" >> $GITHUB_ENV + echo "ANDROID_KEY_ALIAS=$ANDROID_KEY_ALIAS" >> $GITHUB_ENV + echo "ANDROID_KEYSTORE_FILE=$ANDROID_KEYSTORE_FILE" >> $GITHUB_ENV + + # Write key.properties so Gradle can pick it up (module-relative storeFile) + printf '%s\n' \ + "storePassword=$ANDROID_KEYSTORE_PASSWORD" \ + "keyPassword=$ANDROID_KEY_PASSWORD" \ + "keyAlias=$ANDROID_KEY_ALIAS" \ + "storeFile=upload-keystore.jks" \ + > android/key.properties + chmod 600 android/key.properties
🧹 Nitpick comments (3)
.github/workflows/main.yml (3)
120-128: Avoid re-plumbing secrets here; prefer env propagated via $GITHUB_ENV or rely on key.propertiesYou already persist these values in the previous step and also write key.properties. To reduce surface area of secret usage and duplication, either:
- reference env.* instead of secrets.* here, or
- drop the env block entirely and rely on key.properties in Gradle.
Minimal change:
env: - ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} - ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} - ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + ANDROID_KEYSTORE_PASSWORD: ${{ env.ANDROID_KEYSTORE_PASSWORD }} + ANDROID_KEY_PASSWORD: ${{ env.ANDROID_KEY_PASSWORD }} + ANDROID_KEY_ALIAS: ${{ env.ANDROID_KEY_ALIAS }} ANDROID_KEYSTORE_FILE: upload-keystore.jks
129-136: Same as above: reference env. or drop block to rely on key.properties*Keep a single source of truth for signing inputs to avoid drift.
env: - ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} - ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} - ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + ANDROID_KEYSTORE_PASSWORD: ${{ env.ANDROID_KEYSTORE_PASSWORD }} + ANDROID_KEY_PASSWORD: ${{ env.ANDROID_KEY_PASSWORD }} + ANDROID_KEY_ALIAS: ${{ env.ANDROID_KEY_ALIAS }} ANDROID_KEYSTORE_FILE: upload-keystore.jks
183-183: Add trailing newline to satisfy lintersStatic analysis flags “no new line at end of file.” Add a newline at EOF.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
.github/workflows/main.yml(3 hunks)
🧰 Additional context used
🪛 YAMLlint (1.37.1)
.github/workflows/main.yml
[error] 183-183: no new line character at the end of file
(new-line-at-end-of-file)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
Provided comprehensive APK signature verification instructions that align with the current CI implementation
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
GITHUB_SECRETS_SETUP.md (1)
382-447: Stronger signing verification: great improvement and aligned with prior feedbackYou replaced the weak META-INF check with jarsigner/apksigner verification and fingerprint comparison. This addresses the earlier concern precisely and will prevent false positives from debug-signed builds. Looks good.
🧹 Nitpick comments (7)
GITHUB_SECRETS_SETUP.md (7)
397-407: Use proper headings and clarify jarsigner’s limitation (V1-only) — prefer apksigner for V2/V3/V4Two tweaks for accuracy and linting:
- Convert the bold “Step 1/Step 2” lines into real Markdown headings.
- Explicitly note that jarsigner validates only V1 (JAR) signatures; modern Android APKs are validated fully by apksigner (V2/V3/V4). Keep jarsigner as a fallback.
Apply this diff:
-**Step 1: Verify signature integrity with jarsigner** +##### Step 1: Verify signature integrity with jarsigner @@ -jarsigner -verify -verbose -certs build/app/outputs/flutter-apk/app-release.apk +jarsigner -verify -verbose -certs build/app/outputs/flutter-apk/app-release.apk @@ -jarsigner -verify -verbose -certs -strict build/app/outputs/flutter-apk/app-release.apk +jarsigner -verify -verbose -certs -strict build/app/outputs/flutter-apk/app-release.apk + +Note: jarsigner validates only V1 (JAR) signatures. Many Android builds use V2/V3/V4. Prefer apksigner for full Android signature verification when available. @@ -**Step 2: Alternative verification with apksigner (if available)** +##### Step 2: Verification with apksigner (preferred, if available)Also applies to: 415-425
417-425: Broaden apksigner discovery to support ANDROID_SDK_ROOT and PATHRight now you only search $ANDROID_HOME. Many setups export ANDROID_SDK_ROOT or have apksigner on PATH. Improve robustness.
Apply this diff:
-# Find apksigner in your Android SDK build-tools -find $ANDROID_HOME/build-tools -name apksigner -type f | sort -V | tail -1 +# Find apksigner (prefer PATH; fallback to SDK env vars) +APKSIGNER_BIN="$(command -v apksigner || true)" +if [ -z "$APKSIGNER_BIN" ]; then + SDK_ROOT="${ANDROID_SDK_ROOT:-$ANDROID_HOME}" + APKSIGNER_BIN="$(find "$SDK_ROOT"/build-tools -name apksigner -type f 2>/dev/null | sort -V | tail -1 || true)" +fi +echo "${APKSIGNER_BIN:-apksigner not found}" @@ -# Verify with apksigner -apksigner verify --print-certs build/app/outputs/flutter-apk/app-release.apk +# Verify with apksigner +"$APKSIGNER_BIN" verify --print-certs build/app/outputs/flutter-apk/app-release.apk @@ -apksigner verify --verbose build/app/outputs/flutter-apk/app-release.apk +"$APKSIGNER_BIN" verify --verbose build/app/outputs/flutter-apk/app-release.apk
128-133: Add fenced code languages to satisfy markdownlint (MD040) and improve readabilitySeveral code fences lack a language hint. Use text/properties to appease linters and highlight appropriately.
Apply this diff:
-``` +```text Keystore File: android/app/upload-keystore.jks Store Password: [the password you chose] Key Password: [the key password you chose] Key Alias: [the alias you chose]@@
-+text
Alias name: youraliasname
Creation date: Aug 13, 2025
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Your App Name, OU=Organization, O=Company...
Valid from: Wed Aug 13 18:47:47 UYT 2025 until: Sun Dec 29 18:47:47 UYT 2052
Certificate fingerprints:
SHA1: FD:DB:64:E4:7C:60:4D:BD:27:9F:A7:C2:D7:16:AB:6B:40:74:9A:F3
SHA256: 67:6F:67:26:76:D3:59:3A:C5:2F:01:7C:58:1C:50:C0:8B...@@ -``` +```text Setting environment variables ANDROID_KEYSTORE_PASSWORD=*** ANDROID_KEY_PASSWORD=*** ANDROID_KEY_ALIAS=*** ANDROID_KEYSTORE_FILE=upload-keystore.jks@@
-+text
✅ APK built successfully
Verifying with jarsigner...
jar verified.
✅ Certificate details displayed@@ -``` +```text jar verified. Certificate details displayed...Also applies to: 217-229, 371-377, 384-389, 411-413 --- `77-77`: **Typo: “Detal Amacuro” → “Delta Amacuro”** Minor geographical correction. Apply this diff: ```diff -- **ST (State)**: Your state/province (e.g., `Detal Amacuro`) +- **ST (State)**: Your state/province (e.g., `Delta Amacuro`)
324-353: Prefer Environment-scoped secrets with protections over repo-wide secretsFor production signing material, use GitHub Actions Environments with required reviewers, branch protections, and restricted access. This reduces blast radius compared to repository secrets.
Apply this diff:
### Navigate to GitHub Secrets @@ -3. In the left sidebar, click **Secrets and variables** -4. Click **Actions** +3. In the left sidebar, click **Environments** (preferred for production) and create an environment like `release` with required reviewers and branch protections. Add your secrets under this environment. +4. Alternatively, click **Secrets and variables** → **Actions** to add repository-level secrets (broader scope).
535-542: Add .gitignore guidance to prevent accidental commits of sensitive filesEnsure keystore and key.properties never get committed.
Apply this diff:
### 🔐 Keep Credentials Secure - Never commit `key.properties` or keystore files to your repository - Use GitHub Secrets for all sensitive values - Regularly audit who has access to repository secrets - Enable 2FA on your GitHub account + +Add these to your `.gitignore`: +```gitignore +# Android signing artifacts +android/key.properties +android/app/*.jks +android/app/*.keystore +android/app/*.p12 +```
543-552: Mention Play App Signing best practices and recovery pathHelpful context for teams using Google Play App Signing: treat this keystore as the “upload key,” and document recovery/reset via Play Console if compromised.
Apply this diff:
### 🛡️ Backup Your Keystore - Keep secure backups of your keystore file - Document your passwords in a password manager - Test your backups periodically ### ⚠️ If Compromised If you suspect your keystore or passwords are compromised: 1. Immediately change GitHub secrets 2. Generate new keystore for future releases 3. Review GitHub Actions history for unauthorized builds +4. If you use Google Play App Signing, rotate the upload key via Play Console and reconfigure CI with the new upload key. Document the new fingerprints.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
GITHUB_SECRETS_SETUP.md(1 hunks)
🧰 Additional context used
🪛 LanguageTool
GITHUB_SECRETS_SETUP.md
[grammar] ~7-~7: There might be a mistake here.
Context: ...Guide Achieves By the end, you'll have: - ✅ Properly signed APKs built in GitHub A...
(QB_NEW_EN)
[grammar] ~8-~8: There might be a mistake here.
Context: ...erly signed APKs built in GitHub Actions - ✅ Consistent signing certificates across...
(QB_NEW_EN)
[grammar] ~9-~9: There might be a mistake here.
Context: ...t signing certificates across all builds - ✅ Secure credential storage in GitHub Se...
(QB_NEW_EN)
[grammar] ~10-~10: There might be a mistake here.
Context: ...ure credential storage in GitHub Secrets - ✅ APK installation that works without un...
(QB_NEW_EN)
[grammar] ~21-~21: There might be a mistake here.
Context: ...hat You Need Before starting, you need: - ✅ Android keystore file (.jks or `.key...
(QB_NEW_EN)
[grammar] ~50-~50: There might be a mistake here.
Context: ...-key.jks` If you find keystore files: - ✅ You have a keystore → Skip to "Cre...
(QB_NEW_EN)
[grammar] ~53-~53: There might be a mistake here.
Context: ...found** → These won't work for release builds, create a new one **If no keystore files...
(QB_NEW_EN)
[grammar] ~53-~53: There might be a mistake here.
Context: ...ork for release builds, create a new one If no keystore files found: - ❌ **No ke...
(QB_NEW_EN)
[grammar] ~55-~55: There might be a mistake here.
Context: ...a new one If no keystore files found: - ❌ No keystore exists → Continue to "...
(QB_NEW_EN)
[grammar] ~66-~66: There might be a mistake here.
Context: ...ds (choose strong, memorable passwords):** - Store Password: Protects the keystore ...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...ssword**: Protects the signing key (can be same as store password) Key Alias:...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...: Protects the signing key (can be same as store password) Key Alias: A name ...
(QB_NEW_EN)
[grammar] ~72-~72: There might be a mistake here.
Context: ...tion (these identify your organization):** - CN (Common Name): Your app name or org...
(QB_NEW_EN)
[grammar] ~73-~73: There might be a mistake here.
Context: ...ganization (e.g., MyApp, My Company) - OU (Organizational Unit): Your departm...
(QB_NEW_EN)
[grammar] ~74-~74: There might be a mistake here.
Context: ...g., Mobile Development, Engineering) - O (Organization): Your company name (e...
(QB_NEW_EN)
[grammar] ~75-~75: There might be a mistake here.
Context: ...our company name (e.g., MyCompany Inc) - L (Locality): Your city (e.g., `Tucupi...
(QB_NEW_EN)
[grammar] ~76-~76: There might be a mistake here.
Context: ...ocality)**: Your city (e.g., Tucupita) - ST (State): Your state/province (e.g.,...
(QB_NEW_EN)
[grammar] ~77-~77: There might be a mistake here.
Context: ...r state/province (e.g., Detal Amacuro) - C (Country): Your country code (e.g., ...
(QB_NEW_EN)
[grammar] ~100-~100: There might be a mistake here.
Context: ...OUR_COUNTRY" ``` Command Explanation: - -keystore upload-keystore.jks: Creates file named `upload-keystore.jk...
(QB_NEW_EN)
[grammar] ~101-~101: There might be a mistake here.
Context: ...pload-keystore.jks: Creates file named upload-keystore.jks--keyalg RSA -keysize 2048`: Uses RSA encryption with 2048-bit key ...
(QB_NEW_EN)
[grammar] ~102-~102: There might be a mistake here.
Context: ...SA encryption with 2048-bit key (secure) - -validity 10000: Certificate valid for ~27 years - `-al...
(QB_NEW_EN)
[grammar] ~103-~103: There might be a mistake here.
Context: ... 10000: Certificate valid for ~27 years - -alias YOUR_CHOSEN_ALIAS`: Replace with your chosen alias name - ...
(QB_NEW_EN)
[grammar] ~104-~104: There might be a mistake here.
Context: ...AS: Replace with your chosen alias name - -storepass / -keypass: Replace with your chosen passwords - ...
(QB_NEW_EN)
[grammar] ~105-~105: There might be a mistake here.
Context: ...ass: Replace with your chosen passwords - -dname`: Replace with your organization informa...
(QB_NEW_EN)
[grammar] ~135-~135: There might be a mistake here.
Context: ... ``` Store this information securely: - Add to your password manager - Write in ...
(QB_NEW_EN)
[grammar] ~186-~186: There might be a mistake here.
Context: ...ad-keystore.jks ``` File explanation: - storePassword: The password that protects your keysto...
(QB_NEW_EN)
[grammar] ~187-~187: There might be a mistake here.
Context: ...assword that protects your keystore file - keyPassword: The password that protects your signin...
(QB_NEW_EN)
[grammar] ~188-~188: There might be a mistake here.
Context: ...assword that protects your signing key (often same as store password) - keyAlias: T...
(QB_NEW_EN)
[grammar] ~188-~188: There might be a mistake here.
Context: ...t protects your signing key (often same as store password) - keyAlias: The name ...
(QB_NEW_EN)
[grammar] ~188-~188: There might be a mistake here.
Context: ...gning key (often same as store password) - keyAlias: The name of your signing key within th...
(QB_NEW_EN)
[grammar] ~189-~189: There might be a mistake here.
Context: ... of your signing key within the keystore - storeFile: The filename of your keystore (should ...
(QB_NEW_EN)
[grammar] ~210-~210: There might be a mistake here.
Context: ...ASSWORD ``` Replace the placeholders: - YOUR_KEY_ALIAS: Use the value from your `key.propertie...
(QB_NEW_EN)
[grammar] ~230-~230: There might be a mistake here.
Context: ... GitHub builds use the same certificate. ### Common Errors and Solutions #### ❌ "keys...
(QB_NEW_EN)
[grammar] ~235-~235: There might be a mistake here.
Context: ...ur store password is wrong. Check these: 1. Verify the password in key.properties ...
(QB_NEW_EN)
[grammar] ~241-~241: There might be a mistake here.
Context: ...ist" Your alias name is wrong. Fix this: 1. List all aliases in your keystore: ...
(QB_NEW_EN)
[grammar] ~250-~250: There might be a mistake here.
Context: ... file not found" The file path is wrong: 1. Check the file exists: `ls -la app/upl...
(QB_NEW_EN)
[grammar] ~251-~251: There might be a mistake here.
Context: ...is wrong: 1. Check the file exists: ls -la app/upload-keystore.jks 2. Look for keystore files: `find . -name...
(QB_NEW_EN)
[grammar] ~252-~252: There might be a mistake here.
Context: ...re.jks2. **Look for keystore files**:find . -name ".jks" -o -name ".keystore"` 3. Update the storeFile path in key.prope...
(QB_NEW_EN)
[grammar] ~282-~282: There might be a mistake here.
Context: ... values** (you'll need them for GitHub): 1. Store Password: storePassword= value...
(QB_NEW_EN)
[grammar] ~283-~283: There might be a mistake here.
Context: ...Store Password**: storePassword= value 2. Key Password: keyPassword= value 3...
(QB_NEW_EN)
[grammar] ~284-~284: There might be a mistake here.
Context: .... Key Password: keyPassword= value 3. Key Alias: keyAlias= value 4. **Stor...
(QB_NEW_EN)
[grammar] ~285-~285: There might be a mistake here.
Context: ...ue 3. Key Alias: keyAlias= value 4. Store File: storeFile= value (usuall...
(QB_NEW_EN)
[grammar] ~335-~335: There might be a mistake here.
Context: ...secret**: #### 1. ANDROID_KEYSTORE_FILE - Name: ANDROID_KEYSTORE_FILE - **Valu...
(QB_NEW_EN)
[grammar] ~336-~336: There might be a mistake here.
Context: ...## 1. ANDROID_KEYSTORE_FILE - Name: ANDROID_KEYSTORE_FILE - Value: Your very long base64 string fr...
(QB_NEW_EN)
[grammar] ~339-~339: There might be a mistake here.
Context: ...tep 2 #### 2. ANDROID_KEYSTORE_PASSWORD - Name: ANDROID_KEYSTORE_PASSWORD - **...
(QB_NEW_EN)
[grammar] ~340-~340: There might be a mistake here.
Context: .... ANDROID_KEYSTORE_PASSWORD - Name: ANDROID_KEYSTORE_PASSWORD - Value: The storePassword value from ...
(QB_NEW_EN)
[grammar] ~343-~343: There might be a mistake here.
Context: ...properties #### 3. ANDROID_KEY_PASSWORD - Name: ANDROID_KEY_PASSWORD - **Val...
(QB_NEW_EN)
[grammar] ~344-~344: There might be a mistake here.
Context: ...### 3. ANDROID_KEY_PASSWORD - Name: ANDROID_KEY_PASSWORD - Value: The keyPassword value from yo...
(QB_NEW_EN)
[grammar] ~347-~347: There might be a mistake here.
Context: ...ey.properties #### 4. ANDROID_KEY_ALIAS - Name: ANDROID_KEY_ALIAS - Value:...
(QB_NEW_EN)
[grammar] ~348-~348: There might be a mistake here.
Context: ... #### 4. ANDROID_KEY_ALIAS - Name: ANDROID_KEY_ALIAS - Value: The keyAlias value from your ...
(QB_NEW_EN)
[grammar] ~369-~369: There might be a mistake here.
Context: ...ions log: #### ✅ Setup Android Keystore Should show: ``` Setting environment var...
(QB_NEW_EN)
[grammar] ~379-~379: There might be a mistake here.
Context: ...pload-keystore.jks ``` #### ✅ Build APK Should complete without errors about key...
(QB_NEW_EN)
[grammar] ~382-~382: There might be a mistake here.
Context: ...e or signing. #### ✅ Verify APK Signing Should show: ``` ✅ APK built successfull...
(QB_NEW_EN)
[grammar] ~443-~443: There might be a mistake here.
Context: ...rep "SHA256:" ``` Compare and verify: - Both keystore and APK should show identi...
(QB_NEW_EN)
[grammar] ~457-~457: There might be a mistake here.
Context: ...the build What the CI checks prevent: - Unsigned APKs reaching production - APKs...
(QB_NEW_EN)
[grammar] ~458-~458: There might be a mistake here.
Context: ...t:** - Unsigned APKs reaching production - APKs signed with debug certificates - AP...
(QB_NEW_EN)
[grammar] ~459-~459: There might be a mistake here.
Context: ...on - APKs signed with debug certificates - APKs signed with wrong/compromised certi...
(QB_NEW_EN)
[grammar] ~460-~460: There might be a mistake here.
Context: ...gned with wrong/compromised certificates - Signature corruption during build proces...
(QB_NEW_EN)
[grammar] ~461-~461: There might be a mistake here.
Context: ...sed certificates - Signature corruption during build process #### Troubleshooting Sig...
(QB_NEW_EN)
[grammar] ~466-~466: There might be a mistake here.
Context: ...gnature is invalid or corrupted - Check that keystore file exists and is not corrupt...
(QB_NEW_EN)
[grammar] ~471-~471: There might be a mistake here.
Context: ...gned with different certificate - Check that correct keystore file is being used - V...
(QB_NEW_EN)
[grammar] ~475-~475: There might be a mistake here.
Context: ...t found**: Keystore or APK access issues - Verify keystore file path and permission...
(QB_NEW_EN)
[grammar] ~492-~492: There might be a mistake here.
Context: ...keystore.jks ``` The build system will: - Use your keystore when available locally...
(QB_NEW_EN)
[grammar] ~494-~494: There might be a mistake here.
Context: ...le locally - Fall back to debug signing if keystore is missing - Show warnings abo...
(QB_NEW_EN)
[grammar] ~501-~501: There might be a mistake here.
Context: ...❌ "keystore not found" in GitHub Actions Cause: Base64 keystore secret is wrong...
(QB_NEW_EN)
[grammar] ~503-~503: There might be a mistake here.
Context: ...secret is wrong or missing Solution: 1. Regenerate base64: `base64 -w 0 android/...
(QB_NEW_EN)
[grammar] ~504-~504: There might be a mistake here.
Context: ...ng Solution: 1. Regenerate base64: base64 -w 0 android/app/upload-keystore.jks 2. Update ANDROID_KEYSTORE_FILE secret wi...
(QB_NEW_EN)
[grammar] ~507-~507: There might be a mistake here.
Context: ...### ❌ "Wrong password" in GitHub Actions Cause: Password secrets don't match yo...
(QB_NEW_EN)
[grammar] ~509-~509: There might be a mistake here.
Context: ... don't match your keystore Solution: 1. Double-check the values in your local `k...
(QB_NEW_EN)
[grammar] ~511-~511: There might be a mistake here.
Context: ...ey.properties` 2. Update GitHub secrets with exact same values 3. Ensure no extra sp...
(QB_NEW_EN)
[style] ~511-~511: ‘exact same’ might be wordy. Consider a shorter alternative.
Context: ...operties` 2. Update GitHub secrets with exact same values 3. Ensure no extra spaces or cha...
(EN_WORDINESS_PREMIUM_EXACT_SAME)
[grammar] ~516-~516: There might be a mistake here.
Context: ...PK wasn't properly signed Solution: 1. Check that keystore file was created in ...
(QB_NEW_EN)
[grammar] ~517-~517: There might be a mistake here.
Context: ...properly signed Solution: 1. Check that keystore file was created in "Setup And...
(QB_NEW_EN)
[grammar] ~528-~528: There might be a mistake here.
Context: ...nt fingerprints between local and GitHub Cause: GitHub is using different keyst...
(QB_NEW_EN)
[grammar] ~530-~530: There might be a mistake here.
Context: ...nt keystore or credentials Solution: 1. Verify base64 keystore decodes to identi...
(QB_NEW_EN)
[grammar] ~538-~538: There might be a mistake here.
Context: ...tices ### 🔐 Keep Credentials Secure - Never commit key.properties or keystore...
(QB_NEW_EN)
[grammar] ~539-~539: There might be a mistake here.
Context: ... or keystore files to your repository - Use GitHub Secrets for all sensitive valu...
(QB_NEW_EN)
[grammar] ~541-~541: There might be a mistake here.
Context: ...rets - Enable 2FA on your GitHub account ### 🛡️ Backup Your Keystore - Keep secure ba...
(QB_NEW_EN)
[grammar] ~544-~544: There might be a mistake here.
Context: ...account ### 🛡️ Backup Your Keystore - Keep secure backups of your keystore file...
(QB_NEW_EN)
[grammar] ~545-~545: There might be a mistake here.
Context: ... secure backups of your keystore file - Document your passwords in a password man...
(QB_NEW_EN)
[grammar] ~546-~546: There might be a mistake here.
Context: ...manager - Test your backups periodically ###
(QB_NEW_EN)
[grammar] ~548-~548: There might be a mistake here.
Context: ...kups periodically ###
(QB_NEW_EN)
[grammar] ~549-~549: There might be a mistake here.
Context: ...r keystore or passwords are compromised: 1. Immediately change GitHub secrets 2. Gen...
(QB_NEW_EN)
🪛 markdownlint-cli2 (0.17.2)
GITHUB_SECRETS_SETUP.md
128-128: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
217-217: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
371-371: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
384-384: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
397-397: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
410-410: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
415-415: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (1)
GITHUB_SECRETS_SETUP.md (1)
448-456: CI Workflow Signing and Cleanup VerifiedAll documented signing verification and cleanup steps are present and correctly enforced in
.github/workflows/main.yml:
- Keystore decoding and writing
•echo "${{ secrets.ANDROID_KEYSTORE_FILE }}" | base64 --decode > android/app/upload-keystore.jks(line 76)
• Environment variables andkey.propertiesare generated for Gradle (lines 80–88)- Signature verification with failure on non-zero exit codes
•set -eis enabled before runningjarsigner -verify -verbose -certs build/app/outputs/flutter-apk/app-release.apk(lines 144–146)
•apksigner verify --print-certsfallback is invoked ifapksigneris found in build-tools (lines 147–149)- Cleanup of keystore artifacts
•rm -f android/app/upload-keystore.jksin the “Cleanup Keystore” step (line 183)- All steps properly gated by
steps.should_build.outputs.SHOULD_BUILD == 'true', with cleanup usingif: always()to ensure removal even on failure (lines 56–74, 180–182)No further changes required.
…ionally assert certificate fingerprint Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
.github/workflows/main.yml (1)
72-89: Fix env interpolation bug and wrong storeFile path in key.properties (leads to unsigned/mis-signed builds).
- Using ${{ env.ANDROID_KEYSTORE_FILE }} inside the same run block won’t see values written to $GITHUB_ENV earlier in that block; it expands before the step runs, resulting in an empty value. That writes
storeFile=app/and breaks signing resolution.- For Gradle’s typical Flutter setup (key.properties at android/, file() resolved from app module), storeFile should be module-relative:
upload-keystore.jks(notapp/...).- Also prefer shell variables over inline ${{ secrets.* }} in echoes to keep logs cleaner.
Apply this refactor to make the step robust and self‑contained:
- name: Setup Android Keystore if: steps.should_build.outputs.SHOULD_BUILD == 'true' run: | # Create the keystore file from the secret echo "${{ secrets.ANDROID_KEYSTORE_FILE }}" | base64 --decode > android/app/upload-keystore.jks - # Set environment variables for signing - echo "ANDROID_KEYSTORE_PASSWORD=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" >> $GITHUB_ENV - echo "ANDROID_KEY_PASSWORD=${{ secrets.ANDROID_KEY_PASSWORD }}" >> $GITHUB_ENV - echo "ANDROID_KEY_ALIAS=${{ secrets.ANDROID_KEY_ALIAS }}" >> $GITHUB_ENV - echo "ANDROID_KEYSTORE_FILE=upload-keystore.jks" >> $GITHUB_ENV - - # Write key.properties so Gradle can pick it up without extra config - echo "storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" > android/key.properties - echo "keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}" >> android/key.properties - echo "keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}" >> android/key.properties - echo "storeFile=app/${{ env.ANDROID_KEYSTORE_FILE }}" >> android/key.properties + # Export for immediate use in this step and persist for later steps + export ANDROID_KEYSTORE_PASSWORD='${{ secrets.ANDROID_KEYSTORE_PASSWORD }}' + export ANDROID_KEY_PASSWORD='${{ secrets.ANDROID_KEY_PASSWORD }}' + export ANDROID_KEY_ALIAS='${{ secrets.ANDROID_KEY_ALIAS }}' + export ANDROID_KEYSTORE_FILE='upload-keystore.jks' + { + echo "ANDROID_KEYSTORE_PASSWORD=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" + echo "ANDROID_KEY_PASSWORD=${{ secrets.ANDROID_KEY_PASSWORD }}" + echo "ANDROID_KEY_ALIAS=${{ secrets.ANDROID_KEY_ALIAS }}" + echo "ANDROID_KEYSTORE_FILE=upload-keystore.jks" + } >> "$GITHUB_ENV" + + # Write key.properties (module-relative storeFile) without echoing secrets in logs + printf '%s\n' \ + "storePassword=$ANDROID_KEYSTORE_PASSWORD" \ + "keyPassword=$ANDROID_KEY_PASSWORD" \ + "keyAlias=$ANDROID_KEY_ALIAS" \ + "storeFile=upload-keystore.jks" \ + > android/key.properties + + # Restrict permissions + chmod 600 android/app/upload-keystore.jks android/key.properties
🧹 Nitpick comments (8)
.github/workflows/main.yml (3)
123-128: Optional: drop per-step env blocks; rely on $GITHUB_ENV to reduce duplication.You already persist these vars via $GITHUB_ENV in the setup step. The extra env sections here duplicate secrets injection and increase surface area in logs. Consider removing them for simplicity.
- name: Build APK if: steps.should_build.outputs.SHOULD_BUILD == 'true' - run: flutter build apk --release --dart-define=APP_VERSION=${{ env.VERSION }} --dart-define=GIT_COMMIT=${{ github.sha }} - env: - ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} - ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} - ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} - ANDROID_KEYSTORE_FILE: upload-keystore.jks + run: flutter build apk --release --dart-define=APP_VERSION=${{ env.VERSION }} --dart-define=GIT_COMMIT=${{ github.sha }} @@ - name: Build appBundle if: steps.should_build.outputs.SHOULD_BUILD == 'true' - run: flutter build appbundle --release --dart-define=APP_VERSION=${{ env.VERSION }} --dart-define=GIT_COMMIT=${{ github.sha }} - env: - ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} - ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} - ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} - ANDROID_KEYSTORE_FILE: upload-keystore.jks + run: flutter build appbundle --release --dart-define=APP_VERSION=${{ env.VERSION }} --dart-define=GIT_COMMIT=${{ github.sha }}Also applies to: 132-136
138-169: Normalize fingerprint formats before comparison; handle separators and casing.apksigner prints SHA‑256 with colons/spaces; teams often store the secret with or without separators and in mixed case. Normalize both sides for reliable matching.
# Optional: enforce signer fingerprint if provided if [ -n "${{ secrets.ANDROID_CERT_SHA256 }}" ]; then - FPR=$("$APKSIGNER" verify --print-certs "$APK" 2>/dev/null \ - | awk -F': ' '/SHA-256 digest/{print $2}' | tr -d ' ') - if [ "$FPR" != "${{ secrets.ANDROID_CERT_SHA256 }}" ]; then + FPR=$("$APKSIGNER" verify --print-certs "$APK" 2>/dev/null \ + | awk -F': ' '/SHA-256 digest/{print $2}' \ + | tr -d ' :\n' \ + | tr '[:lower:]' '[:upper:]') + EXPECTED=$(printf '%s' '${{ secrets.ANDROID_CERT_SHA256 }}' \ + | tr -d ' :\n' \ + | tr '[:lower:]' '[:upper:]') + if [ "$FPR" != "$EXPECTED" ]; then echo "❌ APK cert SHA-256 mismatch" exit 1 fi fiIf you want this to run even when apksigner is missing, we can add a fallback that parses jarsigner output (less ideal for V2/V3) or compare against the keystore via keytool while it’s still on disk. Happy to wire that if needed.
196-196: Add missing newline at EOF to satisfy linters.YAMLlint flagged “no new line character at the end of file”. Add a trailing newline.
GITHUB_SECRETS_SETUP.md (5)
126-139: Mark fenced block language; tighten “secure storage” guidance.
- Add a language hint to satisfy markdownlint (MD040). “text” works here.
- The storage guidance looks good; thanks for removing the email suggestion.
**CRITICAL**: Immediately write down these values - you'll need them forever: -``` +```text Keystore File: android/app/upload-keystore.jks Store Password: [the password you chose] Key Password: [the key password you chose] Key Alias: [the alias you chose]--- `292-303`: **Nice cross‑platform base64 guidance. Add a brief “don’t paste into logs” caution.** Minor doc enhancement: warn readers not to paste the long base64 string into issues/PRs or build logs. ```diff Convert your keystore file to base64 for secure storage in GitHub: @@ -**Copy the entire output** - it will be thousands of characters long. Save it temporarily in a text file. +**Copy the entire output** — it will be thousands of characters long. Save it temporarily in a local text file. +Do not paste this value into public issues, PRs, or CI logs.Also applies to: 311-328
214-229: Add language hint for “Expected Success Output” block.Satisfies MD040 and improves readability.
-If successful, you'll see something like: -``` +If successful, you'll see something like: +```text Alias name: youraliasname Creation date: Aug 13, 2025 Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=Your App Name, OU=Organization, O=Company... Valid from: Wed Aug 13 18:47:47 UYT 2025 until: Sun Dec 29 18:47:47 UYT 2052 Certificate fingerprints: SHA1: FD:DB:64:E4:7C:60:4D:BD:27:9F:A7:C2:D7:16:AB:6B:40:74:9A:F3 SHA256: 67:6F:67:26:76:D3:59:3A:C5:2F:01:7C:58:1C:50:C0:8B...--- `379-387`: **Mark these output blocks with a language to satisfy MD040.** Use “text” to avoid syntax highlighting; keeps CI log snippets clean. ```diff #### ✅ Setup Android Keystore Should show: -``` +```text Setting environment variables ANDROID_KEYSTORE_PASSWORD=*** ANDROID_KEY_PASSWORD=*** ANDROID_KEY_ALIAS=*** ANDROID_KEYSTORE_FILE=upload-keystore.jks@@
✅ Verify APK Signing
Should show:
-+text
✅ APK built successfully
Verifying with jarsigner...
jar verified.
✅ Certificate details displayedAlso applies to: 392-400
443-456: Normalize fingerprints when comparing; note colon/casing differences.Readers often copy SHA‑256 with/without colons. Suggest normalizing both sides in the examples so comparisons don’t fail on formatting.
**Extract keystore certificate SHA-256 fingerprint:** ```bash keytool -list -v -keystore android/app/upload-keystore.jks -alias YOUR_ALIAS -storepass YOUR_STORE_PASSWORD | grep "SHA256:"Extract APK certificate fingerprint:
-# Method 1: Using jarsigner -jarsigner -verify -verbose -certs build/app/outputs/flutter-apk/app-release.apk | grep "SHA256:" +# Method 1: Using jarsigner (normalize by stripping colons/spaces) +jarsigner -verify -verbose -certs build/app/outputs/flutter-apk/app-release.apk \ + | awk -F': ' '/SHA256/{print $2}' | tr -d ' :\n' | tr '[:lower:]' '[:upper:]' # Method 2: Using apksigner (if available) -apksigner verify --print-certs build/app/outputs/flutter-apk/app-release.apk | grep "SHA256:" +apksigner verify --print-certs build/app/outputs/flutter-apk/app-release.apk \ + | awk -F': ' '/SHA-256 digest/{print $2}' | tr -d ' :\n' | tr '[:lower:]' '[:upper:]'Compare and verify:
-- Both keystore and APK should show identical SHA-256 fingerprints
+- Both keystore and APK should show identical SHA-256 fingerprints (ignore colons/case)</blockquote></details> </blockquote></details> <details> <summary>📜 Review details</summary> **Configuration used**: CodeRabbit UI **Review profile**: CHILL **Plan**: Pro **💡 Knowledge Base configuration:** - MCP integration is disabled by default for public repositories - Jira integration is disabled by default for public repositories - Linear integration is disabled by default for public repositories You can enable these sources in your CodeRabbit configuration. <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between bc79c027bfad837877e56f9cb5121d7baa9ed157 and d14f9f6a732b56044ddf9974e1dcd871dc115772. </details> <details> <summary>📒 Files selected for processing (2)</summary> * `.github/workflows/main.yml` (3 hunks) * `GITHUB_SECRETS_SETUP.md` (1 hunks) </details> <details> <summary>🧰 Additional context used</summary> <details> <summary>🪛 LanguageTool</summary> <details> <summary>GITHUB_SECRETS_SETUP.md</summary> [grammar] ~7-~7: There might be a mistake here. Context: ...Guide Achieves By the end, you'll have: - ✅ Properly signed APKs built in GitHub A... (QB_NEW_EN) --- [grammar] ~8-~8: There might be a mistake here. Context: ...erly signed APKs built in GitHub Actions - ✅ Consistent signing certificates across... (QB_NEW_EN) --- [grammar] ~9-~9: There might be a mistake here. Context: ...t signing certificates across all builds - ✅ Secure credential storage in GitHub Se... (QB_NEW_EN) --- [grammar] ~10-~10: There might be a mistake here. Context: ...ure credential storage in GitHub Secrets - ✅ APK installation that works without un... (QB_NEW_EN) --- [grammar] ~21-~21: There might be a mistake here. Context: ...hat You Need Before starting, you need: - ✅ Android keystore file (`.jks` or `.key... (QB_NEW_EN) --- [grammar] ~50-~50: There might be a mistake here. Context: ...-key.jks` **If you find keystore files:** - ✅ **You have a keystore** → Skip to "Cre... (QB_NEW_EN) --- [grammar] ~53-~53: There might be a mistake here. Context: ...found** → These won't work for release builds, create a new one **If no keystore files... (QB_NEW_EN) --- [grammar] ~53-~53: There might be a mistake here. Context: ...ork for release builds, create a new one **If no keystore files found:** - ❌ **No ke... (QB_NEW_EN) --- [grammar] ~55-~55: There might be a mistake here. Context: ...a new one **If no keystore files found:** - ❌ **No keystore exists** → Continue to "... (QB_NEW_EN) --- [grammar] ~66-~66: There might be a mistake here. Context: ...ds (choose strong, memorable passwords):** - **Store Password**: Protects the keystore ... (QB_NEW_EN) --- [grammar] ~68-~68: There might be a mistake here. Context: ...ssword**: Protects the signing key (can be same as store password) **Key Alias**:... (QB_NEW_EN) --- [grammar] ~68-~68: There might be a mistake here. Context: ...: Protects the signing key (can be same as store password) **Key Alias**: A name ... (QB_NEW_EN) --- [grammar] ~72-~72: There might be a mistake here. Context: ...tion (these identify your organization):** - **CN (Common Name)**: Your app name or org... (QB_NEW_EN) --- [grammar] ~73-~73: There might be a mistake here. Context: ...ganization (e.g., `MyApp`, `My Company`) - **OU (Organizational Unit)**: Your departm... (QB_NEW_EN) --- [grammar] ~74-~74: There might be a mistake here. Context: ...g., `Mobile Development`, `Engineering`) - **O (Organization)**: Your company name (e... (QB_NEW_EN) --- [grammar] ~75-~75: There might be a mistake here. Context: ...our company name (e.g., `MyCompany Inc`) - **L (Locality)**: Your city (e.g., `Tucupi... (QB_NEW_EN) --- [grammar] ~76-~76: There might be a mistake here. Context: ...ocality)**: Your city (e.g., `Tucupita`) - **ST (State)**: Your state/province (e.g.,... (QB_NEW_EN) --- [grammar] ~77-~77: There might be a mistake here. Context: ...r state/province (e.g., `Delta Amacuro`) - **C (Country)**: Your country code (e.g., ... (QB_NEW_EN) --- [grammar] ~100-~100: There might be a mistake here. Context: ...OUR_COUNTRY" ``` **Command Explanation:** - `-keystore upload-keystore.jks`: Creates file named `upload-keystore.jk... (QB_NEW_EN) --- [grammar] ~101-~101: There might be a mistake here. Context: ...pload-keystore.jks`: Creates file named `upload-keystore.jks` - `-keyalg RSA -keysize 2048`: Uses RSA encryption with 2048-bit key ... (QB_NEW_EN) --- [grammar] ~102-~102: There might be a mistake here. Context: ...SA encryption with 2048-bit key (secure) - `-validity 10000`: Certificate valid for ~27 years - `-al... (QB_NEW_EN) --- [grammar] ~103-~103: There might be a mistake here. Context: ... 10000`: Certificate valid for ~27 years - `-alias YOUR_CHOSEN_ALIAS`: Replace with your chosen alias name - ... (QB_NEW_EN) --- [grammar] ~104-~104: There might be a mistake here. Context: ...AS`: Replace with your chosen alias name - `-storepass / -keypass`: Replace with your chosen passwords - `... (QB_NEW_EN) --- [grammar] ~105-~105: There might be a mistake here. Context: ...ass`: Replace with your chosen passwords - `-dname`: Replace with your organization informa... (QB_NEW_EN) --- [grammar] ~135-~135: There might be a mistake here. Context: ... ``` **Store this information securely:** - Add to your password manager - Store in ... (QB_NEW_EN) --- [grammar] ~186-~186: There might be a mistake here. Context: ...ad-keystore.jks ``` **File explanation:** - `storePassword`: The password that protects your keysto... (QB_NEW_EN) --- [grammar] ~187-~187: There might be a mistake here. Context: ...assword that protects your keystore file - `keyPassword`: The password that protects your signin... (QB_NEW_EN) --- [grammar] ~188-~188: There might be a mistake here. Context: ...assword that protects your signing key (often same as store password) - `keyAlias`: T... (QB_NEW_EN) --- [grammar] ~188-~188: There might be a mistake here. Context: ...t protects your signing key (often same as store password) - `keyAlias`: The name ... (QB_NEW_EN) --- [grammar] ~188-~188: There might be a mistake here. Context: ...gning key (often same as store password) - `keyAlias`: The name of your signing key within th... (QB_NEW_EN) --- [grammar] ~189-~189: There might be a mistake here. Context: ... of your signing key within the keystore - `storeFile`: The filename of your keystore (should ... (QB_NEW_EN) --- [grammar] ~210-~210: There might be a mistake here. Context: ...ASSWORD ``` **Replace the placeholders:** - `YOUR_KEY_ALIAS`: Use the value from your `key.propertie... (QB_NEW_EN) --- [grammar] ~230-~230: There might be a mistake here. Context: ... GitHub builds use the same certificate. ### Common Errors and Solutions #### ❌ "keys... (QB_NEW_EN) --- [grammar] ~235-~235: There might be a mistake here. Context: ...ur store password is wrong. Check these: 1. **Verify the password in key.properties** ... (QB_NEW_EN) --- [grammar] ~241-~241: There might be a mistake here. Context: ...ist" Your alias name is wrong. Fix this: 1. **List all aliases in your keystore**: ... (QB_NEW_EN) --- [grammar] ~250-~250: There might be a mistake here. Context: ... file not found" The file path is wrong: 1. **Check the file exists**: `ls -la app/upl... (QB_NEW_EN) --- [grammar] ~251-~251: There might be a mistake here. Context: ...is wrong: 1. **Check the file exists**: `ls -la app/upload-keystore.jks` 2. **Look for keystore files**: `find . -name... (QB_NEW_EN) --- [grammar] ~252-~252: There might be a mistake here. Context: ...re.jks` 2. **Look for keystore files**: `find . -name "*.jks" -o -name "*.keystore"` 3. **Update the storeFile path** in key.prope... (QB_NEW_EN) --- [grammar] ~282-~282: There might be a mistake here. Context: ... values** (you'll need them for GitHub): 1. **Store Password**: `storePassword=` value... (QB_NEW_EN) --- [grammar] ~283-~283: There might be a mistake here. Context: ...Store Password**: `storePassword=` value 2. **Key Password**: `keyPassword=` value 3... (QB_NEW_EN) --- [grammar] ~284-~284: There might be a mistake here. Context: .... **Key Password**: `keyPassword=` value 3. **Key Alias**: `keyAlias=` value 4. **Stor... (QB_NEW_EN) --- [grammar] ~285-~285: There might be a mistake here. Context: ...ue 3. **Key Alias**: `keyAlias=` value 4. **Store File**: `storeFile=` value (usuall... (QB_NEW_EN) --- [grammar] ~345-~345: There might be a mistake here. Context: ...secret**: #### 1. ANDROID_KEYSTORE_FILE - **Name**: `ANDROID_KEYSTORE_FILE` - **Valu... (QB_NEW_EN) --- [grammar] ~346-~346: There might be a mistake here. Context: ...## 1. ANDROID_KEYSTORE_FILE - **Name**: `ANDROID_KEYSTORE_FILE` - **Value**: Your very long base64 string fr... (QB_NEW_EN) --- [grammar] ~349-~349: There might be a mistake here. Context: ...tep 2 #### 2. ANDROID_KEYSTORE_PASSWORD - **Name**: `ANDROID_KEYSTORE_PASSWORD` - **... (QB_NEW_EN) --- [grammar] ~350-~350: There might be a mistake here. Context: .... ANDROID_KEYSTORE_PASSWORD - **Name**: `ANDROID_KEYSTORE_PASSWORD` - **Value**: The `storePassword` value from ... (QB_NEW_EN) --- [grammar] ~353-~353: There might be a mistake here. Context: ...properties #### 3. ANDROID_KEY_PASSWORD - **Name**: `ANDROID_KEY_PASSWORD` - **Val... (QB_NEW_EN) --- [grammar] ~354-~354: There might be a mistake here. Context: ...### 3. ANDROID_KEY_PASSWORD - **Name**: `ANDROID_KEY_PASSWORD` - **Value**: The `keyPassword` value from yo... (QB_NEW_EN) --- [grammar] ~357-~357: There might be a mistake here. Context: ...ey.properties #### 4. ANDROID_KEY_ALIAS - **Name**: `ANDROID_KEY_ALIAS` - **Value**:... (QB_NEW_EN) --- [grammar] ~358-~358: There might be a mistake here. Context: ... #### 4. ANDROID_KEY_ALIAS - **Name**: `ANDROID_KEY_ALIAS` - **Value**: The `keyAlias` value from your ... (QB_NEW_EN) --- [grammar] ~379-~379: There might be a mistake here. Context: ...ions log: #### ✅ Setup Android Keystore Should show: ``` Setting environment var... (QB_NEW_EN) --- [grammar] ~389-~389: There might be a mistake here. Context: ...pload-keystore.jks ``` #### ✅ Build APK Should complete without errors about key... (QB_NEW_EN) --- [grammar] ~392-~392: There might be a mistake here. Context: ...e or signing. #### ✅ Verify APK Signing Should show: ``` ✅ APK built successfull... (QB_NEW_EN) --- [grammar] ~457-~457: There might be a mistake here. Context: ...rep "SHA256:" ``` **Compare and verify:** - Both keystore and APK should show identi... (QB_NEW_EN) --- [grammar] ~471-~471: There might be a mistake here. Context: ...the build **What the CI checks prevent:** - Unsigned APKs reaching production - APKs... (QB_NEW_EN) --- [grammar] ~472-~472: There might be a mistake here. Context: ...t:** - Unsigned APKs reaching production - APKs signed with debug certificates - AP... (QB_NEW_EN) --- [grammar] ~473-~473: There might be a mistake here. Context: ...on - APKs signed with debug certificates - APKs signed with wrong/compromised certi... (QB_NEW_EN) --- [grammar] ~474-~474: There might be a mistake here. Context: ...gned with wrong/compromised certificates - Signature corruption during build proces... (QB_NEW_EN) --- [grammar] ~475-~475: There might be a mistake here. Context: ...sed certificates - Signature corruption during build process #### Troubleshooting Sig... (QB_NEW_EN) --- [grammar] ~480-~480: There might be a mistake here. Context: ...gnature is invalid or corrupted - Check that keystore file exists and is not corrupt... (QB_NEW_EN) --- [grammar] ~485-~485: There might be a mistake here. Context: ...gned with different certificate - Check that correct keystore file is being used - V... (QB_NEW_EN) --- [grammar] ~489-~489: There might be a mistake here. Context: ...t found**: Keystore or APK access issues - Verify keystore file path and permission... (QB_NEW_EN) --- [grammar] ~506-~506: There might be a mistake here. Context: ...keystore.jks ``` The build system will: - Use your keystore when available locally... (QB_NEW_EN) --- [grammar] ~508-~508: There might be a mistake here. Context: ...le locally - Fall back to debug signing if keystore is missing - Show warnings abo... (QB_NEW_EN) --- [grammar] ~515-~515: There might be a mistake here. Context: ...❌ "keystore not found" in GitHub Actions **Cause**: Base64 keystore secret is wrong... (QB_NEW_EN) --- [grammar] ~517-~517: There might be a mistake here. Context: ...secret is wrong or missing **Solution**: 1. Regenerate base64: `base64 -w 0 android/... (QB_NEW_EN) --- [grammar] ~518-~518: There might be a mistake here. Context: ...ng **Solution**: 1. Regenerate base64: `base64 -w 0 android/app/upload-keystore.jks` 2. Update `ANDROID_KEYSTORE_FILE` secret wi... (QB_NEW_EN) --- [grammar] ~521-~521: There might be a mistake here. Context: ...### ❌ "Wrong password" in GitHub Actions **Cause**: Password secrets don't match yo... (QB_NEW_EN) --- [grammar] ~523-~523: There might be a mistake here. Context: ... don't match your keystore **Solution**: 1. Double-check the values in your local `k... (QB_NEW_EN) --- [grammar] ~525-~525: There might be a mistake here. Context: ...ey.properties` 2. Update GitHub secrets with exact same values 3. Ensure no extra sp... (QB_NEW_EN) --- [style] ~525-~525: ‘exact same’ might be wordy. Consider a shorter alternative. Context: ...operties` 2. Update GitHub secrets with exact same values 3. Ensure no extra spaces or cha... (EN_WORDINESS_PREMIUM_EXACT_SAME) --- [grammar] ~530-~530: There might be a mistake here. Context: ...PK wasn't properly signed **Solution**: 1. Check that keystore file was created in ... (QB_NEW_EN) --- [grammar] ~531-~531: There might be a mistake here. Context: ...properly signed **Solution**: 1. Check that keystore file was created in "Setup And... (QB_NEW_EN) --- [grammar] ~542-~542: There might be a mistake here. Context: ...nt fingerprints between local and GitHub **Cause**: GitHub is using different keyst... (QB_NEW_EN) --- [grammar] ~544-~544: There might be a mistake here. Context: ...nt keystore or credentials **Solution**: 1. Verify base64 keystore decodes to identi... (QB_NEW_EN) --- [grammar] ~552-~552: There might be a mistake here. Context: ...tices ### 🔐 Keep Credentials Secure - Never commit `key.properties` or keystore... (QB_NEW_EN) --- [grammar] ~553-~553: There might be a mistake here. Context: ... or keystore files to your repository - Use GitHub Secrets for all sensitive valu... (QB_NEW_EN) --- [grammar] ~555-~555: There might be a mistake here. Context: ...rets - Enable 2FA on your GitHub account ### 🛡️ Backup Your Keystore - Keep secure ba... (QB_NEW_EN) --- [grammar] ~558-~558: There might be a mistake here. Context: ...account ### 🛡️ Backup Your Keystore - Keep secure backups of your keystore file... (QB_NEW_EN) --- [grammar] ~559-~559: There might be a mistake here. Context: ... secure backups of your keystore file - Document your passwords in a password man... (QB_NEW_EN) --- [grammar] ~560-~560: There might be a mistake here. Context: ...manager - Test your backups periodically ### ⚠️ If Compromised If you suspect your key... (QB_NEW_EN) --- [grammar] ~562-~562: There might be a mistake here. Context: ...kups periodically ### ⚠️ If Compromised If you suspect your keystore or password... (QB_NEW_EN) --- [grammar] ~563-~563: There might be a mistake here. Context: ...r keystore or passwords are compromised: 1. Immediately change GitHub secrets 2. Gen... (QB_NEW_EN) </details> </details> <details> <summary>🪛 markdownlint-cli2 (0.17.2)</summary> <details> <summary>GITHUB_SECRETS_SETUP.md</summary> 128-128: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 217-217: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 381-381: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 394-394: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 423-423: Fenced code blocks should have a language specified (MD040, fenced-code-language) </details> </details> <details> <summary>🪛 YAMLlint (1.37.1)</summary> <details> <summary>.github/workflows/main.yml</summary> [error] 196-196: no new line character at the end of file (new-line-at-end-of-file) </details> </details> </details> <details> <summary>⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)</summary> * GitHub Check: build </details> <details> <summary>🔇 Additional comments (1)</summary><blockquote> <details> <summary>GITHUB_SECRETS_SETUP.md (1)</summary><blockquote> `170-176`: **Good: key.properties examples use module-relative storeFile.** This matches Gradle’s usual resolution from the app module. Keep it consistent with CI (ensure the workflow writes `storeFile=upload-keystore.jks`, not `app/...`). Also applies to: 178-184 </blockquote></details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
Summary by CodeRabbit
Chores
Documentation