@_nazmiforreal/flutter-ota 0.1.20 → 0.1.22
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.
- package/bin/flutter-ota-linux-x64 +0 -0
- package/dart-src/packages/cli-tools/.dart_tool/package_config.json +1 -1
- package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +340 -340
- package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +9 -5
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +3 -2
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +106 -31
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +34 -7
- package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +12 -4
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +7 -8
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +41 -9
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +124 -13
- package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +2 -1
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +58 -28
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +11 -5
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +283 -32
- package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +347 -121
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +18 -9
- package/dart-src/packages/cli-tools/lib/src/commands/storage.dart +32 -15
- package/dart-src/packages/cli-tools/lib/src/config.dart +16 -22
- package/dart-src/packages/cli-tools/lib/src/operations.dart +1 -3
- package/dart-src/packages/cli-tools/lib/src/pocketbase/process_manager.dart +26 -17
- package/dart-src/packages/cli-tools/lib/src/pocketbase/schema_installer.dart +0 -1
- package/dart-src/packages/cli-tools/lib/src/runner.dart +3 -1
- package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +10 -16
- package/dart-src/packages/cli-tools/pubspec.lock +7 -7
- package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +7 -3
- package/dart-src/packages/cli-tools/test/pocketbase_schema_test.dart +13 -16
- package/dart-src/packages/core/CHANGELOG.md +15 -0
- package/dart-src/packages/core/lib/flutter_ota_kit_core.dart +2 -0
- package/dart-src/packages/core/lib/src/app_update_info.dart +71 -1
- package/dart-src/packages/core/lib/src/bundle.dart +59 -17
- package/dart-src/packages/core/lib/src/bundle_artifacts.dart +7 -3
- package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +16 -0
- package/dart-src/packages/core/lib/src/changed_asset.dart +33 -3
- package/dart-src/packages/core/lib/src/get_bundles_args.dart +24 -2
- package/dart-src/packages/core/lib/src/platform.dart +8 -0
- package/dart-src/packages/core/lib/src/semver.dart +0 -3
- package/dart-src/packages/core/lib/src/status.dart +20 -1
- package/dart-src/packages/core/lib/src/strategy.dart +10 -0
- package/dart-src/packages/core/lib/src/update_bundle_params.dart +15 -0
- package/dart-src/packages/core/lib/src/uuid.dart +11 -0
- package/dart-src/packages/core/pubspec.yaml +1 -1
- package/dart-src/plugins/aws/CHANGELOG.md +18 -0
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +0 -1
- package/dart-src/plugins/aws/pubspec.yaml +3 -3
- package/dart-src/plugins/cloudflare/CHANGELOG.md +18 -0
- package/dart-src/plugins/cloudflare/pubspec.yaml +3 -3
- package/dart-src/plugins/plugin-core/CHANGELOG.md +15 -0
- package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +3 -0
- package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +17 -0
- package/dart-src/plugins/plugin-core/lib/src/types.dart +94 -3
- package/dart-src/plugins/plugin-core/pubspec.yaml +2 -2
- package/dart-src/plugins/pocketbase/CHANGELOG.md +18 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_client.dart +3 -0
- package/dart-src/plugins/pocketbase/pubspec.yaml +3 -3
- package/dart-src/plugins/postgres/CHANGELOG.md +18 -0
- package/dart-src/plugins/postgres/pubspec.yaml +3 -3
- package/dart-src/plugins/supabase/CHANGELOG.md +18 -0
- package/dart-src/plugins/supabase/pubspec.yaml +3 -3
- package/package.json +2 -2
- package/scripts/postinstall.js +5 -1
- package/bin/flutter-ota.js +0 -36
|
@@ -38,6 +38,7 @@ export 'src/commands/channel.dart';
|
|
|
38
38
|
export 'src/commands/migrate.dart';
|
|
39
39
|
export 'src/commands/console.dart';
|
|
40
40
|
export 'src/commands/pocketbase.dart';
|
|
41
|
+
export 'src/commands/storage.dart';
|
|
41
42
|
|
|
42
43
|
import 'src/commands/build.dart';
|
|
43
44
|
import 'src/commands/bundle.dart';
|
|
@@ -102,8 +103,8 @@ Future<int> run(List<String> args) async {
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
// Unknown command → fuzzy-match and suggest.
|
|
105
|
-
final unknownMatch =
|
|
106
|
-
|
|
106
|
+
final unknownMatch = RegExp(r'Could not find a command named "?(\w+)"?')
|
|
107
|
+
.firstMatch(msg);
|
|
107
108
|
if (unknownMatch != null) {
|
|
108
109
|
final typed = unknownMatch.group(1)!;
|
|
109
110
|
_printUnknownCommand(runner, typed);
|
|
@@ -190,7 +191,9 @@ void _printUnknownCommand(FlutterPatcherRunner runner, String typed) {
|
|
|
190
191
|
.where((s) => s.startsWith(sub) || _levenshtein(sub, s) <= 2)
|
|
191
192
|
.toList();
|
|
192
193
|
if (subSuggestion.isNotEmpty) {
|
|
193
|
-
stderr.writeln(
|
|
194
|
+
stderr.writeln(
|
|
195
|
+
' ${_cyan('Did you mean?')} $parentCmd ${_green(subSuggestion.first)}',
|
|
196
|
+
);
|
|
194
197
|
stderr.writeln('');
|
|
195
198
|
}
|
|
196
199
|
cmd.printUsage();
|
|
@@ -244,7 +247,7 @@ void _printMissingSubcommand(Command<int> parent) {
|
|
|
244
247
|
stderr.writeln('');
|
|
245
248
|
|
|
246
249
|
if (subNames.isNotEmpty) {
|
|
247
|
-
stderr.writeln(' ${_cyan('
|
|
250
|
+
stderr.writeln(' ${_cyan('Available subcommands:')}');
|
|
248
251
|
for (final s in subNames) {
|
|
249
252
|
stderr.writeln(' ${_green('${parent.name} $s')}');
|
|
250
253
|
}
|
|
@@ -282,7 +285,8 @@ bool get _noColor {
|
|
|
282
285
|
return v != null && v.isNotEmpty;
|
|
283
286
|
}
|
|
284
287
|
|
|
285
|
-
bool get _colorOn =>
|
|
288
|
+
bool get _colorOn =>
|
|
289
|
+
!_noColor && stderr.hasTerminal && stderr.supportsAnsiEscapes;
|
|
286
290
|
|
|
287
291
|
String _red(String s) => _colorOn ? '\x1b[31m$s\x1b[0m' : s;
|
|
288
292
|
String _green(String s) => _colorOn ? '\x1b[32m$s\x1b[0m' : s;
|
|
@@ -35,8 +35,9 @@ Backend requireBackend(FlutterPatcherConfig? config, {Backend? override}) {
|
|
|
35
35
|
if (override != null) return override;
|
|
36
36
|
if (config == null) {
|
|
37
37
|
throw StateError(
|
|
38
|
-
'No configuration found. Run `
|
|
39
|
-
'
|
|
38
|
+
'No configuration found. Run `flutter-ota init <backend>` or set '
|
|
39
|
+
'the required environment variables for your provider '
|
|
40
|
+
'(see `flutter-ota init --help`).',
|
|
40
41
|
);
|
|
41
42
|
}
|
|
42
43
|
return resolveBackend(config);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import 'dart:io';
|
|
2
|
+
|
|
1
3
|
import 'package:flutter_ota_kit_cli/flutter_ota_kit_cli.dart';
|
|
2
4
|
|
|
3
5
|
import '../ui/ui.dart';
|
|
@@ -58,16 +60,28 @@ class BundleCommand extends FlutterPatcherCommand {
|
|
|
58
60
|
|
|
59
61
|
@override
|
|
60
62
|
String get description =>
|
|
61
|
-
'Manage bundles (list / show / delete / disable / enable / force / promote).';
|
|
63
|
+
'Manage bundles (list / show / delete / disable / enable / force / promote / update).';
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
class BundleListCommand extends FlutterPatcherCommand {
|
|
65
67
|
BundleListCommand({this.config, this.backendOverride}) {
|
|
66
|
-
|
|
68
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
69
|
+
argParser.addOption(
|
|
70
|
+
'backend',
|
|
71
|
+
abbr: 'b',
|
|
72
|
+
help: detected != null
|
|
73
|
+
? 'Backend provider [detected: $detected].'
|
|
74
|
+
: 'Backend provider.',
|
|
75
|
+
);
|
|
67
76
|
argParser.addOption('channel', abbr: 'c', help: 'Filter by channel.');
|
|
68
77
|
argParser.addOption('platform', abbr: 'p', help: 'Filter by platform.');
|
|
69
78
|
argParser.addOption('enabled', help: 'Filter by enabled (true/false).');
|
|
70
|
-
argParser.addOption(
|
|
79
|
+
argParser.addOption(
|
|
80
|
+
'limit',
|
|
81
|
+
abbr: 'l',
|
|
82
|
+
defaultsTo: '20',
|
|
83
|
+
help: 'Page size.',
|
|
84
|
+
);
|
|
71
85
|
}
|
|
72
86
|
|
|
73
87
|
final FlutterPatcherConfig? config;
|
|
@@ -90,7 +104,10 @@ class BundleListCommand extends FlutterPatcherCommand {
|
|
|
90
104
|
final limitRaw = argResults!['limit'] as String;
|
|
91
105
|
final limit = int.tryParse(limitRaw);
|
|
92
106
|
if (limit == null || limit < 1) {
|
|
93
|
-
throw PackException(
|
|
107
|
+
throw PackException(
|
|
108
|
+
'--limit must be a positive integer (got "$limitRaw")',
|
|
109
|
+
64,
|
|
110
|
+
);
|
|
94
111
|
}
|
|
95
112
|
final res = await listBundles(
|
|
96
113
|
backend,
|
|
@@ -111,26 +128,29 @@ class BundleListCommand extends FlutterPatcherCommand {
|
|
|
111
128
|
final b = res.data[i];
|
|
112
129
|
final enabled = b.enabled ? green('✓') : red('✗');
|
|
113
130
|
final force = b.shouldForceUpdate ? green('✓') : dim('✗');
|
|
114
|
-
rows.add([
|
|
115
|
-
'$i',
|
|
116
|
-
cyan(b.id),
|
|
117
|
-
b.channel,
|
|
118
|
-
b.platform.value,
|
|
119
|
-
force,
|
|
120
|
-
enabled,
|
|
121
|
-
]);
|
|
131
|
+
rows.add(['$i', cyan(b.id), b.channel, b.platform.value, force, enabled]);
|
|
122
132
|
}
|
|
123
|
-
table(
|
|
124
|
-
'
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
table('${res.data.length} bundles (total: ${res.pagination.total})', [
|
|
134
|
+
'#',
|
|
135
|
+
'ID',
|
|
136
|
+
'CHANNEL',
|
|
137
|
+
'PLAT',
|
|
138
|
+
'FORCE',
|
|
139
|
+
'ON',
|
|
140
|
+
], rows);
|
|
128
141
|
});
|
|
129
142
|
}
|
|
130
143
|
|
|
131
144
|
class BundleShowCommand extends FlutterPatcherCommand {
|
|
132
145
|
BundleShowCommand({this.config, this.backendOverride}) {
|
|
133
|
-
|
|
146
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
147
|
+
argParser.addOption(
|
|
148
|
+
'backend',
|
|
149
|
+
abbr: 'b',
|
|
150
|
+
help: detected != null
|
|
151
|
+
? 'Backend provider [detected: $detected].'
|
|
152
|
+
: 'Backend provider.',
|
|
153
|
+
);
|
|
134
154
|
argParser.addOption('id', help: 'Bundle id.');
|
|
135
155
|
}
|
|
136
156
|
|
|
@@ -173,7 +193,14 @@ class BundleShowCommand extends FlutterPatcherCommand {
|
|
|
173
193
|
|
|
174
194
|
class BundleDeleteCommand extends FlutterPatcherCommand {
|
|
175
195
|
BundleDeleteCommand({this.config, this.backendOverride}) {
|
|
176
|
-
|
|
196
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
197
|
+
argParser.addOption(
|
|
198
|
+
'backend',
|
|
199
|
+
abbr: 'b',
|
|
200
|
+
help: detected != null
|
|
201
|
+
? 'Backend provider [detected: $detected].'
|
|
202
|
+
: 'Backend provider.',
|
|
203
|
+
);
|
|
177
204
|
argParser.addOption('id', help: 'Bundle id.');
|
|
178
205
|
argParser.addFlag(
|
|
179
206
|
'keep-storage',
|
|
@@ -206,8 +233,10 @@ class BundleDeleteCommand extends FlutterPatcherCommand {
|
|
|
206
233
|
final steps = Steps('delete');
|
|
207
234
|
await steps.run('Deleting bundle $id', () => deleteBundle(backend, id));
|
|
208
235
|
if (!keepStorage && existing.storageUri.isNotEmpty) {
|
|
209
|
-
await steps.run(
|
|
210
|
-
|
|
236
|
+
await steps.run(
|
|
237
|
+
'Removing storage object',
|
|
238
|
+
() => backend.storage.delete(existing.storageUri),
|
|
239
|
+
);
|
|
211
240
|
}
|
|
212
241
|
steps.summary();
|
|
213
242
|
});
|
|
@@ -215,7 +244,14 @@ class BundleDeleteCommand extends FlutterPatcherCommand {
|
|
|
215
244
|
|
|
216
245
|
class BundleDisableCommand extends FlutterPatcherCommand {
|
|
217
246
|
BundleDisableCommand({this.config, this.backendOverride}) {
|
|
218
|
-
|
|
247
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
248
|
+
argParser.addOption(
|
|
249
|
+
'backend',
|
|
250
|
+
abbr: 'b',
|
|
251
|
+
help: detected != null
|
|
252
|
+
? 'Backend provider [detected: $detected].'
|
|
253
|
+
: 'Backend provider.',
|
|
254
|
+
);
|
|
219
255
|
argParser.addOption('id', help: 'Bundle id.');
|
|
220
256
|
}
|
|
221
257
|
|
|
@@ -246,7 +282,14 @@ class BundleDisableCommand extends FlutterPatcherCommand {
|
|
|
246
282
|
|
|
247
283
|
class BundleEnableCommand extends FlutterPatcherCommand {
|
|
248
284
|
BundleEnableCommand({this.config, this.backendOverride}) {
|
|
249
|
-
|
|
285
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
286
|
+
argParser.addOption(
|
|
287
|
+
'backend',
|
|
288
|
+
abbr: 'b',
|
|
289
|
+
help: detected != null
|
|
290
|
+
? 'Backend provider [detected: $detected].'
|
|
291
|
+
: 'Backend provider.',
|
|
292
|
+
);
|
|
250
293
|
argParser.addOption('id', help: 'Bundle id.');
|
|
251
294
|
}
|
|
252
295
|
|
|
@@ -277,7 +320,14 @@ class BundleEnableCommand extends FlutterPatcherCommand {
|
|
|
277
320
|
|
|
278
321
|
class BundleForceCommand extends FlutterPatcherCommand {
|
|
279
322
|
BundleForceCommand({this.config, this.backendOverride}) {
|
|
280
|
-
|
|
323
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
324
|
+
argParser.addOption(
|
|
325
|
+
'backend',
|
|
326
|
+
abbr: 'b',
|
|
327
|
+
help: detected != null
|
|
328
|
+
? 'Backend provider [detected: $detected].'
|
|
329
|
+
: 'Backend provider.',
|
|
330
|
+
);
|
|
281
331
|
argParser.addOption('id', help: 'Bundle id.');
|
|
282
332
|
argParser.addFlag(
|
|
283
333
|
'off',
|
|
@@ -319,7 +369,14 @@ class BundleForceCommand extends FlutterPatcherCommand {
|
|
|
319
369
|
|
|
320
370
|
class BundlePromoteCommand extends FlutterPatcherCommand {
|
|
321
371
|
BundlePromoteCommand({this.config, this.backendOverride}) {
|
|
322
|
-
|
|
372
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
373
|
+
argParser.addOption(
|
|
374
|
+
'backend',
|
|
375
|
+
abbr: 'b',
|
|
376
|
+
help: detected != null
|
|
377
|
+
? 'Backend provider [detected: $detected].'
|
|
378
|
+
: 'Backend provider.',
|
|
379
|
+
);
|
|
323
380
|
argParser.addOption('id', help: 'Bundle id.');
|
|
324
381
|
argParser.addOption('channel', abbr: 'c', help: 'Target channel.');
|
|
325
382
|
}
|
|
@@ -348,21 +405,39 @@ class BundlePromoteCommand extends FlutterPatcherCommand {
|
|
|
348
405
|
final backend = requireBackend(cfg, override: backendOverride);
|
|
349
406
|
banner('bundle · promote');
|
|
350
407
|
final steps = Steps('promote');
|
|
351
|
-
await steps.run(
|
|
352
|
-
|
|
408
|
+
await steps.run(
|
|
409
|
+
'Promoting $id to $channel',
|
|
410
|
+
() => promoteBundle(backend, id!, channel),
|
|
411
|
+
);
|
|
353
412
|
steps.summary();
|
|
354
|
-
|
|
413
|
+
stdout.writeln(
|
|
414
|
+
' ${dim('→')} bundle ${cyan(id!)} → channel ${cyan(channel)}',
|
|
415
|
+
);
|
|
355
416
|
});
|
|
356
417
|
}
|
|
357
418
|
|
|
358
419
|
class BundleUpdateCommand extends FlutterPatcherCommand {
|
|
359
420
|
BundleUpdateCommand({this.config, this.backendOverride}) {
|
|
360
|
-
|
|
421
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
422
|
+
argParser.addOption(
|
|
423
|
+
'backend',
|
|
424
|
+
abbr: 'b',
|
|
425
|
+
help: detected != null
|
|
426
|
+
? 'Backend provider [detected: $detected].'
|
|
427
|
+
: 'Backend provider.',
|
|
428
|
+
);
|
|
361
429
|
argParser.addOption('id', help: 'Bundle id.');
|
|
362
430
|
argParser.addOption('message', abbr: 'm', help: 'New release message.');
|
|
363
|
-
argParser.addOption(
|
|
431
|
+
argParser.addOption(
|
|
432
|
+
'target-version',
|
|
433
|
+
help: 'New target app version (e.g. 1.0.0).',
|
|
434
|
+
);
|
|
364
435
|
argParser.addOption('enabled', help: 'Set enabled (true/false).');
|
|
365
|
-
argParser.addOption(
|
|
436
|
+
argParser.addOption(
|
|
437
|
+
'force',
|
|
438
|
+
abbr: 'f',
|
|
439
|
+
help: 'Set force-update (true/false).',
|
|
440
|
+
);
|
|
366
441
|
}
|
|
367
442
|
|
|
368
443
|
final FlutterPatcherConfig? config;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import 'dart:io';
|
|
2
|
+
|
|
1
3
|
import 'package:flutter_ota_kit_cli/flutter_ota_kit_cli.dart';
|
|
2
4
|
|
|
3
5
|
import '../ui/ui.dart';
|
|
@@ -28,7 +30,14 @@ class ChannelCommand extends FlutterPatcherCommand {
|
|
|
28
30
|
|
|
29
31
|
class ChannelListCommand extends FlutterPatcherCommand {
|
|
30
32
|
ChannelListCommand({this.config, this.backendOverride}) {
|
|
31
|
-
|
|
33
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
34
|
+
argParser.addOption(
|
|
35
|
+
'backend',
|
|
36
|
+
abbr: 'b',
|
|
37
|
+
help: detected != null
|
|
38
|
+
? 'Backend provider [detected: $detected].'
|
|
39
|
+
: 'Backend provider.',
|
|
40
|
+
);
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
final FlutterPatcherConfig? config;
|
|
@@ -56,7 +65,14 @@ class ChannelListCommand extends FlutterPatcherCommand {
|
|
|
56
65
|
|
|
57
66
|
class ChannelGetCommand extends FlutterPatcherCommand {
|
|
58
67
|
ChannelGetCommand({this.config, this.backendOverride}) {
|
|
59
|
-
|
|
68
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
69
|
+
argParser.addOption(
|
|
70
|
+
'backend',
|
|
71
|
+
abbr: 'b',
|
|
72
|
+
help: detected != null
|
|
73
|
+
? 'Backend provider [detected: $detected].'
|
|
74
|
+
: 'Backend provider.',
|
|
75
|
+
);
|
|
60
76
|
argParser.addOption('channel', abbr: 'c', help: 'Channel.');
|
|
61
77
|
}
|
|
62
78
|
|
|
@@ -100,7 +116,14 @@ class ChannelGetCommand extends FlutterPatcherCommand {
|
|
|
100
116
|
|
|
101
117
|
class ChannelSetCommand extends FlutterPatcherCommand {
|
|
102
118
|
ChannelSetCommand({this.config, this.backendOverride}) {
|
|
103
|
-
|
|
119
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
120
|
+
argParser.addOption(
|
|
121
|
+
'backend',
|
|
122
|
+
abbr: 'b',
|
|
123
|
+
help: detected != null
|
|
124
|
+
? 'Backend provider [detected: $detected].'
|
|
125
|
+
: 'Backend provider.',
|
|
126
|
+
);
|
|
104
127
|
argParser.addOption('channel', abbr: 'c', help: 'Channel.');
|
|
105
128
|
argParser.addOption('bundle-id', abbr: 'i', help: 'Bundle id.');
|
|
106
129
|
}
|
|
@@ -112,7 +135,7 @@ class ChannelSetCommand extends FlutterPatcherCommand {
|
|
|
112
135
|
String get name => 'set';
|
|
113
136
|
|
|
114
137
|
@override
|
|
115
|
-
String get description => '
|
|
138
|
+
String get description => 'Set the active bundle for a channel.';
|
|
116
139
|
|
|
117
140
|
@override
|
|
118
141
|
Future<int> run() => runGuarded(() async {
|
|
@@ -131,9 +154,13 @@ class ChannelSetCommand extends FlutterPatcherCommand {
|
|
|
131
154
|
final backend = requireBackend(cfg, override: backendOverride);
|
|
132
155
|
banner('channel · set');
|
|
133
156
|
final steps = Steps('set');
|
|
134
|
-
await steps.run(
|
|
135
|
-
|
|
157
|
+
await steps.run(
|
|
158
|
+
'Promoting $bundleId to $channel',
|
|
159
|
+
() => promoteBundle(backend, bundleId, channel),
|
|
160
|
+
);
|
|
136
161
|
steps.summary();
|
|
137
|
-
|
|
162
|
+
stdout.writeln(
|
|
163
|
+
' ${dim('→')} channel ${cyan(channel)} = ${cyan(bundleId)}',
|
|
164
|
+
);
|
|
138
165
|
});
|
|
139
166
|
}
|
|
@@ -32,6 +32,9 @@ void _saveProjectJson(Map<String, dynamic> json) {
|
|
|
32
32
|
final file = configCandidates().first;
|
|
33
33
|
file.parent.createSync(recursive: true);
|
|
34
34
|
file.writeAsStringSync(const JsonEncoder.withIndent(' ').convert(json));
|
|
35
|
+
if (!Platform.isWindows) {
|
|
36
|
+
Process.runSync('chmod', ['600', file.path]);
|
|
37
|
+
}
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
class ConfigGetCommand extends FlutterPatcherCommand {
|
|
@@ -49,7 +52,8 @@ class ConfigGetCommand extends FlutterPatcherCommand {
|
|
|
49
52
|
@override
|
|
50
53
|
Future<int> run() => runGuarded(() async {
|
|
51
54
|
ui.banner('config · get');
|
|
52
|
-
final key =
|
|
55
|
+
final key =
|
|
56
|
+
argResults!['key'] as String? ??
|
|
53
57
|
(argResults!.rest.isNotEmpty ? argResults!.rest.first : null);
|
|
54
58
|
if (key == null || key.isEmpty) {
|
|
55
59
|
throw StateError('Usage: flutter-ota config get <key>');
|
|
@@ -79,9 +83,11 @@ class ConfigSetCommand extends FlutterPatcherCommand {
|
|
|
79
83
|
|
|
80
84
|
@override
|
|
81
85
|
Future<int> run() => runGuarded(() async {
|
|
82
|
-
final key =
|
|
86
|
+
final key =
|
|
87
|
+
argResults!['key'] as String? ??
|
|
83
88
|
(argResults!.rest.isNotEmpty ? argResults!.rest.first : null);
|
|
84
|
-
final value =
|
|
89
|
+
final value =
|
|
90
|
+
argResults!['value'] as String? ??
|
|
85
91
|
(argResults!.rest.length > 1 ? argResults!.rest[1] : null);
|
|
86
92
|
if (key == null || key.isEmpty || value == null) {
|
|
87
93
|
throw StateError('Usage: flutter-ota config set <key> <value>');
|
|
@@ -121,7 +127,9 @@ void _printConfig(Map<String, dynamic> json, String prefix) {
|
|
|
121
127
|
if (value is Map<String, dynamic>) {
|
|
122
128
|
_printConfig(value, key);
|
|
123
129
|
} else {
|
|
124
|
-
final display = value is String
|
|
130
|
+
final display = value is String
|
|
131
|
+
? value
|
|
132
|
+
: const JsonEncoder().convert(value);
|
|
125
133
|
stdout.writeln(ui.kv(key, display));
|
|
126
134
|
}
|
|
127
135
|
}
|
|
@@ -35,8 +35,10 @@ class ConsoleCommand extends FlutterPatcherCommand {
|
|
|
35
35
|
Future<int> run() => runGuarded(() async {
|
|
36
36
|
final consoleDir = _findConsoleDir();
|
|
37
37
|
if (consoleDir == null) {
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
throw PackException(
|
|
39
|
+
'Console package not found (expected packages/console).',
|
|
40
|
+
1,
|
|
41
|
+
);
|
|
40
42
|
}
|
|
41
43
|
if (argResults!['open'] as bool) {
|
|
42
44
|
banner('console · open');
|
|
@@ -53,11 +55,8 @@ class ConsoleCommand extends FlutterPatcherCommand {
|
|
|
53
55
|
return;
|
|
54
56
|
}
|
|
55
57
|
banner('console');
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
kv('dir', consoleDir.path),
|
|
60
|
-
kv('run', 'flutter run -d chrome'),
|
|
61
|
-
]);
|
|
58
|
+
info('Open the web console with Flutter:');
|
|
59
|
+
step('dir ${dim(consoleDir.path)}');
|
|
60
|
+
step('run ${cyan('flutter run -d chrome')}');
|
|
62
61
|
});
|
|
63
62
|
}
|
|
@@ -6,20 +6,33 @@ import '../backend.dart';
|
|
|
6
6
|
import '../cli_base.dart';
|
|
7
7
|
import '../config.dart';
|
|
8
8
|
import '../operations.dart';
|
|
9
|
+
import '../pack.dart';
|
|
9
10
|
import '../ui/ui.dart';
|
|
10
11
|
import '../util.dart';
|
|
11
12
|
|
|
12
13
|
/// `flutter-ota deploy` — zip + upload + register a new bundle.
|
|
13
14
|
class DeployCommand extends FlutterPatcherCommand {
|
|
14
15
|
DeployCommand({this.config, this.backendOverride}) {
|
|
16
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
15
17
|
argParser.addOption(
|
|
16
18
|
'backend',
|
|
17
19
|
abbr: 'b',
|
|
18
|
-
help:
|
|
20
|
+
help: detected != null
|
|
21
|
+
? 'Backend provider [detected: $detected].'
|
|
22
|
+
: 'Backend provider.',
|
|
23
|
+
);
|
|
24
|
+
argParser.addOption(
|
|
25
|
+
'source',
|
|
26
|
+
abbr: 's',
|
|
27
|
+
help: 'Source directory to zip + upload.',
|
|
19
28
|
);
|
|
20
|
-
argParser.addOption('source', abbr: 's', help: 'Source directory to zip + upload.');
|
|
21
29
|
argParser.addOption('channel', abbr: 'c', help: 'Target channel.');
|
|
22
|
-
argParser.addOption(
|
|
30
|
+
argParser.addOption(
|
|
31
|
+
'platform',
|
|
32
|
+
abbr: 'p',
|
|
33
|
+
defaultsTo: 'android',
|
|
34
|
+
help: 'Platform.',
|
|
35
|
+
);
|
|
23
36
|
argParser.addOption('message', abbr: 'm', help: 'Release message.');
|
|
24
37
|
argParser.addFlag('force', abbr: 'f', help: 'Force the update on clients.');
|
|
25
38
|
argParser.addOption(
|
|
@@ -35,7 +48,10 @@ class DeployCommand extends FlutterPatcherCommand {
|
|
|
35
48
|
abbr: 'k',
|
|
36
49
|
help: 'Path to Ed25519 private key file (sign bundle).',
|
|
37
50
|
);
|
|
38
|
-
argParser.addOption(
|
|
51
|
+
argParser.addOption(
|
|
52
|
+
'git-commit-hash',
|
|
53
|
+
help: 'Git commit hash (auto-detected).',
|
|
54
|
+
);
|
|
39
55
|
argParser.addOption(
|
|
40
56
|
'bundle-id',
|
|
41
57
|
abbr: 'i',
|
|
@@ -68,19 +84,35 @@ class DeployCommand extends FlutterPatcherCommand {
|
|
|
68
84
|
final fingerprintHash = argResults!['fingerprint-hash'] as String?;
|
|
69
85
|
final keyPath = argResults!['key'] as String?;
|
|
70
86
|
String? signingKey;
|
|
71
|
-
if (keyPath != null)
|
|
87
|
+
if (keyPath != null) {
|
|
88
|
+
final keyFile = File(keyPath);
|
|
89
|
+
if (!keyFile.existsSync()) {
|
|
90
|
+
throw PackException('Signing key file not found: $keyPath', 64);
|
|
91
|
+
}
|
|
92
|
+
signingKey = keyFile.readAsStringSync().trim();
|
|
93
|
+
}
|
|
72
94
|
final resolvedGitCommitHash =
|
|
73
95
|
argResults!['git-commit-hash'] as String? ??
|
|
74
96
|
await gitCommitHash(source);
|
|
75
97
|
final bundleId = argResults!['bundle-id'] as String?;
|
|
76
98
|
|
|
77
99
|
banner('deploy');
|
|
78
|
-
info('channel ${cyan(channel)} platform ${cyan(platform)} source ${dim(source)}');
|
|
79
100
|
|
|
80
101
|
final steps = Steps('deploy');
|
|
81
|
-
final bundle = await _deployWithPhases(
|
|
82
|
-
|
|
83
|
-
|
|
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
|
+
);
|
|
84
116
|
steps.summary();
|
|
85
117
|
|
|
86
118
|
final lines = <String>[
|