@_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
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import 'dart:convert';
|
|
1
2
|
import 'dart:io';
|
|
2
3
|
|
|
4
|
+
import 'package:http/http.dart' as http;
|
|
5
|
+
|
|
3
6
|
import 'package:flutter_ota_kit_cli/flutter_ota_kit_cli.dart';
|
|
4
7
|
|
|
5
8
|
import '../ui/ui.dart';
|
|
@@ -7,7 +10,14 @@ import '../ui/ui.dart';
|
|
|
7
10
|
/// `flutter-ota doctor` — environment + backend connectivity check.
|
|
8
11
|
class DoctorCommand extends FlutterPatcherCommand {
|
|
9
12
|
DoctorCommand({this.config, this.backendOverride}) {
|
|
10
|
-
|
|
13
|
+
final detected = config?.provider ?? loadConfig()?.provider;
|
|
14
|
+
argParser.addOption(
|
|
15
|
+
'backend',
|
|
16
|
+
abbr: 'b',
|
|
17
|
+
help: detected != null
|
|
18
|
+
? 'Backend provider [detected: $detected].'
|
|
19
|
+
: 'Backend provider.',
|
|
20
|
+
);
|
|
11
21
|
}
|
|
12
22
|
|
|
13
23
|
final FlutterPatcherConfig? config;
|
|
@@ -41,10 +51,8 @@ class DoctorCommand extends FlutterPatcherCommand {
|
|
|
41
51
|
steps.success('Channel ${cfg.channel}');
|
|
42
52
|
steps.success('Platform ${cfg.platform}');
|
|
43
53
|
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
await _checkPocketBase(cfg, steps);
|
|
47
|
-
}
|
|
54
|
+
// Backend-specific pre-flight checks.
|
|
55
|
+
await _checkProvider(cfg, steps);
|
|
48
56
|
|
|
49
57
|
try {
|
|
50
58
|
final backend = requireBackend(cfg, override: backendOverride);
|
|
@@ -55,19 +63,120 @@ class DoctorCommand extends FlutterPatcherCommand {
|
|
|
55
63
|
final time = ms >= 1000
|
|
56
64
|
? '${(ms / 1000).toStringAsFixed(1)}s'
|
|
57
65
|
: '${ms}ms';
|
|
58
|
-
steps.success(
|
|
59
|
-
'Backend reachable — ${channels.join(', ')} in $time',
|
|
60
|
-
);
|
|
66
|
+
steps.success('Backend reachable — ${channels.join(', ')} in $time');
|
|
61
67
|
} catch (e) {
|
|
62
68
|
steps.fail('Backend unreachable — $e');
|
|
63
69
|
}
|
|
64
70
|
steps.summary();
|
|
65
71
|
});
|
|
66
72
|
|
|
67
|
-
Future<void>
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
Future<void> _checkProvider(FlutterPatcherConfig cfg, Steps steps) async {
|
|
74
|
+
switch (cfg.provider) {
|
|
75
|
+
case 'supabase':
|
|
76
|
+
await _checkSupabase(cfg, steps);
|
|
77
|
+
case 'postgres':
|
|
78
|
+
await _checkPostgres(cfg, steps);
|
|
79
|
+
case 'cloudflare':
|
|
80
|
+
await _checkCloudflare(cfg, steps);
|
|
81
|
+
case 'aws':
|
|
82
|
+
await _checkAws(cfg, steps);
|
|
83
|
+
case 'pocketbase':
|
|
84
|
+
await _checkPocketBase(cfg, steps);
|
|
85
|
+
default:
|
|
86
|
+
steps.fail('Unknown provider: ${cfg.provider}');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
Future<void> _checkSupabase(FlutterPatcherConfig cfg, Steps steps) async {
|
|
91
|
+
final url = cfg.supabase.url;
|
|
92
|
+
if (url == null || url.isEmpty) {
|
|
93
|
+
steps.fail('SUPABASE_URL not set');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
final res = await http
|
|
98
|
+
.get(Uri.parse('$url/rest/v1/'))
|
|
99
|
+
.timeout(const Duration(seconds: 5));
|
|
100
|
+
if (res.statusCode < 400) {
|
|
101
|
+
steps.success('Supabase reachable ($url)');
|
|
102
|
+
} else {
|
|
103
|
+
steps.fail('Supabase returned ${res.statusCode}');
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
steps.fail('Supabase unreachable — $e');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
Future<void> _checkPostgres(FlutterPatcherConfig cfg, Steps steps) async {
|
|
111
|
+
final host = cfg.postgres.host;
|
|
112
|
+
if (host == null || host.isEmpty) {
|
|
113
|
+
steps.fail('POSTGRES_HOST not set');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
final port = int.tryParse(cfg.postgres.port ?? '5432') ?? 5432;
|
|
117
|
+
try {
|
|
118
|
+
final socket = await Socket.connect(
|
|
119
|
+
host,
|
|
120
|
+
port,
|
|
121
|
+
).timeout(const Duration(seconds: 5));
|
|
122
|
+
await socket.close();
|
|
123
|
+
steps.success('Postgres reachable ($host:$port)');
|
|
124
|
+
} catch (e) {
|
|
125
|
+
steps.fail('Postgres unreachable — $e');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
Future<void> _checkCloudflare(FlutterPatcherConfig cfg, Steps steps) async {
|
|
130
|
+
final accountId = cfg.cloudflare.accountId;
|
|
131
|
+
final apiToken = cfg.cloudflare.apiToken;
|
|
132
|
+
if (accountId == null || accountId.isEmpty) {
|
|
133
|
+
steps.fail('CLOUDFLARE_ACCOUNT_ID not set');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (apiToken == null || apiToken.isEmpty) {
|
|
137
|
+
steps.fail('CLOUDFLARE_API_TOKEN not set');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
final res = await http
|
|
142
|
+
.get(
|
|
143
|
+
Uri.parse(
|
|
144
|
+
'https://api.cloudflare.com/client/v4/accounts/$accountId',
|
|
145
|
+
),
|
|
146
|
+
headers: {
|
|
147
|
+
'Authorization': 'Bearer $apiToken',
|
|
148
|
+
'Content-Type': 'application/json',
|
|
149
|
+
},
|
|
150
|
+
)
|
|
151
|
+
.timeout(const Duration(seconds: 5));
|
|
152
|
+
final body = jsonDecode(res.body) as Map<String, dynamic>;
|
|
153
|
+
if (body['success'] == true) {
|
|
154
|
+
steps.success('Cloudflare account reachable');
|
|
155
|
+
} else {
|
|
156
|
+
final errors = body['errors'] as List? ?? [];
|
|
157
|
+
final msg = errors.isNotEmpty ? errors.first['message'] : res.body;
|
|
158
|
+
steps.fail('Cloudflare API error: $msg');
|
|
159
|
+
}
|
|
160
|
+
} catch (e) {
|
|
161
|
+
steps.fail('Cloudflare unreachable — $e');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
Future<void> _checkAws(FlutterPatcherConfig cfg, Steps steps) async {
|
|
166
|
+
final bucket = cfg.aws.bucket;
|
|
167
|
+
if (bucket == null || bucket.isEmpty) {
|
|
168
|
+
steps.fail('AWS_BUCKET not set');
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
final region = cfg.aws.region;
|
|
172
|
+
if (region == null || region.isEmpty) {
|
|
173
|
+
steps.fail('AWS_REGION not set');
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
steps.success('AWS S3 bucket "$bucket" in $region');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
Future<void> _checkPocketBase(FlutterPatcherConfig cfg, Steps steps) async {
|
|
71
180
|
final paths = PocketBaseInstallPaths.resolve();
|
|
72
181
|
final installed = await paths.binaryPath.exists();
|
|
73
182
|
if (installed) {
|
|
@@ -79,8 +188,8 @@ class DoctorCommand extends FlutterPatcherCommand {
|
|
|
79
188
|
}
|
|
80
189
|
final url = cfg.pocketbase.url;
|
|
81
190
|
if (url != null && url.isNotEmpty) {
|
|
191
|
+
final client = PocketBaseClient(url);
|
|
82
192
|
try {
|
|
83
|
-
final client = PocketBaseClient(url);
|
|
84
193
|
final ok = await client.health();
|
|
85
194
|
if (ok) {
|
|
86
195
|
steps.success('PocketBase reachable');
|
|
@@ -89,6 +198,8 @@ class DoctorCommand extends FlutterPatcherCommand {
|
|
|
89
198
|
}
|
|
90
199
|
} catch (e) {
|
|
91
200
|
steps.fail('PocketBase unreachable — $e');
|
|
201
|
+
} finally {
|
|
202
|
+
client.close();
|
|
92
203
|
}
|
|
93
204
|
}
|
|
94
205
|
}
|
|
@@ -25,6 +25,7 @@ class FingerprintCommand extends FlutterPatcherCommand {
|
|
|
25
25
|
banner(name);
|
|
26
26
|
final source = argResults!['source'] as String;
|
|
27
27
|
final hash = generateFingerprint(source);
|
|
28
|
-
|
|
28
|
+
step('source ${dim(source)}');
|
|
29
|
+
step('hash ${cyan(hash)}');
|
|
29
30
|
});
|
|
30
31
|
}
|
|
@@ -31,7 +31,11 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
31
31
|
defaultsTo: 'production',
|
|
32
32
|
help: 'Default channel.',
|
|
33
33
|
);
|
|
34
|
-
argParser.addOption(
|
|
34
|
+
argParser.addOption(
|
|
35
|
+
'platform',
|
|
36
|
+
defaultsTo: 'android',
|
|
37
|
+
help: 'Default platform.',
|
|
38
|
+
);
|
|
35
39
|
argParser.addOption(
|
|
36
40
|
'source',
|
|
37
41
|
abbr: 's',
|
|
@@ -42,7 +46,11 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
42
46
|
'global',
|
|
43
47
|
help: 'Write to the global ~/.flutter_ota_kit config (no scaffolding).',
|
|
44
48
|
);
|
|
45
|
-
argParser.addFlag(
|
|
49
|
+
argParser.addFlag(
|
|
50
|
+
'force',
|
|
51
|
+
abbr: 'f',
|
|
52
|
+
help: 'Overwrite an existing config.',
|
|
53
|
+
);
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
final FlutterPatcherConfig? config;
|
|
@@ -226,10 +234,7 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
226
234
|
'Supabase service role key',
|
|
227
235
|
env['SUPABASE_SERVICE_ROLE_KEY'],
|
|
228
236
|
),
|
|
229
|
-
anonKey: _p(
|
|
230
|
-
'Supabase publishable key',
|
|
231
|
-
env['SUPABASE_PUBLISHABLE_KEY'],
|
|
232
|
-
),
|
|
237
|
+
anonKey: _p('Supabase anon key', env['SUPABASE_ANON_KEY']),
|
|
233
238
|
bucket: _p('Storage bucket', env['SUPABASE_BUCKET'] ?? 'bundles'),
|
|
234
239
|
basePath: _p('Storage base path', env['SUPABASE_BASE_PATH']),
|
|
235
240
|
managementKey: _p(
|
|
@@ -248,8 +253,10 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
248
253
|
step('Wrote ${file.path}');
|
|
249
254
|
|
|
250
255
|
if (global) {
|
|
251
|
-
warn(
|
|
252
|
-
|
|
256
|
+
warn(
|
|
257
|
+
'Secrets are stored in plaintext under ~/.flutter_ota_kit. '
|
|
258
|
+
'Restrict permissions and use a secrets manager in production.',
|
|
259
|
+
);
|
|
253
260
|
return;
|
|
254
261
|
}
|
|
255
262
|
|
|
@@ -259,21 +266,33 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
259
266
|
stdout.writeln();
|
|
260
267
|
stdout.writeln(' ${cyan('Next steps:')}');
|
|
261
268
|
step('1. ${dim('flutter pub get')}');
|
|
262
|
-
step(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
269
|
+
step(
|
|
270
|
+
'2. In lib/main.dart call ${green('await setupFlutterOta();')} '
|
|
271
|
+
'right after WidgetsFlutterBinding.ensureInitialized() (before runApp). '
|
|
272
|
+
'A `.env` scaffold was written — put secrets there, then build with '
|
|
273
|
+
'`--dart-define-from-file=.env` (environment overrides config).',
|
|
274
|
+
);
|
|
266
275
|
if (provider == 'supabase') {
|
|
267
|
-
step(
|
|
276
|
+
step(
|
|
277
|
+
'3. Provision the backend once: ${dim('flutter-ota migrate supabase')}',
|
|
278
|
+
);
|
|
268
279
|
} else if (provider == 'postgres') {
|
|
269
280
|
step('3. Provision the backend: ${dim('flutter-ota migrate postgres')}');
|
|
281
|
+
} else if (provider == 'cloudflare') {
|
|
282
|
+
step(
|
|
283
|
+
'3. Provision the backend: ${dim('flutter-ota migrate cloudflare')}',
|
|
284
|
+
);
|
|
285
|
+
} else if (provider == 'aws') {
|
|
286
|
+
step('3. Provision the backend: ${dim('flutter-ota migrate aws')}');
|
|
270
287
|
} else if (provider == 'pocketbase') {
|
|
271
|
-
step(
|
|
272
|
-
|
|
273
|
-
|
|
288
|
+
step(
|
|
289
|
+
'3. Provision the backend: ${dim('flutter-ota migrate pocketbase')}',
|
|
290
|
+
);
|
|
274
291
|
}
|
|
275
|
-
step(
|
|
276
|
-
|
|
292
|
+
step(
|
|
293
|
+
'4. Build a patch: ${dim('flutter-ota build --version 1.0.1')} '
|
|
294
|
+
'then ${dim('flutter-ota deploy')}',
|
|
295
|
+
);
|
|
277
296
|
});
|
|
278
297
|
|
|
279
298
|
/// Generate the integration files (pubspec dep, manifest permission, setup dart
|
|
@@ -304,7 +323,9 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
304
323
|
final marker = '\ndependencies:';
|
|
305
324
|
final idx = content.indexOf(marker);
|
|
306
325
|
if (idx == -1) {
|
|
307
|
-
warn(
|
|
326
|
+
warn(
|
|
327
|
+
'Could not find a `dependencies:` block; add `flutter_ota_kit` manually.',
|
|
328
|
+
);
|
|
308
329
|
return;
|
|
309
330
|
}
|
|
310
331
|
final insertAt = idx + marker.length;
|
|
@@ -326,7 +347,9 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
326
347
|
}
|
|
327
348
|
final match = RegExp(r'<manifest[^>]*>').firstMatch(content);
|
|
328
349
|
if (match == null) {
|
|
329
|
-
warn(
|
|
350
|
+
warn(
|
|
351
|
+
'Could not locate <manifest> tag; add INTERNET permission manually.',
|
|
352
|
+
);
|
|
330
353
|
return;
|
|
331
354
|
}
|
|
332
355
|
final insertAt = match.end;
|
|
@@ -420,7 +443,7 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
420
443
|
default:
|
|
421
444
|
final c = cfg.supabase;
|
|
422
445
|
entries['SUPABASE_URL'] = c.url ?? '';
|
|
423
|
-
entries['
|
|
446
|
+
entries['SUPABASE_ANON_KEY'] = c.anonKey ?? '';
|
|
424
447
|
entries['SUPABASE_BUCKET'] = c.bucket ?? 'bundles';
|
|
425
448
|
entries['CHANNEL'] = cfg.channel;
|
|
426
449
|
entries['APP_VERSION'] = '';
|
|
@@ -462,7 +485,14 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
462
485
|
step('Added .env to .gitignore');
|
|
463
486
|
}
|
|
464
487
|
|
|
465
|
-
String _q(String? s)
|
|
488
|
+
String _q(String? s) {
|
|
489
|
+
if (s == null) return "''";
|
|
490
|
+
final escaped = s
|
|
491
|
+
.replaceAll('\\', '\\\\')
|
|
492
|
+
.replaceAll('\$', '\\\$')
|
|
493
|
+
.replaceAll("'", "\\'");
|
|
494
|
+
return "'$escaped'";
|
|
495
|
+
}
|
|
466
496
|
|
|
467
497
|
/// Build the body of `lib/flutter_ota_kit_setup.dart` for the selected backend.
|
|
468
498
|
///
|
|
@@ -489,7 +519,7 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
489
519
|
password: const String.fromEnvironment('POSTGRES_PASSWORD', defaultValue: ''),
|
|
490
520
|
servingBaseUrl: const String.fromEnvironment('POSTGRES_SERVING_BASE_URL', defaultValue: ${_q(c.servingBaseUrl ?? '')}),
|
|
491
521
|
channel: const String.fromEnvironment('CHANNEL', defaultValue: $channel),
|
|
492
|
-
platform: Platform.
|
|
522
|
+
platform: Platform.${cfg.platform},
|
|
493
523
|
updateStrategy: UpdateStrategy.appVersion,
|
|
494
524
|
appVersion: $appVersionExpr,
|
|
495
525
|
));''';
|
|
@@ -506,7 +536,7 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
506
536
|
secretAccessKey: const String.fromEnvironment('R2_SECRET_ACCESS_KEY', defaultValue: ''),
|
|
507
537
|
basePath: const String.fromEnvironment('R2_BASE_PATH', defaultValue: ${_q(c.r2BasePath ?? '')}),
|
|
508
538
|
channel: const String.fromEnvironment('CHANNEL', defaultValue: $channel),
|
|
509
|
-
platform: Platform.
|
|
539
|
+
platform: Platform.${cfg.platform},
|
|
510
540
|
updateStrategy: UpdateStrategy.appVersion,
|
|
511
541
|
appVersion: $appVersionExpr,
|
|
512
542
|
));''';
|
|
@@ -522,7 +552,7 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
522
552
|
basePath: const String.fromEnvironment('AWS_BASE_PATH', defaultValue: ${_q(c.basePath ?? '')}),
|
|
523
553
|
endpoint: const String.fromEnvironment('AWS_ENDPOINT', defaultValue: ${_q(c.endpoint ?? '')}),
|
|
524
554
|
channel: const String.fromEnvironment('CHANNEL', defaultValue: $channel),
|
|
525
|
-
platform: Platform.
|
|
555
|
+
platform: Platform.${cfg.platform},
|
|
526
556
|
updateStrategy: UpdateStrategy.appVersion,
|
|
527
557
|
appVersion: $appVersionExpr,
|
|
528
558
|
));''';
|
|
@@ -537,7 +567,7 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
537
567
|
bundlesCollection: const String.fromEnvironment('POCKETBASE_BUNDLES_COLLECTION', defaultValue: ${_q(c.bundlesCollection)}),
|
|
538
568
|
bundlesBucket: const String.fromEnvironment('POCKETBASE_BUCKET', defaultValue: ${_q(c.bundlesBucket)}),
|
|
539
569
|
channel: const String.fromEnvironment('CHANNEL', defaultValue: $channel),
|
|
540
|
-
platform: Platform.
|
|
570
|
+
platform: Platform.${cfg.platform},
|
|
541
571
|
updateStrategy: UpdateStrategy.appVersion,
|
|
542
572
|
appVersion: $appVersionExpr,
|
|
543
573
|
));''';
|
|
@@ -548,10 +578,10 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
548
578
|
'''
|
|
549
579
|
FlutterPatcher.configureSupabase(SupabaseUpdateConfig(
|
|
550
580
|
supabaseUrl: const String.fromEnvironment('SUPABASE_URL', defaultValue: ${_q(c.url ?? '')}),
|
|
551
|
-
anonKey: const String.fromEnvironment('
|
|
581
|
+
anonKey: const String.fromEnvironment('SUPABASE_ANON_KEY', defaultValue: ''),
|
|
552
582
|
bucket: const String.fromEnvironment('SUPABASE_BUCKET', defaultValue: ${_q(c.bucket ?? 'bundles')}),
|
|
553
583
|
channel: const String.fromEnvironment('CHANNEL', defaultValue: $channel),
|
|
554
|
-
platform: Platform.
|
|
584
|
+
platform: Platform.${cfg.platform},
|
|
555
585
|
updateStrategy: UpdateStrategy.appVersion,
|
|
556
586
|
appVersion: $appVersionExpr,
|
|
557
587
|
sdkVersion: const String.fromEnvironment('SDK_VERSION', defaultValue: '1.0.0'),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import 'dart:convert';
|
|
2
|
+
import 'dart:io';
|
|
2
3
|
|
|
3
4
|
import '../cli_base.dart';
|
|
4
5
|
import '../config.dart';
|
|
@@ -8,7 +9,10 @@ import '../ui/ui.dart';
|
|
|
8
9
|
/// `flutter_ota_kit keys` — generate an Ed25519 keypair for bundle signing.
|
|
9
10
|
class KeysCommand extends FlutterPatcherCommand {
|
|
10
11
|
KeysCommand() {
|
|
11
|
-
argParser.addFlag(
|
|
12
|
+
argParser.addFlag(
|
|
13
|
+
'save',
|
|
14
|
+
help: 'Persist the public key into the project config.',
|
|
15
|
+
);
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
@override
|
|
@@ -38,11 +42,10 @@ class KeysCommand extends FlutterPatcherCommand {
|
|
|
38
42
|
final file = configCandidates().first;
|
|
39
43
|
final json =
|
|
40
44
|
file.existsSync() && file.readAsStringSync().trim().isNotEmpty
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
? (jsonDecode(file.readAsStringSync()) as Map<String, dynamic>)
|
|
46
|
+
: <String, dynamic>{};
|
|
43
47
|
writePath(json, 'publicKey', publicB64);
|
|
44
|
-
await steps.run<void>('Saving public key to ${file.path}',
|
|
45
|
-
() async {
|
|
48
|
+
await steps.run<void>('Saving public key to ${file.path}', () async {
|
|
46
49
|
_save(json);
|
|
47
50
|
});
|
|
48
51
|
}
|
|
@@ -53,5 +56,8 @@ class KeysCommand extends FlutterPatcherCommand {
|
|
|
53
56
|
final file = configCandidates().first;
|
|
54
57
|
file.parent.createSync(recursive: true);
|
|
55
58
|
file.writeAsStringSync(const JsonEncoder.withIndent(' ').convert(json));
|
|
59
|
+
if (!Platform.isWindows) {
|
|
60
|
+
Process.runSync('chmod', ['600', file.path]);
|
|
61
|
+
}
|
|
56
62
|
}
|
|
57
63
|
}
|