Enable Firebase by default for Android and iOS builds so normal local, test, and production builds no longer depend on passing -PfirebaseEnabled.

Opt out with -PfirebaseEnabled=false or FIREBASE_ENABLED=false. A missing google-services.json or GoogleService-Info.plist now fails the build instead of silently skipping Firebase.
This commit is contained in:
Jose Olarte III
2026-10-12 10:20:39 -04:00
parent 1d1801e9b5
commit a271cd1434
13 changed files with 209 additions and 67 deletions
+8 -6
View File
@@ -2,16 +2,18 @@
## Android Build — Google Play Services / FOSS Compatibility
**Firebase is opt-in. Do NOT enable it accidentally.**
**Firebase is on by default for Android and iOS builds.** Do not turn it off unless the build is an exceptional FOSS/F-Droid artifact.
`android/google-services.json` is gitignored and may be present on disk for push notification development, but Firebase is only activated when you explicitly pass `-PfirebaseEnabled` to Gradle:
`android/app/google-services.json` is required when Firebase is enabled. It does not decide the switch: a missing file fails the build, and a file on disk is ignored when Firebase is explicitly disabled.
- **FOSS / APK / Aurora / Zapstore / F-Droid builds**: just `./gradlew assembleRelease` — Firebase stays off even if `google-services.json` is on disk.
- **Firebase / FCM / Play Store builds**: `./gradlew bundleRelease -PfirebaseEnabled` — explicitly opt in.
- **Default (local, test, production, Play Store)**: `./gradlew assembleRelease` or `./gradlew bundleRelease` — do not pass `-PfirebaseEnabled`.
- **Opt out (F-Droid / FOSS)**: `./gradlew assembleRelease -PfirebaseEnabled=false`. npm scripts honor `FIREBASE_ENABLED=false`, which forwards that property.
This guard is in `android/app/build.gradle`. Do NOT change this conditional to activate Firebase unconditionally based on file presence alone — that was the bug that broke FOSS distribution in June 2026.
iOS builds bundle `ios/App/App/GoogleService-Info.plist` by default (`ios/App/scripts/bundle-firebase-config.sh`, run from `scripts/build-ios.sh` and as an Xcode build phase). Opt out with `FIREBASE_ENABLED=false`. A missing plist fails the build; the file's presence does not enable Firebase on its own.
`google-services.json` is intentionally excluded from git (`android/.gitignore`). Never commit it.
The guard is in `android/app/build.gradle`. Do NOT activate Firebase from file presence alone — that was the bug that broke FOSS distribution in June 2026.
Never commit `google-services.json`.
Full details, incident history, and F-Droid notes: `doc/android-firebase-gms.md`
+3 -3
View File
@@ -1474,9 +1474,11 @@ So, to create a signed `aab` release:
```bash
# if you use pkgx: pkgx +openjdk.org /bin/zsh
./gradlew bundleRelease -Dlint.baselines.continue=true -PfirebaseEnabled
./gradlew bundleRelease -Dlint.baselines.continue=true
```
Firebase is on by default (`firebaseEnabled=true` in `android/gradle.properties`). Do not pass `-PfirebaseEnabled` for a normal release. F-Droid and other builds that must omit Firebase use `-PfirebaseEnabled=false`.
... and find your `aab` file at app/build/outputs/bundle/release
* Gradle packages whatever web bundle was last synced into
@@ -1486,8 +1488,6 @@ So, to create a signed `aab` release:
* `build-android.sh` runs `./gradlew clean`, which deletes any earlier `aab`, so
run `bundleRelease` after the build script finishes.
* Note that F-Droid builds should omit `-PfirebaseEnabled`.
At play.google.com/console:
- Go to Production or the Closed Testing and either Create Track or Manage Track.
+26 -14
View File
@@ -130,20 +130,32 @@ dependencies {
apply from: 'capacitor.build.gradle'
// Firebase / Google Play Services are opt-in. Pass -PfirebaseEnabled to any Gradle command
// to activate Firebase (FCM push notifications). Without this flag the build works on
// F-Droid, Aurora, Zapstore, and plain APK sideloading even when google-services.json
// is present on disk (it is gitignored; see AGENTS.md for the full story).
try {
def servicesJSON = file('google-services.json')
if (servicesJSON.exists() && servicesJSON.text && project.hasProperty('firebaseEnabled')) {
apply plugin: 'com.google.gms.google-services'
logger.info("Firebase enabled: google-services plugin applied")
} else if (servicesJSON.exists() && !project.hasProperty('firebaseEnabled')) {
logger.info("google-services.json present but firebaseEnabled not set — skipping Firebase plugin (pass -PfirebaseEnabled to enable)")
// Firebase is on for every build. Opt out only with -PfirebaseEnabled=false
// (F-Droid / other builds that must not apply the google-services plugin).
// The config file does not decide the switch: when Firebase is on, a missing
// file fails the build; when it is off, a file on disk is ignored.
def firebaseEnabledRaw = project.findProperty('firebaseEnabled')
def firebaseEnabled = true
if (firebaseEnabledRaw != null) {
def normalized = firebaseEnabledRaw.toString().trim().toLowerCase()
if (normalized == 'false' || normalized == '0' || normalized == 'no') {
firebaseEnabled = false
}
}
if (!firebaseEnabled) {
logger.lifecycle('Firebase disabled (-PfirebaseEnabled=false): google-services plugin not applied')
} else {
logger.info("google-services.json not found — Firebase plugin not applied")
def servicesJSON = file('google-services.json')
def servicesBody = servicesJSON.isFile() ? servicesJSON.getText('UTF-8') : ''
if (!servicesBody.trim() || !servicesBody.contains('mobilesdk_app_id')) {
throw new GradleException(
'Firebase is enabled, but android/app/google-services.json is missing, empty, or has no mobilesdk_app_id.\n' +
'Download it from the Firebase console (Project settings → Your apps → Android app)\n' +
'and place it at android/app/google-services.json.\n' +
'To build without Firebase (F-Droid / FOSS), pass -PfirebaseEnabled=false.'
)
}
} catch(Exception e) {
logger.info("google-services plugin not applied: ${e.message}")
apply plugin: 'com.google.gms.google-services'
logger.lifecycle('Firebase enabled: google-services plugin applied')
}
@@ -15,8 +15,8 @@ public class NotificationInspectorPlugin extends Plugin {
}
/**
* Whether this build carries a Firebase config, i.e. was built with
* -PfirebaseEnabled. Without it, PushNotifications.register() throws on the
* Whether this build carries a Firebase config. Normal builds do.
* -PfirebaseEnabled=false omits it. Without it, PushNotifications.register() throws on the
* Capacitor plugin thread and kills the app, so JS must check this first.
* The google-services Gradle plugin generates the google_app_id resource,
* which is what Firebase's own auto-initialization looks for.
+5
View File
@@ -22,3 +22,8 @@ org.gradle.jvmargs=-Xmx1536m --add-opens=jdk.compiler/com.sun.tools.javac.api=AL
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
android.suppressUnsupportedCompileSdk=36
# Firebase/FCM is on for every Gradle build (Android Studio, ./gradlew, npm scripts).
# Opt out with -PfirebaseEnabled=false. android/app/google-services.json does not
# toggle this; a missing file fails the build while Firebase is enabled.
firebaseEnabled=true
+18 -21
View File
@@ -2,49 +2,45 @@
## Overview
The app is designed to work on Android devices with and without Google Play Services (GMS). Firebase/FCM push notifications are an opt-in feature at build time; all other functionality works on GMS-less devices (F-Droid, LineageOS without OpenGApps, etc.).
The app is designed to work on Android devices with and without Google Play Services (GMS). Firebase/FCM is **on by default** for local, test, and production builds. GMS-less devices still run the app; FCM degrades when Play Services is absent. F-Droid and other builds that must omit Firebase opt out explicitly.
## How the opt-in guard works
## How the switch works
`android/app/build.gradle` applies the `com.google.gms.google-services` Gradle plugin only when **both** conditions are true:
`android/app/build.gradle` applies `com.google.gms.google-services` unless `firebaseEnabled` is `false`. `android/gradle.properties` sets `firebaseEnabled=true`, so Android Studio, `./gradlew`, and `scripts/build-android.sh` all pick it up. `-PfirebaseEnabled` and `-PfirebaseEnabled=true` stay on. The config file does not decide the switch.
1. `android/google-services.json` is present on disk
2. The Gradle property `firebaseEnabled` is explicitly passed
When Firebase is on, `android/app/google-services.json` must exist, be non-empty, and contain `mobilesdk_app_id`. Otherwise configuration fails with an error that names that path. When Firebase is off, the file is ignored even if it is on disk.
```groovy
if (servicesJSON.exists() && servicesJSON.text && project.hasProperty('firebaseEnabled')) {
apply plugin: 'com.google.gms.google-services'
}
```
`scripts/build-android.sh` forwards an opt-out: `FIREBASE_ENABLED=false` adds `-PfirebaseEnabled=false` to its Gradle commands. npm scripts do not pass `-PfirebaseEnabled`; they rely on the default.
This means the file can live on disk for development purposes without accidentally activating Firebase.
iOS uses the same default. `ios/App/scripts/bundle-firebase-config.sh` copies `ios/App/App/GoogleService-Info.plist` into the app and the share extension. `scripts/build-ios.sh` runs it with `--check` before a device build, and both Xcode targets run it as a build phase. `FIREBASE_ENABLED=false` skips the plist. A missing or empty plist fails the build when Firebase is on.
## Build commands
| Target | Command | Firebase |
|---|---|---|
| APK / sideload / Zapstore | `./gradlew assembleRelease` | off |
| Aurora / Play Store without FCM | `./gradlew bundleRelease` | off |
| Play Store with FCM push notifications | `./gradlew bundleRelease -PfirebaseEnabled` | on |
| F-Droid | `./gradlew assembleRelease` | off (required) |
| Local / test / production APK or AAB | `./gradlew assembleRelease` or `bundleRelease` | on |
| Play Store with FCM | `./gradlew bundleRelease` | on |
| iOS (Xcode or `scripts/build-ios.sh`) | default | on |
| F-Droid / FOSS Android | `./gradlew assembleRelease -PfirebaseEnabled=false` | off |
| iOS without Firebase | `FIREBASE_ENABLED=false` | off |
## Behavior on non-GMS devices
When built with `-PfirebaseEnabled`, Firebase SDKs check for GMS availability at startup and degrade gracefully if it is absent:
When Firebase is enabled (the default), Firebase SDKs check for GMS availability at startup and degrade gracefully if it is absent:
- Firebase initializes but detects no GMS
- FCM skips token registration silently (no token, no notifications)
- The app continues to work normally
This means a single Play Store AAB (`bundleRelease -PfirebaseEnabled`) covers both GMS and non-GMS users. GMS users get push notifications; non-GMS users get a fully functional app without them.
This means a single Play Store AAB (`bundleRelease`, Firebase on by default) covers both GMS and non-GMS users. GMS users get push notifications; non-GMS users get a fully functional app without them.
**Aurora Store** pulls the exact APK from Play Store servers, so Aurora users get whichever variant was uploaded. The Play Store AAB built with `-PfirebaseEnabled` is correct for Aurora.
**Aurora Store** pulls the exact APK from Play Store servers, so Aurora users get whichever variant was uploaded. The default Play Store AAB (Firebase on) is the one to upload.
**F-Droid** is stricter: their build policy rejects any APK with GMS dependencies at the binary level, even with graceful degradation. F-Droid submission would require a separate `assembleRelease` build (no flag) and a dedicated F-Droid listing.
**F-Droid** is stricter: their build policy rejects any APK with GMS dependencies at the binary level, even with graceful degradation. F-Droid submission would require a separate `assembleRelease -PfirebaseEnabled=false` build and a dedicated F-Droid listing.
## Push registration in builds without Firebase
A build without `-PfirebaseEnabled` contains the `@capacitor/push-notifications` plugin, but no Firebase config. In that build, `PushNotifications.register()` throws `Default FirebaseApp is not initialized` on the Capacitor plugin thread, which kills the app; a JS `try/catch` cannot intercept it.
A build with `-PfirebaseEnabled=false` contains the `@capacitor/push-notifications` plugin, but no Firebase config. In that build, `PushNotifications.register()` throws `Default FirebaseApp is not initialized` on the Capacitor plugin thread, which kills the app; a JS `try/catch` cannot intercept it.
The JS therefore asks native code first. `NotificationInspector.isFirebaseConfigured()` (`android/app/src/main/java/app/timesafari/notifications/NotificationInspectorPlugin.java`) reports whether the `google_app_id` string resource exists; the google-services Gradle plugin generates that resource only when Firebase is enabled, and it is the same signal Firebase's auto-initialization uses. `firebaseMessagingClient.ts` skips `register()` on Android when it is absent (or when the check fails), logging `No Firebase config in this Android build; skipping push registration`. Listener setup and the notification permission request run either way, since local daily notifications need that permission.
@@ -56,7 +52,8 @@ Any code that calls `PushNotifications.register()` or other `FirebaseMessaging`
- Gitignored (`android/.gitignore` line 80) — never commit it
- Contains Firebase project credentials (project number, app ID, API key)
- Safe to leave on disk; has no effect unless `-PfirebaseEnabled` is passed
- Required for the default (Firebase on) build. Place it at `android/app/google-services.json`
- Ignored when you pass `-PfirebaseEnabled=false`; leaving it on disk does not re-enable Firebase
- Obtain from the Firebase console: Project Settings → Your apps → Android app → Download `google-services.json`
## Known GMS dependency: MLKit barcode scanner
+3 -3
View File
@@ -240,7 +240,7 @@ Complete this section once before your first physical-device push test. If Fireb
2. The repo gitignores this file — each developer keeps a local copy; never commit it.
3. Rebuild so Gradle applies the Google Services plugin (`android/app/build.gradle` applies `com.google.gms.google-services` when the file exists). If the file is missing, the build logs: *google-services.json not found, google-services plugin not applied. Push Notifications won't work*.
3. Rebuild. Firebase is on by default, so Gradle applies the Google Services plugin. If `android/app/google-services.json` is missing or empty, the build stops and names that path. Opt out only with `-PfirebaseEnabled=false` or `FIREBASE_ENABLED=false`.
4. Sync Capacitor if you changed native config:
@@ -928,11 +928,11 @@ Compare with panel **Backend Status** and Event Log error text.
### Gradle: Push Notifications won't work
**Symptoms:** Build log: `google-services.json not found, google-services plugin not applied`.
**Symptoms:** Build fails: `android/app/google-services.json is missing, empty, or has no mobilesdk_app_id`.
**Likely causes:** Missing `android/app/google-services.json`.
**Verification:** File exists on disk; rebuild shows Google Services plugin applied.
**Verification:** File exists on disk and the build log says `Firebase enabled: google-services plugin applied`.
**Fixes:** Download from Firebase Console (package `app.timesafari.app`); place in `android/app/`; `npx cap sync android` and rebuild.
+44 -4
View File
@@ -19,8 +19,6 @@
C86585DF2ED456DE00824752 /* TimeSafariShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = C86585D52ED456DE00824752 /* TimeSafariShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
C8C56E142EE0474B00737D0E /* SharedImageUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C56E132EE0474B00737D0E /* SharedImageUtility.swift */; };
C8C56E162EE064CB00737D0E /* SharedImagePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C56E152EE064CA00737D0E /* SharedImagePlugin.swift */; };
C8E73DD12FC6E5DC0057F59A /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C8E73DD02FC6E5DC0057F59A /* GoogleService-Info.plist */; };
C8E73DD22FC6E5DC0057F59A /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C8E73DD02FC6E5DC0057F59A /* GoogleService-Info.plist */; };
E9F1A0022EE05A8B00737D01 /* NotificationInspectorPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F1A0012EE05A8B00737D01 /* NotificationInspectorPlugin.swift */; };
/* End PBXBuildFile section */
@@ -185,6 +183,7 @@
504EC3001FED79650016851F /* Sources */,
504EC3011FED79650016851F /* Frameworks */,
504EC3021FED79650016851F /* Resources */,
F1REBA5E000000000000A001 /* Bundle Firebase Config */,
012076E8FFE4BF260A79B034 /* Fix Privacy Manifest */,
96A7EF592DF3366D00084D51 /* Fix Privacy Manifest */,
C86585E02ED456DE00824752 /* Embed Foundation Extensions */,
@@ -207,6 +206,7 @@
C86585D12ED456DE00824752 /* Sources */,
C86585D22ED456DE00824752 /* Frameworks */,
C86585D32ED456DE00824752 /* Resources */,
F1REBA5E000000000000A002 /* Bundle Firebase Config */,
);
buildRules = (
);
@@ -270,7 +270,6 @@
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
504EC30D1FED79650016851F /* Main.storyboard in Resources */,
2FAD9763203C412B000D30F8 /* config.xml in Resources */,
C8E73DD12FC6E5DC0057F59A /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -278,7 +277,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C8E73DD22FC6E5DC0057F59A /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -342,6 +340,48 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
F1REBA5E000000000000A001 /* Bundle Firebase Config */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
enableUserScriptSandboxing = 0;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Bundle Firebase Config";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PROJECT_DIR}/scripts/bundle-firebase-config.sh\"\n";
showEnvVarsInLog = 0;
};
F1REBA5E000000000000A002 /* Bundle Firebase Config */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
enableUserScriptSandboxing = 0;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Bundle Firebase Config";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PROJECT_DIR}/scripts/bundle-firebase-config.sh\"\n";
showEnvVarsInLog = 0;
};
96A7EF592DF3366D00084D51 /* Fix Privacy Manifest */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
+61
View File
@@ -0,0 +1,61 @@
#!/bin/sh
# Bundle GoogleService-Info.plist when Firebase is enabled (the default).
# Opt out with FIREBASE_ENABLED=false. The plist does not decide the switch:
# a missing file fails an enabled build, and an opt-out build does not bundle
# the plist even when the file is on disk.
#
# Xcode run-script phases invoke this with no arguments. build-ios.sh may pass
# --check to validate before a long build without copying into a product.
set -eu
check_only=0
if [ "${1:-}" = "--check" ]; then
check_only=1
fi
if [ -z "${SRCROOT:-}" ]; then
script_dir=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
SRCROOT=$(CDPATH= cd -- "$script_dir/.." && pwd)
fi
normalized=$(printf '%s' "${FIREBASE_ENABLED:-true}" | tr '[:upper:]' '[:lower:]')
plist="${SRCROOT}/App/GoogleService-Info.plist"
firebase_off=0
case "$normalized" in
false|0|no) firebase_off=1 ;;
esac
if [ "$firebase_off" -eq 1 ]; then
if [ "$check_only" -eq 0 ]; then
dest_dir="${TARGET_BUILD_DIR:-}/${UNLOCALIZED_RESOURCES_FOLDER_PATH:-}"
if [ -n "${TARGET_BUILD_DIR:-}" ] && [ -n "${UNLOCALIZED_RESOURCES_FOLDER_PATH:-}" ]; then
rm -f "${dest_dir}/GoogleService-Info.plist"
fi
fi
echo "note: Firebase disabled (FIREBASE_ENABLED=false); GoogleService-Info.plist will not be bundled."
exit 0
fi
if [ ! -s "$plist" ] || ! grep -q "GOOGLE_APP_ID" "$plist"; then
echo "error: Firebase is enabled, but ios/App/App/GoogleService-Info.plist is missing, empty, or has no GOOGLE_APP_ID." >&2
echo "error: Download it from the Firebase console (Project settings → Your apps → Apple apps)" >&2
echo "error: and place it at ios/App/App/GoogleService-Info.plist." >&2
echo "error: To build without Firebase, set FIREBASE_ENABLED=false." >&2
exit 1
fi
if [ "$check_only" -eq 1 ]; then
echo "note: Firebase enabled; ios/App/App/GoogleService-Info.plist is present."
exit 0
fi
if [ -z "${TARGET_BUILD_DIR:-}" ] || [ -z "${UNLOCALIZED_RESOURCES_FOLDER_PATH:-}" ]; then
echo "error: Bundle Firebase Config must run as an Xcode build phase (TARGET_BUILD_DIR is unset)." >&2
exit 1
fi
dest_dir="${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
mkdir -p "$dest_dir"
cp "$plist" "${dest_dir}/GoogleService-Info.plist"
echo "note: Firebase enabled; bundled GoogleService-Info.plist."
+23 -8
View File
@@ -23,6 +23,7 @@
# --assets Generate assets only
# --deploy Deploy APK to connected device
# --uninstall Uninstall app from connected device
# FIREBASE_ENABLED=false Opt out of Firebase (-PfirebaseEnabled=false)
# -h, --help Show this help message
# -v, --verbose Enable verbose logging
#
@@ -439,6 +440,9 @@ print_android_usage() {
echo " --uninstall Uninstall app from connected device"
echo " --api-ip <ip> Custom IP address for claim API (defaults to 10.0.2.2)"
echo ""
echo "Firebase is on by default. Opt out with FIREBASE_ENABLED=false"
echo "(passes -PfirebaseEnabled=false to Gradle)."
echo ""
echo "Common Options:"
echo " -h, --help Show this help message"
echo " -v, --verbose Enable verbose logging"
@@ -460,6 +464,17 @@ print_android_usage() {
# Parse command line arguments
parse_android_args "$@"
# Firebase is on unless FIREBASE_ENABLED is false, 0, or no. Gradle also
# defaults firebaseEnabled=true; this only forwards an explicit opt-out.
gradle_firebase_arg=""
firebase_enabled_normalized=$(printf '%s' "${FIREBASE_ENABLED:-true}" | tr '[:upper:]' '[:lower:]')
case "$firebase_enabled_normalized" in
false|0|no)
gradle_firebase_arg="-PfirebaseEnabled=false"
log_info "Firebase disabled for this Android build (FIREBASE_ENABLED=${FIREBASE_ENABLED})"
;;
esac
# Print build header
print_header "TimeSafari Android Build Process"
@@ -528,7 +543,7 @@ if [ "$CLEAN_ONLY" = true ]; then
log_info "Clean-only mode: cleaning build artifacts"
safe_execute "Cleaning Android app" "npm run clean:android" || exit 1
safe_execute "Cleaning dist directory" "clean_build_artifacts dist" || exit 1
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean ${gradle_firebase_arg} && cd .." || exit 4
log_success "Clean completed successfully!"
exit 0
fi
@@ -561,7 +576,7 @@ if [ "$DEPLOY_APP" = true ]; then
fi
# Build APK
safe_execute "Building APK" "cd android && ./gradlew assembleDebug && cd .." || exit 5
safe_execute "Building APK" "cd android && ./gradlew assembleDebug ${gradle_firebase_arg} && cd .." || exit 5
# Install APK on device
safe_execute "Installing APK on device" "adb install -r android/app/build/outputs/apk/debug/app-debug.apk" || exit 6
@@ -628,26 +643,26 @@ fi
safe_execute "Generating assets" "npx capacitor-assets generate --android --assetPath resources" || exit 7
# Step 8: Clean Gradle build
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean ${gradle_firebase_arg} && cd .." || exit 4
# Step 9: Build based on type
if [ "$BUILD_TYPE" = "debug" ]; then
safe_execute "Assembling debug build" "cd android && ./gradlew assembleDebug && cd .." || exit 5
safe_execute "Assembling debug build" "cd android && ./gradlew assembleDebug ${gradle_firebase_arg} && cd .." || exit 5
elif [ "$BUILD_TYPE" = "release" ]; then
safe_execute "Assembling release build" "cd android && ./gradlew assembleRelease && cd .." || exit 5
safe_execute "Assembling release build" "cd android && ./gradlew assembleRelease ${gradle_firebase_arg} && cd .." || exit 5
fi
# Step 10: Build APK/AAB if requested
if [ "$BUILD_APK" = true ]; then
if [ "$BUILD_TYPE" = "debug" ]; then
safe_execute "Building debug APK" "cd android && ./gradlew assembleDebug && cd .." || exit 5
safe_execute "Building debug APK" "cd android && ./gradlew assembleDebug ${gradle_firebase_arg} && cd .." || exit 5
else
safe_execute "Building release APK" "cd android && ./gradlew assembleRelease && cd .." || exit 5
safe_execute "Building release APK" "cd android && ./gradlew assembleRelease ${gradle_firebase_arg} && cd .." || exit 5
fi
fi
if [ "$BUILD_AAB" = true ]; then
safe_execute "Building AAB" "cd android && ./gradlew bundleRelease && cd .." || exit 5
safe_execute "Building AAB" "cd android && ./gradlew bundleRelease ${gradle_firebase_arg} && cd .." || exit 5
if [ "$BUILD_MODE" = "production" ]; then
safe_execute "Checking production AAB" "./scripts/check-prod-bundle.sh android/app/build/outputs/bundle/release/app-release.aab" || exit 10
fi
+9
View File
@@ -44,6 +44,8 @@ print_ios_usage() {
echo " --auto-run Auto-run app after build"
echo " --api-ip <ip> Custom IP address for claim API (uses Capacitor default)"
echo ""
echo "Firebase is on by default. Opt out with FIREBASE_ENABLED=false."
echo ""
echo "Common Options:"
echo " -h, --help Show this help message"
echo " -v, --verbose Enable verbose logging"
@@ -537,6 +539,13 @@ if [ "$ASSETS_ONLY" = true ]; then
exit 0
fi
# Firebase is on unless FIREBASE_ENABLED=false. Fail before the web build and
# xcodebuild when the plist is missing. Xcode runs the same script as a build
# phase so GUI builds get the same check and bundle the plist.
if ! ios/App/scripts/bundle-firebase-config.sh --check; then
exit 1
fi
# Handle deploy-app mode
if [ "$DEPLOY_APP" = true ]; then
deploy_ios_app
+1 -1
View File
@@ -11,7 +11,7 @@ export type PendingNotificationInfo = {
export interface NotificationInspectorPlugin {
getPendingNotifications(): Promise<{ pending: PendingNotificationInfo[] }>;
/** Android only: whether the build has a Firebase config (-PfirebaseEnabled). */
/** Android only: whether this build included Firebase (off only with -PfirebaseEnabled=false). */
isFirebaseConfigured(): Promise<{ configured: boolean }>;
}
@@ -69,9 +69,10 @@ async function registerRetrievedToken(
}
/**
* Whether PushNotifications.register() is safe to call. Android builds without
* -PfirebaseEnabled (FOSS/F-Droid/APK) have no Firebase config, and register()
* there throws natively, crashing the app before any JS catch can run.
* Whether PushNotifications.register() is safe to call. Android builds with
* Firebase disabled (-PfirebaseEnabled=false, FOSS/F-Droid) have no Firebase
* config, and register() there throws natively, crashing the app before any
* JS catch can run.
*/
async function canRegisterNativePush(): Promise<boolean> {
if (Capacitor.getPlatform() !== "android") {
@@ -101,7 +102,7 @@ export async function reregisterFcmTokenNow(): Promise<string> {
if (!(await canRegisterNativePush())) {
throw new Error(
"Push is unavailable: this Android build has no Firebase config (built without -PfirebaseEnabled)",
"Push is unavailable: this Android build has no Firebase config (built with -PfirebaseEnabled=false)",
);
}