Skip to content

Commit a91f541

Browse files
feat: allow FFCLI to be installed as a dev dependency (invertase#401)
1 parent 61239e2 commit a91f541

File tree

11 files changed

+247
-27
lines changed

11 files changed

+247
-27
lines changed

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,47 @@ on this path: `.dart_tool/pub/bin/flutterfire_cli_monorepo/*.snapshot`. This wil
3737

3838
## Install
3939

40+
### Global Installation
41+
4042
To install, run the following command:
4143

4244
```bash
4345
dart pub global activate flutterfire_cli
4446
```
4547

48+
### Dev Dependency (Beta)
49+
50+
It is now possible to install as a dev dependency:
51+
52+
```bash
53+
dart pub add --dev flutterfire_cli
54+
```
55+
**important** - remember to deactivate your current global installation of `flutterfire_cli`.
56+
57+
To run flutterfire commands from your dev dependency:
58+
59+
```bash
60+
dart run flutterfire_cli:flutterfire [COMMAND]
61+
```
62+
63+
It might be worth creating an alias in your shell of choice, if you're using zsh shell, it might look like:
64+
65+
```bash
66+
echo 'alias flutterfire="dart run flutterfire_cli:flutterfire"' >> ~/.zshrc
67+
```
68+
69+
This will allow you to run commands like previously from within your Flutter app:
70+
71+
```bash
72+
flutterfire configure
73+
```
74+
75+
Remember to update or open a new terminal window so alias change is applied.
76+
77+
This feature is in Beta, if you find any bugs, please file a new issue on the repo.
78+
79+
#### Installation Requirements for Global and Dev Dependency
80+
4681
- FlutterFire CLI requires the Firebase CLI (`firebase-tools`) to be installed on your local machine, [follow these instructions](https://firebase.google.com/docs/cli) for installation.
4782
- If you're running on a windows machine, we highly recommend you install via npm (i.e. `npm install -g firebase-tools`). The standalone
4883
`firebase-tools` version can cause problems which you can read about [here](https://github.com/invertase/flutterfire_cli/issues/55#issuecomment-1316201478).

packages/flutterfire_cli/lib/src/commands/base.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*
1616
*/
1717

18+
import 'dart:io';
19+
1820
import 'package:args/args.dart';
1921
import 'package:args/command_runner.dart';
2022
import 'package:cli_util/cli_logging.dart';
@@ -65,6 +67,20 @@ abstract class FlutterFireCommand extends Command<void> {
6567
if (flutterApp == null) {
6668
throw FlutterAppRequiredException();
6769
}
70+
_warnUserIfRunningGlobally();
71+
}
72+
73+
// Warns user if they have a dev dependency on flutterfire_cli and are running globally
74+
void _warnUserIfRunningGlobally() {
75+
final scriptPath = Platform.script.toFilePath();
76+
77+
// Check if we're in a .dart_tool directory (dev dependency)
78+
if (!scriptPath.contains('.dart_tool') &&
79+
flutterApp!.dependsOnPackage('flutterfire_cli')) {
80+
logger.stdout(
81+
"If you're trying to run FlutterFire CLI as a dev dependency, you need to run `dart run flutterfire_cli:flutterfire` instead of `flutterfire`",
82+
);
83+
}
6884
}
6985

7086
/// Overridden to support line wrapping when printing usage.

packages/flutterfire_cli/lib/src/commands/config.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ class ConfigCommand extends FlutterFireCommand {
564564
@override
565565
Future<void> run() async {
566566
// Has to set during `run()` otherwise `argResults` will be null
567-
updateDebugMode(argResults!['debug']as bool);
567+
updateDebugMode(argResults!['debug'] as bool);
568568
try {
569569
commandRequiresFlutterApp();
570570
final reconfigured = await checkIfUserRequiresReconfigure();
@@ -659,7 +659,7 @@ class ConfigCommand extends FlutterFireCommand {
659659
iosInputs != null) {
660660
final firebaseJsonWrite = await appleWrites(
661661
platformOptions: fetchedFirebaseOptions.iosOptions!,
662-
flutterAppPath: flutterApp!.package.path,
662+
flutterApp: flutterApp!,
663663
serviceFilePath: iosInputs!.serviceFilePath,
664664
logger: logger,
665665
buildConfiguration: iosInputs?.buildConfiguration,
@@ -676,7 +676,7 @@ class ConfigCommand extends FlutterFireCommand {
676676
macosInputs != null) {
677677
final firebaseJsonWrite = await appleWrites(
678678
platformOptions: fetchedFirebaseOptions.macosOptions!,
679-
flutterAppPath: flutterApp!.package.path,
679+
flutterApp: flutterApp!,
680680
serviceFilePath: macosInputs!.serviceFilePath,
681681
logger: logger,
682682
buildConfiguration: macosInputs?.buildConfiguration,

packages/flutterfire_cli/lib/src/commands/reconfigure.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ class Reconfigure extends FlutterFireCommand {
156156
platform: platform,
157157
logger: logger,
158158
projectConfiguration: ProjectConfiguration.buildConfiguration,
159+
isDevDependency: flutterApp!.dependsOnPackage('flutterfire_cli'),
159160
);
160161

161162
final buildConfigurations = getNestedMap(
@@ -196,6 +197,7 @@ class Reconfigure extends FlutterFireCommand {
196197
platform: platform,
197198
logger: logger,
198199
projectConfiguration: ProjectConfiguration.defaultConfig,
200+
isDevDependency: flutterApp!.dependsOnPackage('flutterfire_cli'),
199201
);
200202

201203
await _writeFile(
@@ -230,6 +232,7 @@ class Reconfigure extends FlutterFireCommand {
230232
platform: platform,
231233
logger: logger,
232234
projectConfiguration: ProjectConfiguration.target,
235+
isDevDependency: flutterApp!.dependsOnPackage('flutterfire_cli'),
233236
);
234237
// ignore: cast_nullable_to_non_nullable
235238
final configuration = targets[key] as Map<String, dynamic>;
@@ -374,6 +377,7 @@ class Reconfigure extends FlutterFireCommand {
374377
@override
375378
Future<void> run() async {
376379
try {
380+
commandRequiresFlutterApp();
377381
final firebaseJson = File(
378382
path.join(
379383
flutterApp!.package.path,

packages/flutterfire_cli/lib/src/common/utils.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ void validateAppBundleId(
515515
String platform,
516516
) {
517517
final bundleIdRegex = RegExp(
518-
r'^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?|([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)+))$',
518+
r'^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?|([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)+))$',
519519
);
520520

521521
if (!bundleIdRegex.hasMatch(bundleId)) {
@@ -541,6 +541,7 @@ void validateAndroidPackageName(String appId) {
541541

542542
String firebaseCLIJsonParse(String output) {
543543
// Sometimes the response has an appended time out exception which breaks parsing
544-
final regex = RegExp(r'\}\s*\{\s*"status":\s*"error",\s*"error":\s*"Timed out."\s*\}');
544+
final regex =
545+
RegExp(r'\}\s*\{\s*"status":\s*"error",\s*"error":\s*"Timed out."\s*\}');
545546
return output.replaceFirst(regex, '}');
546547
}

packages/flutterfire_cli/lib/src/firebase/firebase_apple_writes.dart

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:yaml/yaml.dart';
77

88
import '../common/utils.dart';
99
import '../firebase/firebase_options.dart';
10+
import '../flutter_app.dart';
1011

1112
const debugSymbolScriptName =
1213
'FlutterFire: "flutterfire upload-crashlytics-symbols"';
@@ -15,7 +16,7 @@ const bundleServiceScriptName =
1516

1617
Future<FirebaseJsonWrites> appleWrites({
1718
required String platform,
18-
required String flutterAppPath,
19+
required FlutterApp flutterApp,
1920
required String serviceFilePath,
2021
required FirebaseOptions platformOptions,
2122
required Logger logger,
@@ -27,7 +28,7 @@ Future<FirebaseJsonWrites> appleWrites({
2728
case ProjectConfiguration.buildConfiguration:
2829
return FirebaseAppleBuildConfiguration(
2930
platformOptions: platformOptions,
30-
flutterAppPath: flutterAppPath,
31+
flutterApp: flutterApp,
3132
serviceFilePath: serviceFilePath,
3233
logger: logger,
3334
platform: platform,
@@ -38,7 +39,7 @@ Future<FirebaseJsonWrites> appleWrites({
3839
case ProjectConfiguration.defaultConfig:
3940
return FirebaseAppleTargetConfiguration(
4041
platformOptions: platformOptions,
41-
flutterAppPath: flutterAppPath,
42+
flutterApp: flutterApp,
4243
serviceFilePath: serviceFilePath,
4344
logger: logger,
4445
platform: platform,
@@ -53,15 +54,15 @@ Future<FirebaseJsonWrites> appleWrites({
5354
class FirebaseAppleTargetConfiguration extends FirebaseAppleConfiguration {
5455
FirebaseAppleTargetConfiguration({
5556
required FirebaseOptions platformOptions,
56-
required String flutterAppPath,
57+
required FlutterApp flutterApp,
5758
required String serviceFilePath,
5859
required Logger logger,
5960
required String platform,
6061
required ProjectConfiguration projectConfiguration,
6162
required this.target,
6263
}) : super(
6364
platformOptions: platformOptions,
64-
flutterAppPath: flutterAppPath,
65+
flutterApp: flutterApp,
6566
serviceFilePath: serviceFilePath,
6667
logger: logger,
6768
platform: platform,
@@ -123,10 +124,11 @@ end
123124

124125
final debugSymbolScriptAdded = await addFlutterFireDebugSymbolsScript(
125126
target: target,
126-
flutterAppPath: flutterAppPath,
127+
flutterAppPath: flutterApp.package.path,
127128
logger: logger,
128129
platform: platform,
129130
projectConfiguration: projectConfiguration,
131+
isDevDependency: flutterApp.dependsOnPackage('flutterfire_cli'),
130132
);
131133

132134
return _firebaseJsonWrites(debugSymbolScriptAdded, target);
@@ -141,15 +143,15 @@ end
141143
class FirebaseAppleBuildConfiguration extends FirebaseAppleConfiguration {
142144
FirebaseAppleBuildConfiguration({
143145
required FirebaseOptions platformOptions,
144-
required String flutterAppPath,
146+
required FlutterApp flutterApp,
145147
required String serviceFilePath,
146148
required Logger logger,
147149
required String platform,
148150
required ProjectConfiguration projectConfiguration,
149151
required this.buildConfiguration,
150152
}) : super(
151153
platformOptions: platformOptions,
152-
flutterAppPath: flutterAppPath,
154+
flutterApp: flutterApp,
153155
serviceFilePath: serviceFilePath,
154156
logger: logger,
155157
platform: platform,
@@ -185,7 +187,7 @@ class FirebaseAppleBuildConfiguration extends FirebaseAppleConfiguration {
185187
} else {
186188
// iOS is bundled in the root of the app bundle
187189
command =
188-
'flutterfire bundle-service-file --plist-destination="\${BUILT_PRODUCTS_DIR}/\${PRODUCT_NAME}.app" --build-configuration=\${CONFIGURATION} --platform=$platform --apple-project-path="\${SRCROOT}"';
190+
'${flutterApp.dependsOnPackage('flutterfire_cli') ? 'dart run flutterfire_cli:flutterfire' : 'flutterfire'} bundle-service-file --plist-destination="\${BUILT_PRODUCTS_DIR}/\${PRODUCT_NAME}.app" --build-configuration=\${CONFIGURATION} --platform=$platform --apple-project-path="\${SRCROOT}"';
189191
}
190192

191193
return '''
@@ -226,10 +228,11 @@ end
226228
await _writeGoogleServiceFileToPath();
227229
await _writeBundleServiceFileScriptToProject();
228230
final debugSymbolScriptAdded = await addFlutterFireDebugSymbolsScript(
229-
flutterAppPath: flutterAppPath,
231+
flutterAppPath: flutterApp.package.path,
230232
logger: logger,
231233
projectConfiguration: projectConfiguration,
232234
platform: platform,
235+
isDevDependency: flutterApp.dependsOnPackage('flutterfire_cli'),
233236
);
234237

235238
return _firebaseJsonWrites(
@@ -248,15 +251,15 @@ end
248251
abstract class FirebaseAppleConfiguration {
249252
FirebaseAppleConfiguration({
250253
required this.platformOptions,
251-
required this.flutterAppPath,
254+
required this.flutterApp,
252255
required this.serviceFilePath,
253256
required this.logger,
254257
required this.platform,
255258
required this.projectConfiguration,
256259
});
257260
// Either "ios" or "macos"
258261
final String platform;
259-
final String flutterAppPath;
262+
final FlutterApp flutterApp;
260263
final FirebaseOptions platformOptions;
261264
final String serviceFilePath;
262265
final Logger logger;
@@ -280,7 +283,7 @@ abstract class FirebaseAppleConfiguration {
280283
pathToMap: keysToMap,
281284
projectId: platformOptions.projectId,
282285
appId: platformOptions.appId,
283-
fileOutput: path.relative(serviceFilePath, from: flutterAppPath),
286+
fileOutput: path.relative(serviceFilePath, from: flutterApp.package.path),
284287
uploadDebugSymbols: uploadDebugSymbols,
285288
);
286289
}
@@ -306,6 +309,7 @@ Future<bool> addFlutterFireDebugSymbolsScript({
306309
required Logger logger,
307310
required String platform,
308311
required ProjectConfiguration projectConfiguration,
312+
required bool isDevDependency,
309313
}) async {
310314
final packageConfigContents = File(
311315
path.join(
@@ -352,6 +356,7 @@ Future<bool> addFlutterFireDebugSymbolsScript({
352356
target,
353357
projectConfiguration,
354358
platform,
359+
isDevDependency,
355360
),
356361
]);
357362

@@ -372,6 +377,7 @@ String _debugSymbolsScript(
372377
String target,
373378
ProjectConfiguration projectConfiguration,
374379
String platform,
380+
bool isDevDependency,
375381
) {
376382
final projectType = switch (projectConfiguration) {
377383
ProjectConfiguration.buildConfiguration =>
@@ -401,7 +407,7 @@ else
401407
fi
402408
403409
# Command to upload symbols script used to upload symbols to Firebase server
404-
flutterfire upload-crashlytics-symbols --upload-symbols-script-path="\$PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT" --platform=$platform --apple-project-path="\${SRCROOT}" --env-platform-name="\${PLATFORM_NAME}" --env-configuration="\${CONFIGURATION}" --env-project-dir="\${PROJECT_DIR}" --env-built-products-dir="\${BUILT_PRODUCTS_DIR}" --env-dwarf-dsym-folder-path="\${DWARF_DSYM_FOLDER_PATH}" --env-dwarf-dsym-file-name="\${DWARF_DSYM_FILE_NAME}" --env-infoplist-path="\${INFOPLIST_PATH}" $projectType
410+
${isDevDependency ? 'dart run flutterfire_cli:flutterfire' : 'flutterfire'} upload-crashlytics-symbols --upload-symbols-script-path="\$PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT" --platform=$platform --apple-project-path="\${SRCROOT}" --env-platform-name="\${PLATFORM_NAME}" --env-configuration="\${CONFIGURATION}" --env-project-dir="\${PROJECT_DIR}" --env-built-products-dir="\${BUILT_PRODUCTS_DIR}" --env-dwarf-dsym-folder-path="\${DWARF_DSYM_FOLDER_PATH}" --env-dwarf-dsym-file-name="\${DWARF_DSYM_FILE_NAME}" --env-infoplist-path="\${INFOPLIST_PATH}" $projectType
405411
)
406412
407413
for target in project.targets

packages/flutterfire_cli/lib/src/firebase/firebase_pubspec_model.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ class FirebasePubSpecModel {
77

88
factory FirebasePubSpecModel.fromJson(Map<String, dynamic> json) {
99
return FirebasePubSpecModel(
10-
googleServicesGradlePluginVersion: json['google_services_gradle_plugin_version'],
10+
googleServicesGradlePluginVersion:
11+
json['google_services_gradle_plugin_version'],
1112
crashlyticsGradlePluginVersion: json['crashlytics_gradle_plugin_version'],
1213
performanceGradlePluginVersion: json['performance_gradle_plugin_version'],
1314
);
@@ -16,4 +17,4 @@ class FirebasePubSpecModel {
1617
final String googleServicesGradlePluginVersion;
1718
final String crashlyticsGradlePluginVersion;
1819
final String performanceGradlePluginVersion;
19-
}
20+
}

packages/flutterfire_cli/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ dependencies:
1616
file: ^7.0.0
1717
http: ^1.4.0
1818
interact: ^2.2.0
19-
meta: ^1.17.0
19+
meta: ^1.6.0
2020
path: ^1.8.0
2121
platform: ^3.0.2
2222
pub_updater: ^0.5.0

0 commit comments

Comments
 (0)