@_nazmiforreal/flutter-ota 0.1.2 → 0.1.4
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/dart-src/packages/cli-tools/.dart_tool/package_config.json +496 -0
- package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +743 -0
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +7 -6
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +46 -49
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +44 -23
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +27 -17
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +11 -6
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +39 -22
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +22 -13
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +117 -36
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +15 -9
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +14 -8
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +37 -12
- package/dart-src/packages/cli-tools/lib/src/config.dart +13 -3
- package/dart-src/packages/cli-tools/lib/src/operations.dart +45 -0
- package/dart-src/packages/cli-tools/lib/src/pack.dart +9 -0
- package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +234 -0
- package/dart-src/packages/cli-tools/pubspec.lock +638 -0
- package/dart-src/packages/cli-tools/test/backends_test.dart +207 -0
- package/dart-src/packages/cli-tools/test/build_pack_test.dart +101 -0
- package/dart-src/packages/cli-tools/test/cli_test.dart +442 -0
- package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +83 -0
- package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +256 -0
- package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +347 -0
- package/dart-src/packages/cli-tools/test/standalone_backend_test.dart +315 -0
- package/dart-src/packages/core/test/rollout_test.dart +128 -0
- package/dart-src/packages/core/test/semver_test.dart +52 -0
- package/dart-src/packages/core/test/semver_vectors.json +1 -0
- package/dart-src/packages/core/test/types_test.dart +129 -0
- package/dart-src/packages/core/test/uuid_test.dart +38 -0
- package/dart-src/plugins/aws/test/aws_database_test.dart +167 -0
- package/dart-src/plugins/aws/test/aws_lambda_edge_storage_test.dart +190 -0
- package/dart-src/plugins/aws/test/aws_storage_test.dart +73 -0
- package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +83 -0
- package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +122 -0
- package/dart-src/plugins/cloudflare/test/d1_bundle_mapper_test.dart +106 -0
- package/dart-src/plugins/cloudflare/test/d1_database_test.dart +190 -0
- package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +256 -0
- package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +83 -0
- package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +77 -0
- package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +166 -0
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_store_test.dart +74 -0
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +332 -0
- package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +711 -0
- package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +294 -0
- package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +192 -0
- package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +51 -0
- package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +1600 -0
- package/dart-src/plugins/plugin-core/test/helpers_test.dart +259 -0
- package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +139 -0
- package/dart-src/plugins/plugin-core/test/request_update_bundle_state_test.dart +86 -0
- package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +77 -0
- package/dart-src/plugins/postgres/test/mock_postgres_client.dart +297 -0
- package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +140 -0
- package/dart-src/plugins/postgres/test/postgres_database_test.dart +181 -0
- package/dart-src/plugins/standalone/test/standalone_test.dart +264 -0
- package/dart-src/plugins/supabase/test/mock_supabase_client.dart +654 -0
- package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +112 -0
- package/dart-src/plugins/supabase/test/supabase_database_test.dart +207 -0
- package/dart-src/plugins/supabase/test/supabase_storage_test.dart +175 -0
- package/package.json +1 -1
- package/scripts/vendor.sh +29 -0
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import 'dart:io';
|
|
2
|
-
|
|
3
1
|
import 'package:args/args.dart';
|
|
4
2
|
|
|
5
3
|
import 'backend.dart';
|
|
6
4
|
import 'config.dart';
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
import 'pack.dart';
|
|
6
|
+
import 'ui/ui.dart';
|
|
9
7
|
|
|
10
8
|
/// Run [body], mapping errors to a non-zero exit code with a clean message.
|
|
11
9
|
Future<int> runGuarded(Future<void> Function() body) async {
|
|
@@ -13,10 +11,13 @@ Future<int> runGuarded(Future<void> Function() body) async {
|
|
|
13
11
|
await body();
|
|
14
12
|
return 0;
|
|
15
13
|
} on StateError catch (e) {
|
|
16
|
-
|
|
14
|
+
err(e.message);
|
|
17
15
|
return 1;
|
|
16
|
+
} on PackException catch (e) {
|
|
17
|
+
err(e.message);
|
|
18
|
+
return e.exitCode;
|
|
18
19
|
} catch (e) {
|
|
19
|
-
|
|
20
|
+
err(e.toString());
|
|
20
21
|
return 1;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import 'dart:io';
|
|
2
|
-
|
|
3
1
|
import 'package:args/command_runner.dart';
|
|
4
2
|
import 'package:flutter_patcher_cli/flutter_patcher_cli.dart';
|
|
5
3
|
|
|
4
|
+
import '../ui/ui.dart';
|
|
5
|
+
|
|
6
6
|
/// `flutter_patcher build` — package a release APK into a device-compatible
|
|
7
7
|
/// OTA patch (`dist/patch.zip`) that the `deploy` command can publish.
|
|
8
8
|
///
|
|
@@ -48,52 +48,49 @@ class BuildCommand extends Command<int> {
|
|
|
48
48
|
'Build a device-compatible OTA patch (patch.zip) from a release APK.';
|
|
49
49
|
|
|
50
50
|
@override
|
|
51
|
-
Future<int> run() async {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (targetVersionCode == null) {
|
|
70
|
-
stderr.writeln('error: --target-version-code must be an integer');
|
|
71
|
-
return 64;
|
|
72
|
-
}
|
|
51
|
+
Future<int> run() => runGuarded(() async {
|
|
52
|
+
final results = argResults!;
|
|
53
|
+
final apk = results['apk'] as String?;
|
|
54
|
+
if (apk == null || apk.isEmpty) {
|
|
55
|
+
throw PackException('missing required --apk <path>', 64);
|
|
56
|
+
}
|
|
57
|
+
final version = results['version'] as String?;
|
|
58
|
+
if (version == null || version.isEmpty) {
|
|
59
|
+
throw PackException('missing required --version <string>', 64);
|
|
60
|
+
}
|
|
61
|
+
final targetVersionCodeRaw = results['target-version-code'] as String?;
|
|
62
|
+
if (targetVersionCodeRaw == null || targetVersionCodeRaw.isEmpty) {
|
|
63
|
+
throw PackException('missing required --target-version-code <int>', 64);
|
|
64
|
+
}
|
|
65
|
+
final targetVersionCode = int.tryParse(targetVersionCodeRaw);
|
|
66
|
+
if (targetVersionCode == null) {
|
|
67
|
+
throw PackException('--target-version-code must be an integer', 64);
|
|
68
|
+
}
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
70
|
+
banner('build');
|
|
71
|
+
final bar = ProgressBar(1, 'pack');
|
|
72
|
+
final result = await packPatch(
|
|
73
|
+
apkPath: apk,
|
|
74
|
+
version: version,
|
|
75
|
+
targetVersionCode: targetVersionCode,
|
|
76
|
+
abi: results['abi'] as String?,
|
|
77
|
+
requestedAssets: results['assets'] as List<String>,
|
|
78
|
+
out: results['out'] as String,
|
|
79
|
+
onProgress: (done, total, label) {
|
|
80
|
+
bar.total = total;
|
|
81
|
+
bar.update(done, label);
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
box('patch.zip', [
|
|
86
|
+
kv('version', result.version),
|
|
87
|
+
kv('target code', result.targetVersionCode.toString()),
|
|
88
|
+
kv('abis', cyan(result.abis.join(' · '))),
|
|
89
|
+
kv('bundle md5', gray(result.md5)),
|
|
90
|
+
kv('overlay assets', result.assetCount.toString()),
|
|
91
|
+
kv('payload', result.payloadPath),
|
|
92
|
+
kv('manifest', result.manifestPath),
|
|
93
|
+
]);
|
|
94
|
+
step('Build complete — ready to deploy.');
|
|
95
|
+
});
|
|
99
96
|
}
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import 'dart:io';
|
|
2
|
-
|
|
3
1
|
import 'package:args/args.dart';
|
|
4
2
|
import 'package:args/command_runner.dart';
|
|
3
|
+
import 'package:flutter_patcher_cli/flutter_patcher_cli.dart';
|
|
5
4
|
|
|
6
|
-
import '../
|
|
7
|
-
import '../cli_base.dart';
|
|
8
|
-
import '../config.dart';
|
|
9
|
-
import '../operations.dart';
|
|
5
|
+
import '../ui/ui.dart';
|
|
10
6
|
|
|
11
7
|
/// `flutter_patcher bundle` — manage bundles.
|
|
12
8
|
class BundleCommand extends Command<int> {
|
|
@@ -69,20 +65,32 @@ class BundleListCommand extends Command<int> {
|
|
|
69
65
|
limit: int.parse(argResults!['limit'] as String),
|
|
70
66
|
),
|
|
71
67
|
);
|
|
68
|
+
banner('bundle · list');
|
|
72
69
|
if (res.data.isEmpty) {
|
|
73
|
-
|
|
70
|
+
step('(no bundles)');
|
|
74
71
|
return;
|
|
75
72
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
final target = b.targetAppVersion ?? b.fingerprintHash ?? '-';
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
73
|
+
final lines = <String>[];
|
|
74
|
+
for (var i = 0; i < res.data.length; i++) {
|
|
75
|
+
final b = res.data[i];
|
|
76
|
+
final target = b.targetAppVersion ?? b.fingerprintHash ?? dim('-');
|
|
77
|
+
lines
|
|
78
|
+
..add(kv('#$i', cyan(b.id)))
|
|
79
|
+
..add(kv('channel', b.channel))
|
|
80
|
+
..add(kv(
|
|
81
|
+
'enabled',
|
|
82
|
+
b.enabled ? green('yes') : yellow('no'),
|
|
83
|
+
))
|
|
84
|
+
..add(kv('platform', b.platform.value))
|
|
85
|
+
..add(kv('target', target));
|
|
86
|
+
if (b.message != null) lines.add(kv('message', b.message!));
|
|
87
|
+
if (b.metadata?.signature != null) {
|
|
88
|
+
lines.add(kv('signature', green('✓ signed')));
|
|
89
|
+
}
|
|
90
|
+
lines.add('');
|
|
83
91
|
}
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
box('${res.data.length} bundles', lines);
|
|
93
|
+
step('total: ${res.pagination.total}');
|
|
86
94
|
});
|
|
87
95
|
}
|
|
88
96
|
|
|
@@ -107,12 +115,16 @@ class BundleDeleteCommand extends Command<int> {
|
|
|
107
115
|
Future<int> run() => runGuarded(() async {
|
|
108
116
|
final id = argResults!['id'] as String?;
|
|
109
117
|
if (id == null || id.isEmpty) {
|
|
110
|
-
throw
|
|
118
|
+
throw PackException('Usage: flutter_patcher bundle delete --id <id>', 64);
|
|
111
119
|
}
|
|
112
120
|
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
113
121
|
final backend = requireBackend(cfg, override: backendOverride);
|
|
114
|
-
|
|
115
|
-
|
|
122
|
+
banner('bundle · delete');
|
|
123
|
+
await spinner(
|
|
124
|
+
() => deleteBundle(backend, id),
|
|
125
|
+
'Deleting bundle $id',
|
|
126
|
+
done: 'Deleted',
|
|
127
|
+
);
|
|
116
128
|
});
|
|
117
129
|
}
|
|
118
130
|
|
|
@@ -139,13 +151,22 @@ class BundlePromoteCommand extends Command<int> {
|
|
|
139
151
|
final id = argResults!['id'] as String?;
|
|
140
152
|
final channel = argResults!['channel'] as String?;
|
|
141
153
|
if (id == null || id.isEmpty || channel == null || channel.isEmpty) {
|
|
142
|
-
throw
|
|
143
|
-
'Usage: flutter_patcher bundle promote <id> --channel <channel>',
|
|
154
|
+
throw PackException(
|
|
155
|
+
'Usage: flutter_patcher bundle promote --id <id> --channel <channel>',
|
|
156
|
+
64,
|
|
144
157
|
);
|
|
145
158
|
}
|
|
146
159
|
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
147
160
|
final backend = requireBackend(cfg, override: backendOverride);
|
|
148
|
-
|
|
149
|
-
|
|
161
|
+
banner('bundle · promote');
|
|
162
|
+
await spinner(
|
|
163
|
+
() => promoteBundle(backend, id, channel),
|
|
164
|
+
'Promoting $id to $channel',
|
|
165
|
+
done: 'Promoted',
|
|
166
|
+
);
|
|
167
|
+
box('promote', [
|
|
168
|
+
kv('bundle', cyan(id)),
|
|
169
|
+
kv('channel', channel),
|
|
170
|
+
]);
|
|
150
171
|
});
|
|
151
172
|
}
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import 'dart:io';
|
|
2
|
-
|
|
3
1
|
import 'package:args/args.dart';
|
|
4
2
|
import 'package:args/command_runner.dart';
|
|
3
|
+
import 'package:flutter_patcher_cli/flutter_patcher_cli.dart';
|
|
5
4
|
|
|
6
|
-
import '../
|
|
7
|
-
import '../cli_base.dart';
|
|
8
|
-
import '../config.dart';
|
|
9
|
-
import '../operations.dart';
|
|
5
|
+
import '../ui/ui.dart';
|
|
10
6
|
|
|
11
7
|
/// `flutter_patcher channel` — manage channels.
|
|
12
8
|
class ChannelCommand extends Command<int> {
|
|
@@ -56,14 +52,13 @@ class ChannelListCommand extends Command<int> {
|
|
|
56
52
|
Future<int> run() => runGuarded(() async {
|
|
57
53
|
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
58
54
|
final backend = requireBackend(cfg, override: backendOverride);
|
|
55
|
+
banner('channel · list');
|
|
59
56
|
final channels = await listChannels(backend);
|
|
60
57
|
if (channels.isEmpty) {
|
|
61
|
-
|
|
58
|
+
step('(no channels)');
|
|
62
59
|
return;
|
|
63
60
|
}
|
|
64
|
-
|
|
65
|
-
stdout.writeln(' $c');
|
|
66
|
-
}
|
|
61
|
+
box('${channels.length} channels', channels.map((c) => ' $c').toList());
|
|
67
62
|
});
|
|
68
63
|
}
|
|
69
64
|
|
|
@@ -88,17 +83,23 @@ class ChannelGetCommand extends Command<int> {
|
|
|
88
83
|
Future<int> run() => runGuarded(() async {
|
|
89
84
|
final channel = argResults!['channel'] as String?;
|
|
90
85
|
if (channel == null || channel.isEmpty) {
|
|
91
|
-
throw
|
|
86
|
+
throw PackException('Usage: flutter_patcher channel get --channel <channel>', 64);
|
|
92
87
|
}
|
|
93
88
|
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
94
89
|
final backend = requireBackend(cfg, override: backendOverride);
|
|
90
|
+
banner('channel · get');
|
|
95
91
|
final bundle = await getChannel(backend, channel);
|
|
96
92
|
if (bundle == null) {
|
|
97
|
-
|
|
93
|
+
err('no live bundle on channel "$channel"');
|
|
98
94
|
return;
|
|
99
95
|
}
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
box('channel "$channel"', [
|
|
97
|
+
kv('live bundle', cyan(bundle.id)),
|
|
98
|
+
kv('platform', bundle.platform.value),
|
|
99
|
+
kv('enabled', bundle.enabled ? green('yes') : yellow('no')),
|
|
100
|
+
kv('target', bundle.targetAppVersion ?? bundle.fingerprintHash ?? dim('-')),
|
|
101
|
+
if (bundle.message != null) kv('message', bundle.message!),
|
|
102
|
+
]);
|
|
102
103
|
});
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -128,13 +129,22 @@ class ChannelSetCommand extends Command<int> {
|
|
|
128
129
|
channel.isEmpty ||
|
|
129
130
|
bundleId == null ||
|
|
130
131
|
bundleId.isEmpty) {
|
|
131
|
-
throw
|
|
132
|
+
throw PackException(
|
|
132
133
|
'Usage: flutter_patcher channel set --channel <c> --bundle-id <id>',
|
|
134
|
+
64,
|
|
133
135
|
);
|
|
134
136
|
}
|
|
135
137
|
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
136
138
|
final backend = requireBackend(cfg, override: backendOverride);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
+
banner('channel · set');
|
|
140
|
+
await spinner(
|
|
141
|
+
() => promoteBundle(backend, bundleId, channel),
|
|
142
|
+
'Promoting $bundleId to $channel',
|
|
143
|
+
done: 'Channel set',
|
|
144
|
+
);
|
|
145
|
+
box('channel set', [
|
|
146
|
+
kv('channel', channel),
|
|
147
|
+
kv('bundle', cyan(bundleId)),
|
|
148
|
+
]);
|
|
139
149
|
});
|
|
140
150
|
}
|
|
@@ -2,9 +2,10 @@ import 'dart:io';
|
|
|
2
2
|
|
|
3
3
|
import 'package:args/args.dart';
|
|
4
4
|
import 'package:args/command_runner.dart';
|
|
5
|
+
import 'package:flutter_patcher_cli/flutter_patcher_cli.dart';
|
|
5
6
|
import 'package:path/path.dart' as p;
|
|
6
7
|
|
|
7
|
-
import '../
|
|
8
|
+
import '../ui/ui.dart';
|
|
8
9
|
|
|
9
10
|
/// `flutter_patcher console` — open the web console.
|
|
10
11
|
class ConsoleCommand extends Command<int> {
|
|
@@ -33,11 +34,11 @@ class ConsoleCommand extends Command<int> {
|
|
|
33
34
|
Future<int> run() => runGuarded(() async {
|
|
34
35
|
final consoleDir = _findConsoleDir();
|
|
35
36
|
if (consoleDir == null) {
|
|
36
|
-
|
|
37
|
+
err('Console package not found (expected packages/console).');
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
39
40
|
if (argResults!['open'] as bool) {
|
|
40
|
-
|
|
41
|
+
step('Launching console...');
|
|
41
42
|
final process = await Process.start(
|
|
42
43
|
'flutter',
|
|
43
44
|
['run', '-d', 'chrome'],
|
|
@@ -48,8 +49,12 @@ class ConsoleCommand extends Command<int> {
|
|
|
48
49
|
exitCode = await process.exitCode;
|
|
49
50
|
return;
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
banner('console');
|
|
53
|
+
box('flutter_patcher console', [
|
|
54
|
+
'Open the web console with Flutter:',
|
|
55
|
+
'',
|
|
56
|
+
kv('dir', consoleDir.path),
|
|
57
|
+
kv('run', 'flutter run -d chrome'),
|
|
58
|
+
]);
|
|
54
59
|
});
|
|
55
60
|
}
|
|
@@ -7,6 +7,7 @@ import '../backend.dart';
|
|
|
7
7
|
import '../cli_base.dart';
|
|
8
8
|
import '../config.dart';
|
|
9
9
|
import '../operations.dart';
|
|
10
|
+
import '../ui/ui.dart';
|
|
10
11
|
import '../util.dart';
|
|
11
12
|
|
|
12
13
|
/// `flutter_patcher deploy` — zip + upload + register a new bundle.
|
|
@@ -60,36 +61,52 @@ class DeployCommand extends Command<int> {
|
|
|
60
61
|
argResults!['git-commit-hash'] as String? ?? await gitCommitHash(source);
|
|
61
62
|
final bundleId = argResults!['bundle-id'] as String?;
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
64
|
+
banner('deploy');
|
|
65
|
+
stdout.writeln(
|
|
66
|
+
'${gray('channel')} ${cyan(channel)} '
|
|
67
|
+
'${gray('platform')} ${cyan(platform)} '
|
|
68
|
+
'${gray('source')} ${dim(source)}',
|
|
69
|
+
);
|
|
70
|
+
final bundle = await spinner(
|
|
71
|
+
() => deployBundle(
|
|
72
|
+
backend,
|
|
73
|
+
DeployOptions(
|
|
74
|
+
source: source,
|
|
75
|
+
channel: channel,
|
|
76
|
+
platform: platform,
|
|
77
|
+
message: message,
|
|
78
|
+
force: force,
|
|
79
|
+
targetAppVersion: targetAppVersion,
|
|
80
|
+
fingerprintHash: fingerprintHash,
|
|
81
|
+
signingKeyBase64: signingKey,
|
|
82
|
+
gitCommitHash: resolvedGitCommitHash,
|
|
83
|
+
bundleId: bundleId,
|
|
84
|
+
),
|
|
76
85
|
),
|
|
86
|
+
'Uploading & registering bundle',
|
|
87
|
+
done: 'Bundle deployed',
|
|
77
88
|
);
|
|
78
89
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
90
|
+
final lines = <String>[
|
|
91
|
+
kv('bundle id', cyan(bundle.id)),
|
|
92
|
+
kv('channel', bundle.channel),
|
|
93
|
+
kv('platform', bundle.platform.value),
|
|
94
|
+
kv('enabled', bundle.enabled ? green('true') : yellow('false')),
|
|
95
|
+
kv('storage uri', gray(bundle.storageUri)),
|
|
96
|
+
];
|
|
84
97
|
if (bundle.targetAppVersion != null) {
|
|
85
|
-
|
|
98
|
+
lines.add(kv('target app', bundle.targetAppVersion!));
|
|
86
99
|
}
|
|
87
100
|
if (bundle.fingerprintHash != null) {
|
|
88
|
-
|
|
101
|
+
lines.add(kv('fingerprint', bundle.fingerprintHash!));
|
|
89
102
|
}
|
|
90
103
|
if (bundle.gitCommitHash != null) {
|
|
91
|
-
|
|
104
|
+
lines.add(kv('git commit', bundle.gitCommitHash!));
|
|
105
|
+
}
|
|
106
|
+
if (bundle.message != null) lines.add(kv('message', bundle.message!));
|
|
107
|
+
if (bundle.metadata?.signature != null) {
|
|
108
|
+
lines.add(kv('signature', dim('✓ signed')));
|
|
92
109
|
}
|
|
93
|
-
|
|
110
|
+
box('deployed', lines);
|
|
94
111
|
});
|
|
95
112
|
}
|
|
@@ -2,10 +2,9 @@ import 'dart:io';
|
|
|
2
2
|
|
|
3
3
|
import 'package:args/args.dart';
|
|
4
4
|
import 'package:args/command_runner.dart';
|
|
5
|
+
import 'package:flutter_patcher_cli/flutter_patcher_cli.dart';
|
|
5
6
|
|
|
6
|
-
import '../
|
|
7
|
-
import '../cli_base.dart';
|
|
8
|
-
import '../config.dart';
|
|
7
|
+
import '../ui/ui.dart';
|
|
9
8
|
|
|
10
9
|
/// `flutter_patcher doctor` — environment + backend connectivity check.
|
|
11
10
|
class DoctorCommand extends Command<int> {
|
|
@@ -18,7 +17,8 @@ class DoctorCommand extends Command<int> {
|
|
|
18
17
|
String get name => 'doctor';
|
|
19
18
|
|
|
20
19
|
@override
|
|
21
|
-
String get description =>
|
|
20
|
+
String get description =>
|
|
21
|
+
'Diagnose the local environment and backend connection.';
|
|
22
22
|
|
|
23
23
|
@override
|
|
24
24
|
ArgParser get argParser => ArgParser()
|
|
@@ -26,23 +26,32 @@ class DoctorCommand extends Command<int> {
|
|
|
26
26
|
|
|
27
27
|
@override
|
|
28
28
|
Future<int> run() => runGuarded(() async {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
banner('doctor');
|
|
30
|
+
final lines = <String>[
|
|
31
|
+
kv('dart', Platform.version.split(' ').first),
|
|
32
|
+
kv(
|
|
33
|
+
'os',
|
|
34
|
+
'${Platform.operatingSystem} '
|
|
35
|
+
'${Platform.operatingSystemVersion.split('\n').first}',
|
|
36
|
+
),
|
|
37
|
+
];
|
|
33
38
|
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
34
39
|
if (cfg == null) {
|
|
35
|
-
|
|
40
|
+
lines.add(kv('config', yellow('not found — run `flutter_patcher init`')));
|
|
41
|
+
box('doctor', lines);
|
|
36
42
|
return;
|
|
37
43
|
}
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
lines
|
|
45
|
+
..add(kv('provider', cfg.provider))
|
|
46
|
+
..add(kv('channel', cfg.channel))
|
|
47
|
+
..add(kv('platform', cfg.platform));
|
|
40
48
|
try {
|
|
41
49
|
final backend = requireBackend(cfg, override: backendOverride);
|
|
42
50
|
final channels = await backend.db.getChannels();
|
|
43
|
-
|
|
51
|
+
lines.add(kv('backend', green('reachable — ${channels.join(', ')}')));
|
|
44
52
|
} catch (e) {
|
|
45
|
-
|
|
53
|
+
lines.add(kv('backend', red('unreachable — $e')));
|
|
46
54
|
}
|
|
55
|
+
box('doctor', lines);
|
|
47
56
|
});
|
|
48
57
|
}
|