diff --git a/packages/flutterfire_cli/lib/src/commands/config.dart b/packages/flutterfire_cli/lib/src/commands/config.dart index 084a8be7..b5af9e68 100644 --- a/packages/flutterfire_cli/lib/src/commands/config.dart +++ b/packages/flutterfire_cli/lib/src/commands/config.dart @@ -103,12 +103,21 @@ class ConfigCommand extends FlutterFireCommand { 'If no package name is provided then an attempt will be made to ' 'automatically pick the first available web app id from remote.', ); + argParser.addOption( kTokenFlag, valueHelp: 'firebaseToken', abbr: 't', help: 'The token generated by running `firebase login:ci`', ); + + argParser.addOption( + kServiceAccountFlag, + valueHelp: 'serviceAccount', + help: + 'The path to a Google service account JSON file, used for authentication', + ); + argParser.addFlag( kAppleGradlePluginFlag, defaultsTo: true, @@ -311,6 +320,11 @@ class ConfigCommand extends FlutterFireCommand { return value; } + String? get serviceAccount { + final value = argResults!['service-account'] as String?; + return value; + } + String get outputFilePath { return argResults!['out'] as String; } @@ -356,6 +370,7 @@ class ConfigCommand extends FlutterFireCommand { projectId: newProjectId, account: accountEmail, token: token, + serviceAccount: serviceAccount, ); creatingProjectSpinner.done(); return newProject; @@ -385,12 +400,18 @@ class ConfigCommand extends FlutterFireCommand { return baseMessage; }, ); + firebaseProjects = await firebase.getProjects( + account: accountEmail, + token: token, + serviceAccount: serviceAccount, + ); try { firebaseProjects = await firebase .getProjects( account: accountEmail, token: token, + serviceAccount: serviceAccount, ) .timeout(const Duration(seconds: 15)); @@ -586,6 +607,7 @@ class ConfigCommand extends FlutterFireCommand { iosBundleId: iosBundleId, macosBundleId: macosBundleId, token: token, + serviceAccount: serviceAccount, webAppId: webAppId, android: selectedPlatforms[kAndroid]!, ios: selectedPlatforms[kIos]!, diff --git a/packages/flutterfire_cli/lib/src/common/utils.dart b/packages/flutterfire_cli/lib/src/common/utils.dart index 0dc15bd4..a28c103d 100644 --- a/packages/flutterfire_cli/lib/src/common/utils.dart +++ b/packages/flutterfire_cli/lib/src/common/utils.dart @@ -68,6 +68,7 @@ const String kAndroidAppIdFlag = 'android-app-id'; const String kAndroidPackageNameFlag = 'android-package-name'; const String kWebAppIdFlag = 'web-app-id'; const String kTokenFlag = 'token'; +const String kServiceAccountFlag = 'service-account'; const String kAppleGradlePluginFlag = 'apply-gradle-plugins'; const String kIosBuildConfigFlag = 'ios-build-config'; const String kMacosBuildConfigFlag = 'macos-build-config'; diff --git a/packages/flutterfire_cli/lib/src/firebase.dart b/packages/flutterfire_cli/lib/src/firebase.dart index 9ef37b5a..f6d5cf90 100644 --- a/packages/flutterfire_cli/lib/src/firebase.dart +++ b/packages/flutterfire_cli/lib/src/firebase.dart @@ -69,6 +69,7 @@ Future> runFirebaseCommand( List commandAndArgs, { String? project, String? account, + String? serviceAccount, }) async { final cliExists = await exists(); if (!cliExists) { @@ -89,6 +90,10 @@ Future> runFirebaseCommand( 'firebase', execArgs, workingDirectory: workingDirectoryPath, + environment: { + if (serviceAccount != null) + 'GOOGLE_APPLICATION_CREDENTIALS': serviceAccount, + }, runInShell: true, ); @@ -112,6 +117,7 @@ Future> runFirebaseCommand( Future> getProjects({ String? account, String? token, + String? serviceAccount, }) async { final response = await runFirebaseCommand( [ @@ -119,6 +125,7 @@ Future> getProjects({ if (token != null) '--token=$token', ], account: account, + serviceAccount: serviceAccount, ); final result = List>.from(response['result'] as List); return result @@ -136,6 +143,7 @@ Future createProject({ String? displayName, String? account, String? token, + String? serviceAccount, }) async { final response = await runFirebaseCommand( [ @@ -145,6 +153,7 @@ Future createProject({ if (token != null) '--token=$token', ], account: account, + serviceAccount: serviceAccount, ); final result = Map.from(response['result'] as Map); return FirebaseProject.fromJson({ @@ -159,6 +168,7 @@ Future> getApps({ String? account, String? platform, String? token, + String? serviceAccount, }) async { if (platform != null) _assertFirebaseSupportedPlatform(platform); final response = await runFirebaseCommand( @@ -169,6 +179,7 @@ Future> getApps({ ], project: project, account: account, + serviceAccount: serviceAccount, ); final result = List>.from(response['result'] as List); return result @@ -194,6 +205,7 @@ Future getAppSdkConfig({ required String platform, String? account, String? token, + String? serviceAccount, }) async { final platformFirebase = platform == kMacos ? kIos : platform; _assertFirebaseSupportedPlatform(platformFirebase); @@ -205,6 +217,7 @@ Future getAppSdkConfig({ if (token != null) '--token=$token', ], account: account, + serviceAccount: serviceAccount, ); final result = Map.from(response['result'] as Map); final fileContents = result['fileContents'] as String; @@ -228,6 +241,7 @@ Future findOrCreateFirebaseApp({ String? packageNameOrBundleIdentifier, String? account, String? token, + String? serviceAccount, String? webAppId, }) async { var foundFirebaseApp = false; @@ -262,6 +276,7 @@ Future findOrCreateFirebaseApp({ account: account, platform: platformFirebase, token: token, + serviceAccount: serviceAccount, ); var filteredFirebaseApps = unfilteredFirebaseApps.where( (firebaseApp) { @@ -306,6 +321,7 @@ Future findOrCreateFirebaseApp({ packageName: packageNameOrBundleIdentifier!, account: account, token: token, + serviceAccount: serviceAccount, ); break; case kIos: @@ -315,6 +331,7 @@ Future findOrCreateFirebaseApp({ bundleId: packageNameOrBundleIdentifier!, account: account, token: token, + serviceAccount: serviceAccount, ); break; case kWeb: @@ -323,6 +340,7 @@ Future findOrCreateFirebaseApp({ displayName: displayNameWithPlatform, account: account, token: token, + serviceAccount: serviceAccount, ); break; default: @@ -352,11 +370,13 @@ Future createWebApp({ required String displayName, String? account, String? token, + String? serviceAccount, }) async { final response = await runFirebaseCommand( ['apps:create', 'web', displayName, if (token != null) '--token=$token'], project: project, account: account, + serviceAccount: serviceAccount, ); final result = Map.from(response['result'] as Map); return FirebaseApp.fromJson({ @@ -372,6 +392,7 @@ Future createAndroidApp({ required String packageName, String? account, String? token, + String? serviceAccount, }) async { final response = await runFirebaseCommand( [ @@ -383,6 +404,7 @@ Future createAndroidApp({ ], project: project, account: account, + serviceAccount: serviceAccount, ); final result = Map.from(response['result'] as Map); return FirebaseApp.fromJson({ @@ -398,6 +420,7 @@ Future createAppleApp({ required String bundleId, String? account, String? token, + String? serviceAccount, }) async { final response = await runFirebaseCommand( [ @@ -409,6 +432,7 @@ Future createAppleApp({ ], project: project, account: account, + serviceAccount: serviceAccount, ); final result = Map.from(response['result'] as Map); return FirebaseApp.fromJson({ diff --git a/packages/flutterfire_cli/lib/src/firebase/firebase_android_options.dart b/packages/flutterfire_cli/lib/src/firebase/firebase_android_options.dart index 2a77b41f..bad6ce42 100644 --- a/packages/flutterfire_cli/lib/src/firebase/firebase_android_options.dart +++ b/packages/flutterfire_cli/lib/src/firebase/firebase_android_options.dart @@ -38,6 +38,7 @@ extension FirebaseAndroidOptions on FirebaseOptions { required String firebaseProjectId, String? firebaseAccount, required String? token, + required String? serviceAccount, }) async { var selectedAndroidApplicationId = androidApplicationId ?? flutterApp.androidApplicationId; @@ -52,12 +53,14 @@ extension FirebaseAndroidOptions on FirebaseOptions { project: firebaseProjectId, account: firebaseAccount, token: token, + serviceAccount: serviceAccount, ); final appSdkConfig = await firebase.getAppSdkConfig( appId: firebaseApp.appId, platform: kAndroid, account: firebaseAccount, token: token, + serviceAccount: serviceAccount, ); return convertConfigToOptions( diff --git a/packages/flutterfire_cli/lib/src/firebase/firebase_apple_options.dart b/packages/flutterfire_cli/lib/src/firebase/firebase_apple_options.dart index 61d86221..27977fb3 100644 --- a/packages/flutterfire_cli/lib/src/firebase/firebase_apple_options.dart +++ b/packages/flutterfire_cli/lib/src/firebase/firebase_apple_options.dart @@ -31,6 +31,7 @@ extension FirebaseAppleOptions on FirebaseOptions { required String firebaseProjectId, String? firebaseAccount, required String? token, + required String? serviceAccount, }) async { final platformIdentifier = macos ? kMacos : kIos; var selectedAppleBundleId = appleBundleIdentifier ?? @@ -51,12 +52,14 @@ extension FirebaseAppleOptions on FirebaseOptions { project: firebaseProjectId, account: firebaseAccount, token: token, + serviceAccount: serviceAccount, ); final appSdkConfig = await firebase.getAppSdkConfig( appId: firebaseApp.appId, platform: platformIdentifier, account: firebaseAccount, token: token, + serviceAccount: serviceAccount, ); return convertConfigToOptions( diff --git a/packages/flutterfire_cli/lib/src/firebase/firebase_dart_options.dart b/packages/flutterfire_cli/lib/src/firebase/firebase_dart_options.dart index 6588acd4..31aaa449 100644 --- a/packages/flutterfire_cli/lib/src/firebase/firebase_dart_options.dart +++ b/packages/flutterfire_cli/lib/src/firebase/firebase_dart_options.dart @@ -31,6 +31,7 @@ extension FirebaseDartOptions on FirebaseOptions { String? webAppId, String platform = kWeb, required String? token, + required String? serviceAccount, }) async { final firebaseApp = await firebase.findOrCreateFirebaseApp( displayName: flutterApp.package.pubSpec.name ?? 'flutterfire_app', @@ -38,6 +39,7 @@ extension FirebaseDartOptions on FirebaseOptions { project: firebaseProjectId, account: firebaseAccount, token: token, + serviceAccount: serviceAccount, webAppId: webAppId, ); final appSdkConfig = await firebase.getAppSdkConfig( @@ -45,6 +47,7 @@ extension FirebaseDartOptions on FirebaseOptions { platform: kWeb, account: firebaseAccount, token: token, + serviceAccount: serviceAccount, ); return convertConfigToOptions(appSdkConfig, firebaseProjectId); diff --git a/packages/flutterfire_cli/lib/src/firebase/firebase_platform_options.dart b/packages/flutterfire_cli/lib/src/firebase/firebase_platform_options.dart index 57bbdfd0..fd32eef7 100644 --- a/packages/flutterfire_cli/lib/src/firebase/firebase_platform_options.dart +++ b/packages/flutterfire_cli/lib/src/firebase/firebase_platform_options.dart @@ -38,6 +38,7 @@ Future fetchAllFirebaseOptions({ String? iosBundleId, String? macosBundleId, String? token, + String? serviceAccount, }) async { FirebaseOptions? androidOptions; FirebaseOptions? iosOptions; @@ -53,6 +54,7 @@ Future fetchAllFirebaseOptions({ firebaseProjectId: firebaseProjectId, firebaseAccount: firebaseAccount, token: token, + serviceAccount: serviceAccount, ); } @@ -63,6 +65,7 @@ Future fetchAllFirebaseOptions({ firebaseProjectId: firebaseProjectId, firebaseAccount: firebaseAccount, token: token, + serviceAccount: serviceAccount, ); } if (macos) { @@ -73,6 +76,7 @@ Future fetchAllFirebaseOptions({ firebaseAccount: firebaseAccount, macos: true, token: token, + serviceAccount: serviceAccount, ); } @@ -83,6 +87,7 @@ Future fetchAllFirebaseOptions({ firebaseAccount: firebaseAccount, webAppId: webAppId, token: token, + serviceAccount: serviceAccount, ); } @@ -93,6 +98,7 @@ Future fetchAllFirebaseOptions({ firebaseAccount: firebaseAccount, platform: kWindows, token: token, + serviceAccount: serviceAccount, ); } @@ -103,6 +109,7 @@ Future fetchAllFirebaseOptions({ firebaseAccount: firebaseAccount, platform: kLinux, token: token, + serviceAccount: serviceAccount, ); }