@_nazmiforreal/flutter-ota 0.1.21 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/bin/flutter-ota-linux-x64 +0 -0
  2. package/bin/flutter-ota.js +85 -0
  3. package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +7 -4
  4. package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +95 -32
  5. package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +28 -12
  6. package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +9 -4
  7. package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +29 -6
  8. package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +20 -32
  9. package/dart-src/packages/cli-tools/lib/src/commands/init.dart +40 -19
  10. package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +7 -5
  11. package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +33 -22
  12. package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +256 -68
  13. package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +17 -11
  14. package/dart-src/packages/cli-tools/lib/src/commands/storage.dart +28 -12
  15. package/dart-src/packages/cli-tools/lib/src/config.dart +5 -6
  16. package/dart-src/packages/cli-tools/lib/src/operations.dart +1 -3
  17. package/dart-src/packages/cli-tools/lib/src/pocketbase/process_manager.dart +17 -16
  18. package/dart-src/packages/cli-tools/lib/src/runner.dart +3 -1
  19. package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +8 -14
  20. package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +7 -3
  21. package/dart-src/packages/cli-tools/test/pocketbase_schema_test.dart +13 -16
  22. package/dart-src/packages/core/CHANGELOG.md +15 -0
  23. package/dart-src/packages/core/lib/src/bundle.dart +3 -6
  24. package/dart-src/packages/core/pubspec.yaml +1 -1
  25. package/dart-src/plugins/aws/CHANGELOG.md +18 -0
  26. package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +1 -2
  27. package/dart-src/plugins/aws/pubspec.yaml +3 -3
  28. package/dart-src/plugins/cloudflare/CHANGELOG.md +18 -0
  29. package/dart-src/plugins/cloudflare/pubspec.yaml +3 -3
  30. package/dart-src/plugins/plugin-core/CHANGELOG.md +15 -0
  31. package/dart-src/plugins/plugin-core/pubspec.yaml +2 -2
  32. package/dart-src/plugins/pocketbase/CHANGELOG.md +18 -0
  33. package/dart-src/plugins/pocketbase/pubspec.yaml +3 -3
  34. package/dart-src/plugins/postgres/CHANGELOG.md +18 -0
  35. package/dart-src/plugins/postgres/pubspec.yaml +3 -3
  36. package/dart-src/plugins/supabase/CHANGELOG.md +18 -0
  37. package/dart-src/plugins/supabase/pubspec.yaml +3 -3
  38. package/package.json +2 -2
  39. package/scripts/postinstall.js +54 -2
Binary file
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // Launcher for the flutter_ota_kit CLI.
5
+ //
6
+ // Resolves a platform+arch-specific prebuilt binary (`flutter-ota-<os>-<arch>`)
7
+ // shipped next to this script, falling back to `dart run` of the bundled Dart
8
+ // source when no prebuilt exists for the current architecture (e.g. a user on
9
+ // arm64 with only an x64 binary present). This keeps the package installable on
10
+ // any platform that has the Dart SDK, without us needing to ship every binary.
11
+
12
+ const { spawnSync } = require('child_process');
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ function platform() {
17
+ if (process.platform === 'win32') return 'windows';
18
+ if (process.platform === 'darwin') return 'macos';
19
+ return 'linux';
20
+ }
21
+
22
+ function arch() {
23
+ // Only x64/arm64 matter for Dart AOT; everything else falls back to Dart VM.
24
+ return process.arch === 'arm64' ? 'arm64' : 'x64';
25
+ }
26
+
27
+ const platformName = platform();
28
+ const archName = arch();
29
+ const ext = process.platform === 'win32' ? '.exe' : '';
30
+ const binDir = __dirname;
31
+ const binName = `flutter-ota-${platformName}-${archName}${ext}`;
32
+ const binPath = path.join(binDir, binName);
33
+
34
+ const sourceEntry = path.join(
35
+ binDir, '..', 'dart-src', 'packages', 'cli-tools', 'bin', 'flutter_ota_kit.dart',
36
+ );
37
+ const cliDir = path.dirname(path.dirname(sourceEntry));
38
+
39
+ function chmodIfNeeded(p) {
40
+ try {
41
+ const st = fs.statSync(p);
42
+ // Ensure owner-executable bit is set.
43
+ if (!(st.mode & 0o100)) fs.chmodSync(p, st.mode | 0o755);
44
+ } catch (_) {
45
+ // Non-fatal.
46
+ }
47
+ }
48
+
49
+ function run(bin, args) {
50
+ const res = spawnSync(bin, args, { stdio: 'inherit', windowsHide: false });
51
+ process.exit(res.status == null ? 1 : res.status);
52
+ }
53
+
54
+ if (fs.existsSync(binPath)) {
55
+ chmodIfNeeded(binPath);
56
+ run(binPath, process.argv.slice(2));
57
+ } else if (
58
+ spawnSync('dart', ['--version'], { stdio: 'ignore' }).status === 0 &&
59
+ fs.existsSync(sourceEntry)
60
+ ) {
61
+ // Ensure dependencies are resolved before running from source. We only run
62
+ // `dart pub get` when the package config is missing, so repeated invocations
63
+ // don't re-resolve dependencies on every command.
64
+ const configPath = path.join(cliDir, '.dart_tool', 'package_config.json');
65
+ if (!fs.existsSync(configPath)) {
66
+ const getRes = spawnSync('dart', ['pub', 'get'], { stdio: 'inherit', cwd: cliDir });
67
+ if (getRes.status !== 0) {
68
+ console.error(
69
+ 'flutter-ota: `dart pub get` failed. Install dependencies or build ' +
70
+ `a prebuilt manually:\n cd ${cliDir} && dart compile exe ` +
71
+ `bin/flutter_ota_kit.dart -o ${binPath}`,
72
+ );
73
+ process.exit(1);
74
+ }
75
+ }
76
+ chmodIfNeeded(binPath);
77
+ run('dart', [sourceEntry, ...process.argv.slice(2)]);
78
+ } else {
79
+ console.error(
80
+ `flutter-ota: no prebuilt binary for ${platformName}-${archName} and ` +
81
+ 'the Dart SDK was not found. Install Dart (https://dart.dev) or use a ' +
82
+ 'platform/arch with a shipped prebuilt binary.',
83
+ );
84
+ process.exit(1);
85
+ }
@@ -103,8 +103,8 @@ Future<int> run(List<String> args) async {
103
103
  }
104
104
 
105
105
  // Unknown command → fuzzy-match and suggest.
106
- final unknownMatch =
107
- RegExp(r'Could not find a command named "?(\w+)"?').firstMatch(msg);
106
+ final unknownMatch = RegExp(r'Could not find a command named "?(\w+)"?')
107
+ .firstMatch(msg);
108
108
  if (unknownMatch != null) {
109
109
  final typed = unknownMatch.group(1)!;
110
110
  _printUnknownCommand(runner, typed);
@@ -191,7 +191,9 @@ void _printUnknownCommand(FlutterPatcherRunner runner, String typed) {
191
191
  .where((s) => s.startsWith(sub) || _levenshtein(sub, s) <= 2)
192
192
  .toList();
193
193
  if (subSuggestion.isNotEmpty) {
194
- stderr.writeln(' ${_cyan('Did you mean?')} $parentCmd ${_green(subSuggestion.first)}');
194
+ stderr.writeln(
195
+ ' ${_cyan('Did you mean?')} $parentCmd ${_green(subSuggestion.first)}',
196
+ );
195
197
  stderr.writeln('');
196
198
  }
197
199
  cmd.printUsage();
@@ -283,7 +285,8 @@ bool get _noColor {
283
285
  return v != null && v.isNotEmpty;
284
286
  }
285
287
 
286
- bool get _colorOn => !_noColor && stderr.hasTerminal && stderr.supportsAnsiEscapes;
288
+ bool get _colorOn =>
289
+ !_noColor && stderr.hasTerminal && stderr.supportsAnsiEscapes;
287
290
 
288
291
  String _red(String s) => _colorOn ? '\x1b[31m$s\x1b[0m' : s;
289
292
  String _green(String s) => _colorOn ? '\x1b[32m$s\x1b[0m' : s;
@@ -66,13 +66,22 @@ class BundleCommand extends FlutterPatcherCommand {
66
66
  class BundleListCommand extends FlutterPatcherCommand {
67
67
  BundleListCommand({this.config, this.backendOverride}) {
68
68
  final detected = config?.provider ?? loadConfig()?.provider;
69
- argParser.addOption('backend', abbr: 'b', help: detected != null
70
- ? 'Backend provider [detected: $detected].'
71
- : 'Backend provider.');
69
+ argParser.addOption(
70
+ 'backend',
71
+ abbr: 'b',
72
+ help: detected != null
73
+ ? 'Backend provider [detected: $detected].'
74
+ : 'Backend provider.',
75
+ );
72
76
  argParser.addOption('channel', abbr: 'c', help: 'Filter by channel.');
73
77
  argParser.addOption('platform', abbr: 'p', help: 'Filter by platform.');
74
78
  argParser.addOption('enabled', help: 'Filter by enabled (true/false).');
75
- argParser.addOption('limit', abbr: 'l', defaultsTo: '20', help: 'Page size.');
79
+ argParser.addOption(
80
+ 'limit',
81
+ abbr: 'l',
82
+ defaultsTo: '20',
83
+ help: 'Page size.',
84
+ );
76
85
  }
77
86
 
78
87
  final FlutterPatcherConfig? config;
@@ -95,7 +104,10 @@ class BundleListCommand extends FlutterPatcherCommand {
95
104
  final limitRaw = argResults!['limit'] as String;
96
105
  final limit = int.tryParse(limitRaw);
97
106
  if (limit == null || limit < 1) {
98
- throw PackException('--limit must be a positive integer (got "$limitRaw")', 64);
107
+ throw PackException(
108
+ '--limit must be a positive integer (got "$limitRaw")',
109
+ 64,
110
+ );
99
111
  }
100
112
  final res = await listBundles(
101
113
  backend,
@@ -116,27 +128,29 @@ class BundleListCommand extends FlutterPatcherCommand {
116
128
  final b = res.data[i];
117
129
  final enabled = b.enabled ? green('✓') : red('✗');
118
130
  final force = b.shouldForceUpdate ? green('✓') : dim('✗');
119
- rows.add([
120
- '$i',
121
- cyan(b.id),
122
- b.channel,
123
- b.platform.value,
124
- force,
125
- enabled,
126
- ]);
131
+ rows.add(['$i', cyan(b.id), b.channel, b.platform.value, force, enabled]);
127
132
  }
128
- table(
129
- '${res.data.length} bundles (total: ${res.pagination.total})',
130
- ['#', 'ID', 'CHANNEL', 'PLAT', 'FORCE', 'ON'],
131
- rows,
132
- );
133
+ table('${res.data.length} bundles (total: ${res.pagination.total})', [
134
+ '#',
135
+ 'ID',
136
+ 'CHANNEL',
137
+ 'PLAT',
138
+ 'FORCE',
139
+ 'ON',
140
+ ], rows);
133
141
  });
134
142
  }
135
143
 
136
144
  class BundleShowCommand extends FlutterPatcherCommand {
137
145
  BundleShowCommand({this.config, this.backendOverride}) {
138
146
  final detected = config?.provider ?? loadConfig()?.provider;
139
- argParser.addOption('backend', abbr: 'b', help: detected != null ? 'Backend provider [detected: $detected].' : 'Backend provider.');
147
+ argParser.addOption(
148
+ 'backend',
149
+ abbr: 'b',
150
+ help: detected != null
151
+ ? 'Backend provider [detected: $detected].'
152
+ : 'Backend provider.',
153
+ );
140
154
  argParser.addOption('id', help: 'Bundle id.');
141
155
  }
142
156
 
@@ -180,7 +194,13 @@ class BundleShowCommand extends FlutterPatcherCommand {
180
194
  class BundleDeleteCommand extends FlutterPatcherCommand {
181
195
  BundleDeleteCommand({this.config, this.backendOverride}) {
182
196
  final detected = config?.provider ?? loadConfig()?.provider;
183
- argParser.addOption('backend', abbr: 'b', help: detected != null ? 'Backend provider [detected: $detected].' : 'Backend provider.');
197
+ argParser.addOption(
198
+ 'backend',
199
+ abbr: 'b',
200
+ help: detected != null
201
+ ? 'Backend provider [detected: $detected].'
202
+ : 'Backend provider.',
203
+ );
184
204
  argParser.addOption('id', help: 'Bundle id.');
185
205
  argParser.addFlag(
186
206
  'keep-storage',
@@ -213,8 +233,10 @@ class BundleDeleteCommand extends FlutterPatcherCommand {
213
233
  final steps = Steps('delete');
214
234
  await steps.run('Deleting bundle $id', () => deleteBundle(backend, id));
215
235
  if (!keepStorage && existing.storageUri.isNotEmpty) {
216
- await steps.run('Removing storage object',
217
- () => backend.storage.delete(existing.storageUri));
236
+ await steps.run(
237
+ 'Removing storage object',
238
+ () => backend.storage.delete(existing.storageUri),
239
+ );
218
240
  }
219
241
  steps.summary();
220
242
  });
@@ -223,7 +245,13 @@ class BundleDeleteCommand extends FlutterPatcherCommand {
223
245
  class BundleDisableCommand extends FlutterPatcherCommand {
224
246
  BundleDisableCommand({this.config, this.backendOverride}) {
225
247
  final detected = config?.provider ?? loadConfig()?.provider;
226
- argParser.addOption('backend', abbr: 'b', help: detected != null ? 'Backend provider [detected: $detected].' : 'Backend provider.');
248
+ argParser.addOption(
249
+ 'backend',
250
+ abbr: 'b',
251
+ help: detected != null
252
+ ? 'Backend provider [detected: $detected].'
253
+ : 'Backend provider.',
254
+ );
227
255
  argParser.addOption('id', help: 'Bundle id.');
228
256
  }
229
257
 
@@ -255,7 +283,13 @@ class BundleDisableCommand extends FlutterPatcherCommand {
255
283
  class BundleEnableCommand extends FlutterPatcherCommand {
256
284
  BundleEnableCommand({this.config, this.backendOverride}) {
257
285
  final detected = config?.provider ?? loadConfig()?.provider;
258
- argParser.addOption('backend', abbr: 'b', help: detected != null ? 'Backend provider [detected: $detected].' : 'Backend provider.');
286
+ argParser.addOption(
287
+ 'backend',
288
+ abbr: 'b',
289
+ help: detected != null
290
+ ? 'Backend provider [detected: $detected].'
291
+ : 'Backend provider.',
292
+ );
259
293
  argParser.addOption('id', help: 'Bundle id.');
260
294
  }
261
295
 
@@ -287,7 +321,13 @@ class BundleEnableCommand extends FlutterPatcherCommand {
287
321
  class BundleForceCommand extends FlutterPatcherCommand {
288
322
  BundleForceCommand({this.config, this.backendOverride}) {
289
323
  final detected = config?.provider ?? loadConfig()?.provider;
290
- argParser.addOption('backend', abbr: 'b', help: detected != null ? 'Backend provider [detected: $detected].' : 'Backend provider.');
324
+ argParser.addOption(
325
+ 'backend',
326
+ abbr: 'b',
327
+ help: detected != null
328
+ ? 'Backend provider [detected: $detected].'
329
+ : 'Backend provider.',
330
+ );
291
331
  argParser.addOption('id', help: 'Bundle id.');
292
332
  argParser.addFlag(
293
333
  'off',
@@ -330,7 +370,13 @@ class BundleForceCommand extends FlutterPatcherCommand {
330
370
  class BundlePromoteCommand extends FlutterPatcherCommand {
331
371
  BundlePromoteCommand({this.config, this.backendOverride}) {
332
372
  final detected = config?.provider ?? loadConfig()?.provider;
333
- argParser.addOption('backend', abbr: 'b', help: detected != null ? 'Backend provider [detected: $detected].' : 'Backend provider.');
373
+ argParser.addOption(
374
+ 'backend',
375
+ abbr: 'b',
376
+ help: detected != null
377
+ ? 'Backend provider [detected: $detected].'
378
+ : 'Backend provider.',
379
+ );
334
380
  argParser.addOption('id', help: 'Bundle id.');
335
381
  argParser.addOption('channel', abbr: 'c', help: 'Target channel.');
336
382
  }
@@ -359,22 +405,39 @@ class BundlePromoteCommand extends FlutterPatcherCommand {
359
405
  final backend = requireBackend(cfg, override: backendOverride);
360
406
  banner('bundle · promote');
361
407
  final steps = Steps('promote');
362
- await steps.run('Promoting $id to $channel',
363
- () => promoteBundle(backend, id!, channel));
408
+ await steps.run(
409
+ 'Promoting $id to $channel',
410
+ () => promoteBundle(backend, id!, channel),
411
+ );
364
412
  steps.summary();
365
- stdout.writeln(' ${dim('→')} bundle ${cyan(id!)} → channel ${cyan(channel)}');
413
+ stdout.writeln(
414
+ ' ${dim('→')} bundle ${cyan(id!)} → channel ${cyan(channel)}',
415
+ );
366
416
  });
367
417
  }
368
418
 
369
419
  class BundleUpdateCommand extends FlutterPatcherCommand {
370
420
  BundleUpdateCommand({this.config, this.backendOverride}) {
371
421
  final detected = config?.provider ?? loadConfig()?.provider;
372
- argParser.addOption('backend', abbr: 'b', help: detected != null ? 'Backend provider [detected: $detected].' : 'Backend provider.');
422
+ argParser.addOption(
423
+ 'backend',
424
+ abbr: 'b',
425
+ help: detected != null
426
+ ? 'Backend provider [detected: $detected].'
427
+ : 'Backend provider.',
428
+ );
373
429
  argParser.addOption('id', help: 'Bundle id.');
374
430
  argParser.addOption('message', abbr: 'm', help: 'New release message.');
375
- argParser.addOption('target-version', help: 'New target app version (e.g. 1.0.0).');
431
+ argParser.addOption(
432
+ 'target-version',
433
+ help: 'New target app version (e.g. 1.0.0).',
434
+ );
376
435
  argParser.addOption('enabled', help: 'Set enabled (true/false).');
377
- argParser.addOption('force', abbr: 'f', help: 'Set force-update (true/false).');
436
+ argParser.addOption(
437
+ 'force',
438
+ abbr: 'f',
439
+ help: 'Set force-update (true/false).',
440
+ );
378
441
  }
379
442
 
380
443
  final FlutterPatcherConfig? config;
@@ -31,9 +31,13 @@ class ChannelCommand extends FlutterPatcherCommand {
31
31
  class ChannelListCommand extends FlutterPatcherCommand {
32
32
  ChannelListCommand({this.config, this.backendOverride}) {
33
33
  final detected = config?.provider ?? loadConfig()?.provider;
34
- argParser.addOption('backend', abbr: 'b', help: detected != null
35
- ? 'Backend provider [detected: $detected].'
36
- : 'Backend provider.');
34
+ argParser.addOption(
35
+ 'backend',
36
+ abbr: 'b',
37
+ help: detected != null
38
+ ? 'Backend provider [detected: $detected].'
39
+ : 'Backend provider.',
40
+ );
37
41
  }
38
42
 
39
43
  final FlutterPatcherConfig? config;
@@ -62,9 +66,13 @@ class ChannelListCommand extends FlutterPatcherCommand {
62
66
  class ChannelGetCommand extends FlutterPatcherCommand {
63
67
  ChannelGetCommand({this.config, this.backendOverride}) {
64
68
  final detected = config?.provider ?? loadConfig()?.provider;
65
- argParser.addOption('backend', abbr: 'b', help: detected != null
66
- ? 'Backend provider [detected: $detected].'
67
- : 'Backend provider.');
69
+ argParser.addOption(
70
+ 'backend',
71
+ abbr: 'b',
72
+ help: detected != null
73
+ ? 'Backend provider [detected: $detected].'
74
+ : 'Backend provider.',
75
+ );
68
76
  argParser.addOption('channel', abbr: 'c', help: 'Channel.');
69
77
  }
70
78
 
@@ -109,9 +117,13 @@ class ChannelGetCommand extends FlutterPatcherCommand {
109
117
  class ChannelSetCommand extends FlutterPatcherCommand {
110
118
  ChannelSetCommand({this.config, this.backendOverride}) {
111
119
  final detected = config?.provider ?? loadConfig()?.provider;
112
- argParser.addOption('backend', abbr: 'b', help: detected != null
113
- ? 'Backend provider [detected: $detected].'
114
- : 'Backend provider.');
120
+ argParser.addOption(
121
+ 'backend',
122
+ abbr: 'b',
123
+ help: detected != null
124
+ ? 'Backend provider [detected: $detected].'
125
+ : 'Backend provider.',
126
+ );
115
127
  argParser.addOption('channel', abbr: 'c', help: 'Channel.');
116
128
  argParser.addOption('bundle-id', abbr: 'i', help: 'Bundle id.');
117
129
  }
@@ -142,9 +154,13 @@ class ChannelSetCommand extends FlutterPatcherCommand {
142
154
  final backend = requireBackend(cfg, override: backendOverride);
143
155
  banner('channel · set');
144
156
  final steps = Steps('set');
145
- await steps.run('Promoting $bundleId to $channel',
146
- () => promoteBundle(backend, bundleId, channel));
157
+ await steps.run(
158
+ 'Promoting $bundleId to $channel',
159
+ () => promoteBundle(backend, bundleId, channel),
160
+ );
147
161
  steps.summary();
148
- stdout.writeln(' ${dim('→')} channel ${cyan(channel)} = ${cyan(bundleId)}');
162
+ stdout.writeln(
163
+ ' ${dim('→')} channel ${cyan(channel)} = ${cyan(bundleId)}',
164
+ );
149
165
  });
150
166
  }
@@ -52,7 +52,8 @@ class ConfigGetCommand extends FlutterPatcherCommand {
52
52
  @override
53
53
  Future<int> run() => runGuarded(() async {
54
54
  ui.banner('config · get');
55
- final key = argResults!['key'] as String? ??
55
+ final key =
56
+ argResults!['key'] as String? ??
56
57
  (argResults!.rest.isNotEmpty ? argResults!.rest.first : null);
57
58
  if (key == null || key.isEmpty) {
58
59
  throw StateError('Usage: flutter-ota config get <key>');
@@ -82,9 +83,11 @@ class ConfigSetCommand extends FlutterPatcherCommand {
82
83
 
83
84
  @override
84
85
  Future<int> run() => runGuarded(() async {
85
- final key = argResults!['key'] as String? ??
86
+ final key =
87
+ argResults!['key'] as String? ??
86
88
  (argResults!.rest.isNotEmpty ? argResults!.rest.first : null);
87
- final value = argResults!['value'] as String? ??
89
+ final value =
90
+ argResults!['value'] as String? ??
88
91
  (argResults!.rest.length > 1 ? argResults!.rest[1] : null);
89
92
  if (key == null || key.isEmpty || value == null) {
90
93
  throw StateError('Usage: flutter-ota config set <key> <value>');
@@ -124,7 +127,9 @@ void _printConfig(Map<String, dynamic> json, String prefix) {
124
127
  if (value is Map<String, dynamic>) {
125
128
  _printConfig(value, key);
126
129
  } else {
127
- final display = value is String ? value : const JsonEncoder().convert(value);
130
+ final display = value is String
131
+ ? value
132
+ : const JsonEncoder().convert(value);
128
133
  stdout.writeln(ui.kv(key, display));
129
134
  }
130
135
  }
@@ -21,9 +21,18 @@ class DeployCommand extends FlutterPatcherCommand {
21
21
  ? 'Backend provider [detected: $detected].'
22
22
  : 'Backend provider.',
23
23
  );
24
- argParser.addOption('source', abbr: 's', help: 'Source directory to zip + upload.');
24
+ argParser.addOption(
25
+ 'source',
26
+ abbr: 's',
27
+ help: 'Source directory to zip + upload.',
28
+ );
25
29
  argParser.addOption('channel', abbr: 'c', help: 'Target channel.');
26
- argParser.addOption('platform', abbr: 'p', defaultsTo: 'android', help: 'Platform.');
30
+ argParser.addOption(
31
+ 'platform',
32
+ abbr: 'p',
33
+ defaultsTo: 'android',
34
+ help: 'Platform.',
35
+ );
27
36
  argParser.addOption('message', abbr: 'm', help: 'Release message.');
28
37
  argParser.addFlag('force', abbr: 'f', help: 'Force the update on clients.');
29
38
  argParser.addOption(
@@ -39,7 +48,10 @@ class DeployCommand extends FlutterPatcherCommand {
39
48
  abbr: 'k',
40
49
  help: 'Path to Ed25519 private key file (sign bundle).',
41
50
  );
42
- argParser.addOption('git-commit-hash', help: 'Git commit hash (auto-detected).');
51
+ argParser.addOption(
52
+ 'git-commit-hash',
53
+ help: 'Git commit hash (auto-detected).',
54
+ );
43
55
  argParser.addOption(
44
56
  'bundle-id',
45
57
  abbr: 'i',
@@ -87,9 +99,20 @@ class DeployCommand extends FlutterPatcherCommand {
87
99
  banner('deploy');
88
100
 
89
101
  final steps = Steps('deploy');
90
- final bundle = await _deployWithPhases(steps, backend, source, channel,
91
- platform, message, force, targetAppVersion, fingerprintHash,
92
- signingKey, resolvedGitCommitHash, bundleId);
102
+ final bundle = await _deployWithPhases(
103
+ steps,
104
+ backend,
105
+ source,
106
+ channel,
107
+ platform,
108
+ message,
109
+ force,
110
+ targetAppVersion,
111
+ fingerprintHash,
112
+ signingKey,
113
+ resolvedGitCommitHash,
114
+ bundleId,
115
+ );
93
116
  steps.summary();
94
117
 
95
118
  final lines = <String>[
@@ -11,9 +11,13 @@ import '../ui/ui.dart';
11
11
  class DoctorCommand extends FlutterPatcherCommand {
12
12
  DoctorCommand({this.config, this.backendOverride}) {
13
13
  final detected = config?.provider ?? loadConfig()?.provider;
14
- argParser.addOption('backend', abbr: 'b', help: detected != null
15
- ? 'Backend provider [detected: $detected].'
16
- : 'Backend provider.');
14
+ argParser.addOption(
15
+ 'backend',
16
+ abbr: 'b',
17
+ help: detected != null
18
+ ? 'Backend provider [detected: $detected].'
19
+ : 'Backend provider.',
20
+ );
17
21
  }
18
22
 
19
23
  final FlutterPatcherConfig? config;
@@ -59,9 +63,7 @@ class DoctorCommand extends FlutterPatcherCommand {
59
63
  final time = ms >= 1000
60
64
  ? '${(ms / 1000).toStringAsFixed(1)}s'
61
65
  : '${ms}ms';
62
- steps.success(
63
- 'Backend reachable — ${channels.join(', ')} in $time',
64
- );
66
+ steps.success('Backend reachable — ${channels.join(', ')} in $time');
65
67
  } catch (e) {
66
68
  steps.fail('Backend unreachable — $e');
67
69
  }
@@ -85,19 +87,16 @@ class DoctorCommand extends FlutterPatcherCommand {
85
87
  }
86
88
  }
87
89
 
88
- Future<void> _checkSupabase(
89
- FlutterPatcherConfig cfg,
90
- Steps steps,
91
- ) async {
90
+ Future<void> _checkSupabase(FlutterPatcherConfig cfg, Steps steps) async {
92
91
  final url = cfg.supabase.url;
93
92
  if (url == null || url.isEmpty) {
94
93
  steps.fail('SUPABASE_URL not set');
95
94
  return;
96
95
  }
97
96
  try {
98
- final res = await http.get(Uri.parse('$url/rest/v1/')).timeout(
99
- const Duration(seconds: 5),
100
- );
97
+ final res = await http
98
+ .get(Uri.parse('$url/rest/v1/'))
99
+ .timeout(const Duration(seconds: 5));
101
100
  if (res.statusCode < 400) {
102
101
  steps.success('Supabase reachable ($url)');
103
102
  } else {
@@ -108,10 +107,7 @@ class DoctorCommand extends FlutterPatcherCommand {
108
107
  }
109
108
  }
110
109
 
111
- Future<void> _checkPostgres(
112
- FlutterPatcherConfig cfg,
113
- Steps steps,
114
- ) async {
110
+ Future<void> _checkPostgres(FlutterPatcherConfig cfg, Steps steps) async {
115
111
  final host = cfg.postgres.host;
116
112
  if (host == null || host.isEmpty) {
117
113
  steps.fail('POSTGRES_HOST not set');
@@ -119,9 +115,10 @@ class DoctorCommand extends FlutterPatcherCommand {
119
115
  }
120
116
  final port = int.tryParse(cfg.postgres.port ?? '5432') ?? 5432;
121
117
  try {
122
- final socket = await Socket.connect(host, port).timeout(
123
- const Duration(seconds: 5),
124
- );
118
+ final socket = await Socket.connect(
119
+ host,
120
+ port,
121
+ ).timeout(const Duration(seconds: 5));
125
122
  await socket.close();
126
123
  steps.success('Postgres reachable ($host:$port)');
127
124
  } catch (e) {
@@ -129,10 +126,7 @@ class DoctorCommand extends FlutterPatcherCommand {
129
126
  }
130
127
  }
131
128
 
132
- Future<void> _checkCloudflare(
133
- FlutterPatcherConfig cfg,
134
- Steps steps,
135
- ) async {
129
+ Future<void> _checkCloudflare(FlutterPatcherConfig cfg, Steps steps) async {
136
130
  final accountId = cfg.cloudflare.accountId;
137
131
  final apiToken = cfg.cloudflare.apiToken;
138
132
  if (accountId == null || accountId.isEmpty) {
@@ -168,10 +162,7 @@ class DoctorCommand extends FlutterPatcherCommand {
168
162
  }
169
163
  }
170
164
 
171
- Future<void> _checkAws(
172
- FlutterPatcherConfig cfg,
173
- Steps steps,
174
- ) async {
165
+ Future<void> _checkAws(FlutterPatcherConfig cfg, Steps steps) async {
175
166
  final bucket = cfg.aws.bucket;
176
167
  if (bucket == null || bucket.isEmpty) {
177
168
  steps.fail('AWS_BUCKET not set');
@@ -185,10 +176,7 @@ class DoctorCommand extends FlutterPatcherCommand {
185
176
  steps.success('AWS S3 bucket "$bucket" in $region');
186
177
  }
187
178
 
188
- Future<void> _checkPocketBase(
189
- FlutterPatcherConfig cfg,
190
- Steps steps,
191
- ) async {
179
+ Future<void> _checkPocketBase(FlutterPatcherConfig cfg, Steps steps) async {
192
180
  final paths = PocketBaseInstallPaths.resolve();
193
181
  final installed = await paths.binaryPath.exists();
194
182
  if (installed) {