@_nazmiforreal/flutter-ota 0.1.8 → 0.1.10
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/bin/flutter-ota.js +3 -3
- package/dart-src/packages/cli-tools/LICENSE +21 -0
- package/dart-src/packages/cli-tools/README.md +5 -0
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +142 -50
- package/dart-src/packages/cli-tools/lib/src/config.dart +7 -7
- package/dart-src/packages/core/CHANGELOG.md +3 -0
- package/dart-src/packages/core/LICENSE +21 -0
- package/dart-src/packages/core/README.md +23 -0
- package/dart-src/packages/core/pubspec.yaml +6 -1
- package/dart-src/plugins/aws/CHANGELOG.md +3 -0
- package/dart-src/plugins/aws/LICENSE +21 -0
- package/dart-src/plugins/aws/README.md +28 -0
- package/dart-src/plugins/aws/pubspec.yaml +8 -5
- package/dart-src/plugins/cloudflare/CHANGELOG.md +3 -0
- package/dart-src/plugins/cloudflare/LICENSE +21 -0
- package/dart-src/plugins/cloudflare/README.md +25 -0
- package/dart-src/plugins/cloudflare/pubspec.yaml +8 -5
- package/dart-src/plugins/plugin-core/CHANGELOG.md +3 -0
- package/dart-src/plugins/plugin-core/LICENSE +21 -0
- package/dart-src/plugins/plugin-core/README.md +21 -0
- package/dart-src/plugins/plugin-core/pubspec.yaml +7 -2
- package/dart-src/plugins/postgres/CHANGELOG.md +3 -0
- package/dart-src/plugins/postgres/LICENSE +21 -0
- package/dart-src/plugins/postgres/README.md +29 -0
- package/dart-src/plugins/postgres/pubspec.yaml +8 -5
- package/dart-src/plugins/supabase/CHANGELOG.md +3 -0
- package/dart-src/plugins/supabase/LICENSE +21 -0
- package/dart-src/plugins/supabase/README.md +53 -0
- package/dart-src/plugins/supabase/pubspec.yaml +9 -6
- package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +11 -0
- package/migrations/supabase/20260520014100_hot-updater_rls.sql +11 -0
- package/package.json +1 -1
|
Binary file
|
package/bin/flutter-ota.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
// Launcher for the
|
|
4
|
+
// Launcher for the flutter_ota_kit CLI.
|
|
5
5
|
//
|
|
6
6
|
// Resolves a platform+arch-specific prebuilt binary (`flutter-ota-<os>-<arch>`)
|
|
7
7
|
// shipped next to this script, falling back to `dart run` of the bundled Dart
|
|
@@ -32,7 +32,7 @@ const binName = `flutter-ota-${platformName}-${archName}${ext}`;
|
|
|
32
32
|
const binPath = path.join(binDir, binName);
|
|
33
33
|
|
|
34
34
|
const sourceEntry = path.join(
|
|
35
|
-
binDir, '..', 'dart-src', 'packages', 'cli-tools', 'bin', '
|
|
35
|
+
binDir, '..', 'dart-src', 'packages', 'cli-tools', 'bin', 'flutter_ota_kit.dart',
|
|
36
36
|
);
|
|
37
37
|
const cliDir = path.dirname(path.dirname(sourceEntry));
|
|
38
38
|
|
|
@@ -68,7 +68,7 @@ if (fs.existsSync(binPath)) {
|
|
|
68
68
|
console.error(
|
|
69
69
|
'flutter-ota: `dart pub get` failed. Install dependencies or build ' +
|
|
70
70
|
`a prebuilt manually:\n cd ${cliDir} && dart compile exe ` +
|
|
71
|
-
`bin/
|
|
71
|
+
`bin/flutter_ota_kit.dart -o ${binPath}`,
|
|
72
72
|
);
|
|
73
73
|
process.exit(1);
|
|
74
74
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xuelinger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -7,7 +7,7 @@ import '../ui/ui.dart';
|
|
|
7
7
|
|
|
8
8
|
/// `flutter_ota_kit init <backend>` — scaffold a Flutter project for OTA updates.
|
|
9
9
|
///
|
|
10
|
-
/// Beyond writing the `.
|
|
10
|
+
/// Beyond writing the `.flutter_ota_kit/config.json` (so every other `flutter_ota_kit` command
|
|
11
11
|
/// knows the backend), it also drops the files a host app actually needs:
|
|
12
12
|
///
|
|
13
13
|
/// * adds the `flutter_ota_kit` pub dependency to `pubspec.yaml`,
|
|
@@ -16,7 +16,7 @@ import '../ui/ui.dart';
|
|
|
16
16
|
/// * generates `lib/flutter_ota_kit_setup.dart` that wires the selected backend
|
|
17
17
|
/// and enables zero-click forced updates.
|
|
18
18
|
///
|
|
19
|
-
/// All CLI working state is stored under `.
|
|
19
|
+
/// All CLI working state is stored under `.flutter_ota_kit/`.
|
|
20
20
|
class InitCommand extends FlutterPatcherCommand {
|
|
21
21
|
InitCommand({this.config, this.backendOverride});
|
|
22
22
|
|
|
@@ -28,7 +28,7 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
28
28
|
|
|
29
29
|
@override
|
|
30
30
|
String get description =>
|
|
31
|
-
'Scaffold flutter_ota_kit integration files + .
|
|
31
|
+
'Scaffold flutter_ota_kit integration files + .flutter_ota_kit config for the current '
|
|
32
32
|
'Flutter project. Usage: flutter_ota_kit init <supabase|postgres|cloudflare|aws>';
|
|
33
33
|
|
|
34
34
|
@override
|
|
@@ -40,7 +40,7 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
40
40
|
..addOption('channel', abbr: 'c', defaultsTo: 'production', help: 'Default channel.')
|
|
41
41
|
..addOption('platform', defaultsTo: 'android', help: 'Default platform.')
|
|
42
42
|
..addOption('source', abbr: 's', defaultsTo: './dist', help: 'Default deploy source.')
|
|
43
|
-
..addFlag('global', help: 'Write to the global ~/.
|
|
43
|
+
..addFlag('global', help: 'Write to the global ~/.flutter_ota_kit config (no scaffolding).')
|
|
44
44
|
..addFlag('force', abbr: 'f', help: 'Overwrite an existing config.');
|
|
45
45
|
|
|
46
46
|
String? _p(String label, String? current) {
|
|
@@ -209,7 +209,7 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
209
209
|
|
|
210
210
|
if (global) {
|
|
211
211
|
stdout.writeln(
|
|
212
|
-
' ${yellow('⚠')} Secrets are stored in plaintext under ~/.
|
|
212
|
+
' ${yellow('⚠')} Secrets are stored in plaintext under ~/.flutter_ota_kit. '
|
|
213
213
|
'Restrict permissions and use a secrets manager in production.',
|
|
214
214
|
);
|
|
215
215
|
return;
|
|
@@ -224,6 +224,9 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
224
224
|
stdout.writeln(
|
|
225
225
|
' 2. In lib/main.dart call ${green('await setupFlutterOta();')} '
|
|
226
226
|
'right after WidgetsFlutterBinding.ensureInitialized() (before runApp).');
|
|
227
|
+
stdout.writeln(
|
|
228
|
+
' A `.env` scaffold was written — put secrets there, then build with '
|
|
229
|
+
'`--dart-define-from-file=.env` (environment overrides config).');
|
|
227
230
|
if (provider == 'supabase') {
|
|
228
231
|
stdout.writeln(
|
|
229
232
|
' 3. Provision the backend once: ${dim('flutter_ota_kit migrate supabase')}');
|
|
@@ -242,6 +245,7 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
242
245
|
_addPubDependency();
|
|
243
246
|
_addInternetPermission();
|
|
244
247
|
await _writeSetupFile(cfg, provider);
|
|
248
|
+
_writeEnvFile(cfg, provider);
|
|
245
249
|
_ensureGitignore();
|
|
246
250
|
}
|
|
247
251
|
|
|
@@ -306,22 +310,110 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
306
310
|
void _ensureGitignore() {
|
|
307
311
|
final gi = File('.gitignore');
|
|
308
312
|
final content = gi.existsSync() ? gi.readAsStringSync() : '';
|
|
309
|
-
if (content.contains('.
|
|
310
|
-
step('.gitignore already ignores .
|
|
313
|
+
if (content.contains('.flutter_ota_kit/')) {
|
|
314
|
+
step('.gitignore already ignores .flutter_ota_kit/');
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
final next = content.isEmpty ? '.flutter_ota_kit/\n' : '$content\n.flutter_ota_kit/\n';
|
|
318
|
+
gi.writeAsStringSync(next);
|
|
319
|
+
step('Added .flutter_ota_kit/ to .gitignore');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/// Write a `.env` scaffold for the selected backend. Every key is present but
|
|
323
|
+
/// empty by default; any value supplied during `init` (interactive prompt or
|
|
324
|
+
/// environment hint) is filled in so the app can be built with
|
|
325
|
+
/// `flutter run --dart-define-from-file=.env`. Secrets always land here (env),
|
|
326
|
+
/// never in committed source — the same values are also persisted to
|
|
327
|
+
/// `.flutter_ota_kit/config.json` for the CLI.
|
|
328
|
+
void _writeEnvFile(FlutterPatcherConfig cfg, String provider) {
|
|
329
|
+
final entries = <String, String>{};
|
|
330
|
+
switch (provider) {
|
|
331
|
+
case 'postgres':
|
|
332
|
+
final c = cfg.postgres;
|
|
333
|
+
entries['POSTGRES_HOST'] = c.host ?? '';
|
|
334
|
+
entries['POSTGRES_PORT'] = c.port ?? '5432';
|
|
335
|
+
entries['POSTGRES_DB'] = c.database ?? 'postgres';
|
|
336
|
+
entries['POSTGRES_USER'] = c.username ?? '';
|
|
337
|
+
entries['POSTGRES_PASSWORD'] = c.password ?? '';
|
|
338
|
+
entries['POSTGRES_SERVING_BASE_URL'] = c.servingBaseUrl ?? '';
|
|
339
|
+
entries['CHANNEL'] = cfg.channel;
|
|
340
|
+
entries['APP_VERSION'] = '1.0.0';
|
|
341
|
+
case 'cloudflare':
|
|
342
|
+
final c = cfg.cloudflare;
|
|
343
|
+
entries['CLOUDFLARE_ACCOUNT_ID'] = c.accountId ?? '';
|
|
344
|
+
entries['CLOUDFLARE_D1_DATABASE_ID'] = c.d1DatabaseId ?? '';
|
|
345
|
+
entries['CLOUDFLARE_API_TOKEN'] = c.apiToken ?? '';
|
|
346
|
+
entries['R2_BUCKET'] = c.r2Bucket ?? 'bundles';
|
|
347
|
+
entries['R2_ACCESS_KEY_ID'] = c.r2AccessKeyId ?? '';
|
|
348
|
+
entries['R2_SECRET_ACCESS_KEY'] = c.r2SecretAccessKey ?? '';
|
|
349
|
+
entries['R2_BASE_PATH'] = c.r2BasePath ?? '';
|
|
350
|
+
entries['CHANNEL'] = cfg.channel;
|
|
351
|
+
entries['APP_VERSION'] = '1.0.0';
|
|
352
|
+
case 'aws':
|
|
353
|
+
final c = cfg.aws;
|
|
354
|
+
entries['AWS_BUCKET'] = c.bucket ?? '';
|
|
355
|
+
entries['AWS_REGION'] = c.region ?? '';
|
|
356
|
+
entries['AWS_ACCESS_KEY_ID'] = c.accessKeyId ?? '';
|
|
357
|
+
entries['AWS_SECRET_ACCESS_KEY'] = c.secretAccessKey ?? '';
|
|
358
|
+
entries['AWS_ENDPOINT'] = c.endpoint ?? '';
|
|
359
|
+
entries['AWS_BASE_PATH'] = c.basePath ?? '';
|
|
360
|
+
entries['AWS_SESSION_TOKEN'] = c.sessionToken ?? '';
|
|
361
|
+
entries['CHANNEL'] = cfg.channel;
|
|
362
|
+
entries['APP_VERSION'] = '1.0.0';
|
|
363
|
+
case 'supabase':
|
|
364
|
+
default:
|
|
365
|
+
final c = cfg.supabase;
|
|
366
|
+
entries['SUPABASE_URL'] = c.url ?? '';
|
|
367
|
+
entries['SUPABASE_ANON_KEY'] = c.anonKey ?? '';
|
|
368
|
+
entries['SUPABASE_BUCKET'] = c.bucket ?? 'bundles';
|
|
369
|
+
entries['CHANNEL'] = cfg.channel;
|
|
370
|
+
entries['APP_VERSION'] = '1.0.0';
|
|
371
|
+
entries['SDK_VERSION'] = '1.0.0';
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
final buffer = StringBuffer();
|
|
375
|
+
buffer.writeln('# Generated by `flutter-ota init $provider`.');
|
|
376
|
+
buffer.writeln('# Fill in (or override) values, then build with:');
|
|
377
|
+
buffer.writeln('# flutter run --dart-define-from-file=.env');
|
|
378
|
+
buffer.writeln('# Environment overrides the project config and any defaults.');
|
|
379
|
+
for (final e in entries.entries) {
|
|
380
|
+
buffer.writeln('${e.key}=${e.value}');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
final file = File('.env');
|
|
384
|
+
if (file.existsSync() && !(argResults!['force'] as bool)) {
|
|
385
|
+
step('.env already exists; left untouched (use --force to overwrite)');
|
|
311
386
|
return;
|
|
312
387
|
}
|
|
313
|
-
|
|
388
|
+
file.writeAsStringSync(buffer.toString());
|
|
389
|
+
step('Wrote .env with $provider env scaffolding');
|
|
390
|
+
_ensureEnvGitignored();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
void _ensureEnvGitignored() {
|
|
394
|
+
final gi = File('.gitignore');
|
|
395
|
+
final lines =
|
|
396
|
+
gi.existsSync() ? gi.readAsStringSync().split('\n') : const <String>[];
|
|
397
|
+
if (lines.contains('.env')) {
|
|
398
|
+
step('.gitignore already ignores .env');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
final next = '${gi.existsSync() ? gi.readAsStringSync() : ''}\n.env\n';
|
|
314
402
|
gi.writeAsStringSync(next);
|
|
315
|
-
step('Added .
|
|
403
|
+
step('Added .env to .gitignore');
|
|
316
404
|
}
|
|
317
405
|
|
|
318
406
|
String _q(String? s) => s == null ? "''" : "'${s.replaceAll("'", "\\'")}'";
|
|
319
407
|
|
|
320
408
|
/// Build the body of `lib/flutter_ota_kit_setup.dart` for the selected backend.
|
|
409
|
+
///
|
|
410
|
+
/// Non-secret values default to the project config (written during `init` under
|
|
411
|
+
/// `.flutter_ota_kit/`); every value can be overridden by a build-time
|
|
412
|
+
/// `--dart-define` / `.env` (environment wins). Secrets (keys/tokens) default
|
|
413
|
+
/// to empty and are ONLY read from the environment, never hard-coded here.
|
|
321
414
|
String _setupBody(FlutterPatcherConfig cfg, String provider) {
|
|
322
415
|
final channel = _q(cfg.channel);
|
|
323
|
-
final
|
|
324
|
-
final appVersion =
|
|
416
|
+
final appVersionExpr =
|
|
325
417
|
"const String.fromEnvironment('APP_VERSION', defaultValue: '1.0.0')";
|
|
326
418
|
|
|
327
419
|
late final String configure;
|
|
@@ -330,73 +422,73 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
330
422
|
final c = cfg.postgres;
|
|
331
423
|
configure = '''
|
|
332
424
|
FlutterPatcher.configurePostgres(PostgresUpdateConfig(
|
|
333
|
-
host: ${_q(c.host)},
|
|
334
|
-
port: ${_q(c.port ?? '5432')},
|
|
335
|
-
database: ${_q(c.database)},
|
|
336
|
-
username: ${_q(c.username)},
|
|
337
|
-
password:
|
|
338
|
-
servingBaseUrl: ${_q(c.servingBaseUrl)},
|
|
339
|
-
channel: $channel,
|
|
425
|
+
host: const String.fromEnvironment('POSTGRES_HOST', defaultValue: ${_q(c.host ?? '')}),
|
|
426
|
+
port: int.tryParse(const String.fromEnvironment('POSTGRES_PORT', defaultValue: ${_q(c.port ?? '5432')})) ?? 5432,
|
|
427
|
+
database: const String.fromEnvironment('POSTGRES_DB', defaultValue: ${_q(c.database ?? 'postgres')}),
|
|
428
|
+
username: const String.fromEnvironment('POSTGRES_USER', defaultValue: ${_q(c.username ?? '')}),
|
|
429
|
+
password: const String.fromEnvironment('POSTGRES_PASSWORD', defaultValue: ''),
|
|
430
|
+
servingBaseUrl: const String.fromEnvironment('POSTGRES_SERVING_BASE_URL', defaultValue: ${_q(c.servingBaseUrl ?? '')}),
|
|
431
|
+
channel: const String.fromEnvironment('CHANNEL', defaultValue: $channel),
|
|
340
432
|
platform: Platform.android,
|
|
341
|
-
updateStrategy:
|
|
342
|
-
appVersion: $
|
|
433
|
+
updateStrategy: UpdateStrategy.appVersion,
|
|
434
|
+
appVersion: $appVersionExpr,
|
|
343
435
|
));''';
|
|
344
436
|
case 'cloudflare':
|
|
345
437
|
final c = cfg.cloudflare;
|
|
346
438
|
configure = '''
|
|
347
439
|
FlutterPatcher.configureCloudflare(CloudflareUpdateConfig(
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
cloudflareApiToken:
|
|
351
|
-
bucketName: ${_q(c.r2Bucket ?? 'bundles')},
|
|
352
|
-
accessKeyId:
|
|
353
|
-
secretAccessKey:
|
|
354
|
-
basePath: ${_q(c.r2BasePath)},
|
|
355
|
-
channel: $channel,
|
|
440
|
+
accountId: const String.fromEnvironment('CLOUDFLARE_ACCOUNT_ID', defaultValue: ${_q(c.accountId ?? '')}),
|
|
441
|
+
databaseId: const String.fromEnvironment('CLOUDFLARE_D1_DATABASE_ID', defaultValue: ${_q(c.d1DatabaseId ?? '')}),
|
|
442
|
+
cloudflareApiToken: const String.fromEnvironment('CLOUDFLARE_API_TOKEN', defaultValue: ''),
|
|
443
|
+
bucketName: const String.fromEnvironment('R2_BUCKET', defaultValue: ${_q(c.r2Bucket ?? 'bundles')}),
|
|
444
|
+
accessKeyId: const String.fromEnvironment('R2_ACCESS_KEY_ID', defaultValue: ''),
|
|
445
|
+
secretAccessKey: const String.fromEnvironment('R2_SECRET_ACCESS_KEY', defaultValue: ''),
|
|
446
|
+
basePath: const String.fromEnvironment('R2_BASE_PATH', defaultValue: ${_q(c.r2BasePath ?? '')}),
|
|
447
|
+
channel: const String.fromEnvironment('CHANNEL', defaultValue: $channel),
|
|
356
448
|
platform: Platform.android,
|
|
357
|
-
updateStrategy:
|
|
358
|
-
appVersion: $
|
|
449
|
+
updateStrategy: UpdateStrategy.appVersion,
|
|
450
|
+
appVersion: $appVersionExpr,
|
|
359
451
|
));''';
|
|
360
452
|
case 'aws':
|
|
361
453
|
final c = cfg.aws;
|
|
362
454
|
configure = '''
|
|
363
455
|
FlutterPatcher.configureAws(AwsUpdateConfig(
|
|
364
|
-
bucketName: ${_q(c.bucket)},
|
|
365
|
-
region: ${_q(c.region)},
|
|
366
|
-
accessKeyId:
|
|
367
|
-
secretAccessKey:
|
|
368
|
-
basePath: ${_q(c.basePath)},
|
|
369
|
-
endpoint: ${_q(c.endpoint)},
|
|
370
|
-
channel: $channel,
|
|
456
|
+
bucketName: const String.fromEnvironment('AWS_BUCKET', defaultValue: ${_q(c.bucket ?? '')}),
|
|
457
|
+
region: const String.fromEnvironment('AWS_REGION', defaultValue: ${_q(c.region ?? '')}),
|
|
458
|
+
accessKeyId: const String.fromEnvironment('AWS_ACCESS_KEY_ID', defaultValue: ''),
|
|
459
|
+
secretAccessKey: const String.fromEnvironment('AWS_SECRET_ACCESS_KEY', defaultValue: ''),
|
|
460
|
+
basePath: const String.fromEnvironment('AWS_BASE_PATH', defaultValue: ${_q(c.basePath ?? '')}),
|
|
461
|
+
endpoint: const String.fromEnvironment('AWS_ENDPOINT', defaultValue: ${_q(c.endpoint ?? '')}),
|
|
462
|
+
channel: const String.fromEnvironment('CHANNEL', defaultValue: $channel),
|
|
371
463
|
platform: Platform.android,
|
|
372
|
-
updateStrategy:
|
|
373
|
-
appVersion: $
|
|
464
|
+
updateStrategy: UpdateStrategy.appVersion,
|
|
465
|
+
appVersion: $appVersionExpr,
|
|
374
466
|
));''';
|
|
375
467
|
case 'supabase':
|
|
376
468
|
default:
|
|
377
469
|
final c = cfg.supabase;
|
|
378
470
|
configure = '''
|
|
379
471
|
FlutterPatcher.configureSupabase(SupabaseUpdateConfig(
|
|
380
|
-
supabaseUrl: ${_q(c.url)},
|
|
381
|
-
anonKey:
|
|
382
|
-
bucket: ${_q(c.bucket ?? 'bundles')},
|
|
383
|
-
channel: $channel,
|
|
472
|
+
supabaseUrl: const String.fromEnvironment('SUPABASE_URL', defaultValue: ${_q(c.url ?? '')}),
|
|
473
|
+
anonKey: const String.fromEnvironment('SUPABASE_ANON_KEY', defaultValue: ''),
|
|
474
|
+
bucket: const String.fromEnvironment('SUPABASE_BUCKET', defaultValue: ${_q(c.bucket ?? 'bundles')}),
|
|
475
|
+
channel: const String.fromEnvironment('CHANNEL', defaultValue: $channel),
|
|
384
476
|
platform: Platform.android,
|
|
385
|
-
updateStrategy:
|
|
386
|
-
appVersion: $
|
|
387
|
-
sdkVersion: '1.0.0',
|
|
477
|
+
updateStrategy: UpdateStrategy.appVersion,
|
|
478
|
+
appVersion: $appVersionExpr,
|
|
479
|
+
sdkVersion: const String.fromEnvironment('SDK_VERSION', defaultValue: '1.0.0'),
|
|
388
480
|
));''';
|
|
389
481
|
}
|
|
390
482
|
|
|
391
483
|
return '''
|
|
392
|
-
import 'dart:io' show Platform;
|
|
393
|
-
|
|
394
484
|
import 'package:flutter_ota_kit/flutter_ota_kit.dart';
|
|
395
485
|
|
|
396
486
|
// Generated by `flutter_ota_kit init $provider`.
|
|
397
487
|
//
|
|
398
|
-
//
|
|
399
|
-
//
|
|
488
|
+
// Non-secret values default to the project config (`.flutter_ota_kit/config.json`
|
|
489
|
+
// set up during init); every value can be overridden by a build-time
|
|
490
|
+
// `--dart-define` / `.env` (environment wins). Secrets (keys/tokens) are never
|
|
491
|
+
// hard-coded here — supply them via `.env`.
|
|
400
492
|
//
|
|
401
493
|
// Call [setupFlutterOta] once in `main()`, after
|
|
402
494
|
// WidgetsFlutterBinding.ensureInitialized() and before runApp(). It wires the
|
|
@@ -12,9 +12,9 @@ import 'package:path/path.dart' as p;
|
|
|
12
12
|
/// Resolved configuration for the flutter_ota_kit CLI.
|
|
13
13
|
///
|
|
14
14
|
/// Mirrors hot-updater's config precedence: explicit flags > environment
|
|
15
|
-
/// variables > the project `.
|
|
16
|
-
/// `~/.
|
|
17
|
-
/// be git-ignored as a single unit.
|
|
15
|
+
/// variables > the project `.flutter_ota_kit/config.json` (falling back to the
|
|
16
|
+
/// global `~/.flutter_ota_kit/config.json`). All CLI working state lives under
|
|
17
|
+
/// `.flutter_ota_kit/` so it can be git-ignored as a single unit.
|
|
18
18
|
class FlutterPatcherConfig {
|
|
19
19
|
FlutterPatcherConfig({
|
|
20
20
|
required this.provider,
|
|
@@ -138,14 +138,14 @@ class SupabaseConfigJson {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
/// Search order for a config file on disk. Both the project config and the
|
|
141
|
-
/// global config live under a `.
|
|
142
|
-
/// in one place (and can be git-ignored together).
|
|
141
|
+
/// global config live under a `.flutter_ota_kit/` directory so all CLI state is
|
|
142
|
+
/// grouped in one place (and can be git-ignored together).
|
|
143
143
|
List<File> configCandidates() {
|
|
144
|
-
final cwd = File(p.join(Directory.current.path, '.
|
|
144
|
+
final cwd = File(p.join(Directory.current.path, '.flutter_ota_kit', 'config.json'));
|
|
145
145
|
final home = Platform.environment['HOME'] ??
|
|
146
146
|
Platform.environment['USERPROFILE'] ??
|
|
147
147
|
'';
|
|
148
|
-
final global = File(p.join(home, '.
|
|
148
|
+
final global = File(p.join(home, '.flutter_ota_kit', 'config.json'));
|
|
149
149
|
return [cwd, global];
|
|
150
150
|
}
|
|
151
151
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xuelinger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# flutter_ota_kit_core
|
|
2
|
+
|
|
3
|
+
Pure-Dart core of the [flutter_ota_kit](https://github.com/HYPER12755/flutter_ota_kit) OTA toolkit. It has **no Flutter dependency** and is shared by every other package (client, plugin-core, and all four backends).
|
|
4
|
+
|
|
5
|
+
## What's inside
|
|
6
|
+
|
|
7
|
+
- **Data model** — `Bundle`, `PatchInfo`, `AppUpdateInfo`, and the `Platform` / `UpdateStrategy` / status enums that describe an over-the-air update.
|
|
8
|
+
- **Rollout math** — deterministic staged-rollout cohort helpers (`rollout.dart`).
|
|
9
|
+
- **Semver** — `semverSatisfies(target, range)` for matching a device's app version against a bundle's rollout rule.
|
|
10
|
+
- **Identifiers** — `uuidv7` generation plus a `NIL` sentinel (`uuid.dart`).
|
|
11
|
+
- **Bundle artifacts** — helpers to describe and address content-addressed patch artifacts.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```dart
|
|
16
|
+
import 'package:flutter_ota_kit_core/flutter_ota_kit_core.dart';
|
|
17
|
+
|
|
18
|
+
final ok = semverSatisfies('1.0.0', '>=1.0.0 <2.0.0'); // true
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## License
|
|
22
|
+
|
|
23
|
+
MIT — see the [project LICENSE](https://github.com/HYPER12755/flutter_ota_kit/blob/main/LICENSE).
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
name: flutter_ota_kit_core
|
|
2
2
|
description: Core data model, rollout cohorts, and shared utilities for flutter_ota_kit — Dart translation of hot-updater's packages/core.
|
|
3
3
|
version: 0.1.0
|
|
4
|
-
publish_to:
|
|
4
|
+
publish_to: https://pub.dev
|
|
5
|
+
homepage: https://github.com/HYPER12755/flutter_ota_kit
|
|
6
|
+
repository: https://github.com/HYPER12755/flutter_ota_kit
|
|
7
|
+
issue_tracker: https://github.com/HYPER12755/flutter_ota_kit/issues
|
|
8
|
+
documentation: https://github.com/HYPER12755/flutter_ota_kit#readme
|
|
9
|
+
license: MIT
|
|
5
10
|
|
|
6
11
|
environment:
|
|
7
12
|
sdk: '>=3.0.0 <4.0.0'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xuelinger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# flutter_ota_kit_aws
|
|
2
|
+
|
|
3
|
+
AWS backend for flutter_ota_kit: bundle metadata in an **S3-backed blob
|
|
4
|
+
database** and artifacts in **S3** (with optional CloudFront signed URLs). Used
|
|
5
|
+
by the `flutter-ota` CLI and the Flutter SDK's `FlutterPatcher.configureAws(...)`.
|
|
6
|
+
|
|
7
|
+
## What's inside
|
|
8
|
+
|
|
9
|
+
- `s3Database` / `S3DatabaseConfig` — bundle metadata stored as S3 objects.
|
|
10
|
+
- `s3Storage` / `AwsS3StorageConfig` — artifact storage in S3.
|
|
11
|
+
- CloudFront signed URLs: `cloudfrontSignedUrl`, `verifyCloudfrontSignedUrl`,
|
|
12
|
+
and `withCloudFrontSignedUrl(...)`.
|
|
13
|
+
- `AwsS3ClientLike` / `AwsCloudFrontClientLike` test seams.
|
|
14
|
+
|
|
15
|
+
## Configuration (environment)
|
|
16
|
+
|
|
17
|
+
| Variable | Purpose |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `AWS_REGION` | S3 region (falls back to `AWS_REGION`/`AWS_DEFAULT_REGION` if unset). |
|
|
20
|
+
| `AWS_BUCKET` | Target bucket name. |
|
|
21
|
+
| `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` | Credentials. |
|
|
22
|
+
| `AWS_ENDPOINT` | Optional custom endpoint (e.g. MinIO). |
|
|
23
|
+
| `AWS_BASE_PATH` | Optional key prefix. |
|
|
24
|
+
| `AWS_SESSION_TOKEN` | Optional temporary session token. |
|
|
25
|
+
|
|
26
|
+
## License
|
|
27
|
+
|
|
28
|
+
MIT.
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
name: flutter_ota_kit_aws
|
|
2
2
|
description: AWS S3 storage + S3/DynamoDB database plugins for flutter_ota_kit (faithful port of hot-updater plugins/aws).
|
|
3
3
|
version: 0.1.0
|
|
4
|
-
publish_to:
|
|
4
|
+
publish_to: https://pub.dev
|
|
5
|
+
homepage: https://github.com/HYPER12755/flutter_ota_kit
|
|
6
|
+
repository: https://github.com/HYPER12755/flutter_ota_kit
|
|
7
|
+
issue_tracker: https://github.com/HYPER12755/flutter_ota_kit/issues
|
|
8
|
+
documentation: https://github.com/HYPER12755/flutter_ota_kit#readme
|
|
9
|
+
license: MIT
|
|
5
10
|
|
|
6
11
|
environment:
|
|
7
12
|
sdk: ">=3.3.0 <4.0.0"
|
|
8
13
|
|
|
9
14
|
dependencies:
|
|
10
|
-
flutter_ota_kit_core:
|
|
11
|
-
|
|
12
|
-
flutter_ota_kit_plugin_core:
|
|
13
|
-
path: ../../plugins/plugin-core
|
|
15
|
+
flutter_ota_kit_core: ^0.1.0
|
|
16
|
+
flutter_ota_kit_plugin_core: ^0.0.1
|
|
14
17
|
crypto: ^3.0.0
|
|
15
18
|
http: ^1.2.0
|
|
16
19
|
path: ^1.9.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xuelinger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# flutter_ota_kit_cloudflare
|
|
2
|
+
|
|
3
|
+
Cloudflare backend for flutter_ota_kit: bundle metadata in **D1** (SQLite-at-edge)
|
|
4
|
+
and artifacts in **R2** (S3-compatible storage). Used by the `flutter-ota` CLI and
|
|
5
|
+
the Flutter SDK's `FlutterPatcher.configureCloudflare(...)`.
|
|
6
|
+
|
|
7
|
+
## What's inside
|
|
8
|
+
|
|
9
|
+
- `d1Database` / `D1DatabaseConfig` — bundle metadata in D1.
|
|
10
|
+
`CloudflareWorkerDatabaseConfig` / `cloudflareWorkerDatabase` for the
|
|
11
|
+
Worker-facing variant.
|
|
12
|
+
- `r2Storage` / `R2S3StorageConfig` — artifact storage in R2 (AWS SigV4).
|
|
13
|
+
- D1 row mappers and `D1ClientFactory` / `R2S3ClientFactory` test seams.
|
|
14
|
+
|
|
15
|
+
## Configuration (environment)
|
|
16
|
+
|
|
17
|
+
| Variable | Purpose |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `CLOUDFLARE_ACCOUNT_ID` | Cloudflare account id. |
|
|
20
|
+
| `CLOUDFLARE_API_TOKEN` | API token with D1 read/write + R2 permissions. |
|
|
21
|
+
| R2 access/secret keys + bucket | Supplied via `R2S3StorageConfig` (see the CLI `init` and `doc/backends.md`). |
|
|
22
|
+
|
|
23
|
+
## License
|
|
24
|
+
|
|
25
|
+
MIT.
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
name: flutter_ota_kit_cloudflare
|
|
2
2
|
description: Cloudflare D1 + R2 storage plugins for flutter_ota_kit (faithful port of hot-updater plugins/cloudflare).
|
|
3
3
|
version: 0.1.0
|
|
4
|
-
publish_to:
|
|
4
|
+
publish_to: https://pub.dev
|
|
5
|
+
homepage: https://github.com/HYPER12755/flutter_ota_kit
|
|
6
|
+
repository: https://github.com/HYPER12755/flutter_ota_kit
|
|
7
|
+
issue_tracker: https://github.com/HYPER12755/flutter_ota_kit/issues
|
|
8
|
+
documentation: https://github.com/HYPER12755/flutter_ota_kit#readme
|
|
9
|
+
license: MIT
|
|
5
10
|
|
|
6
11
|
environment:
|
|
7
12
|
sdk: ">=3.3.0 <4.0.0"
|
|
8
13
|
|
|
9
14
|
dependencies:
|
|
10
|
-
flutter_ota_kit_core:
|
|
11
|
-
|
|
12
|
-
flutter_ota_kit_plugin_core:
|
|
13
|
-
path: ../../plugins/plugin-core
|
|
15
|
+
flutter_ota_kit_core: ^0.1.0
|
|
16
|
+
flutter_ota_kit_plugin_core: ^0.0.1
|
|
14
17
|
crypto: ^3.0.0
|
|
15
18
|
http: ^1.2.0
|
|
16
19
|
path: ^1.9.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xuelinger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# flutter_ota_kit_plugin_core
|
|
2
|
+
|
|
3
|
+
Internal plugin abstractions shared by the flutter_ota_kit backend plugins
|
|
4
|
+
(`flutter_ota_kit_supabase`, `flutter_ota_kit_postgres`,
|
|
5
|
+
`flutter_ota_kit_cloudflare`, `flutter_ota_kit_aws`).
|
|
6
|
+
|
|
7
|
+
## What's inside
|
|
8
|
+
|
|
9
|
+
- The `Database`, `Storage`, and build interfaces that each backend implements.
|
|
10
|
+
- Factory helpers: `createDatabasePlugin`, `createStoragePlugin`,
|
|
11
|
+
`createBlobDatabasePlugin`.
|
|
12
|
+
- Pagination, content-addressed asset addressing, and storage/bundle layout
|
|
13
|
+
helpers used to keep all backends wire-compatible with the same bundle schema.
|
|
14
|
+
|
|
15
|
+
This package is a building block for the concrete backends and the `flutter-ota`
|
|
16
|
+
CLI; application code normally consumes the higher-level `flutter_ota_kit` API
|
|
17
|
+
instead.
|
|
18
|
+
|
|
19
|
+
## License
|
|
20
|
+
|
|
21
|
+
MIT.
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
name: flutter_ota_kit_plugin_core
|
|
2
2
|
description: Plugin core abstractions for flutter_ota_kit — database, storage, and build interfaces.
|
|
3
3
|
version: 0.0.1
|
|
4
|
+
publish_to: https://pub.dev
|
|
5
|
+
homepage: https://github.com/HYPER12755/flutter_ota_kit
|
|
6
|
+
repository: https://github.com/HYPER12755/flutter_ota_kit
|
|
7
|
+
issue_tracker: https://github.com/HYPER12755/flutter_ota_kit/issues
|
|
8
|
+
documentation: https://github.com/HYPER12755/flutter_ota_kit#readme
|
|
9
|
+
license: MIT
|
|
4
10
|
|
|
5
11
|
environment:
|
|
6
12
|
sdk: ">=3.3.0 <4.0.0"
|
|
7
13
|
|
|
8
14
|
dependencies:
|
|
9
|
-
flutter_ota_kit_core:
|
|
10
|
-
path: ../../packages/core
|
|
15
|
+
flutter_ota_kit_core: ^0.1.0
|
|
11
16
|
mime: ^1.0.0
|
|
12
17
|
|
|
13
18
|
dev_dependencies:
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xuelinger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# flutter_ota_kit_postgres
|
|
2
|
+
|
|
3
|
+
Postgres backend for flutter_ota_kit: bundle metadata in a Postgres database and
|
|
4
|
+
artifacts in the bytea-backed storage plugin. Used by the `flutter-ota` CLI and
|
|
5
|
+
the Flutter SDK's `FlutterPatcher.configurePostgres(...)`.
|
|
6
|
+
|
|
7
|
+
## What's inside
|
|
8
|
+
|
|
9
|
+
- `postgresDatabase` / `PostgresConfig` — bundle read/write over a Postgres
|
|
10
|
+
connection.
|
|
11
|
+
- `postgresStorage` / `PostgresStorageConfig` / `resolvePostgresStorageClient` —
|
|
12
|
+
artifact storage.
|
|
13
|
+
- `getUpdateInfo` — resolve the latest applicable bundle for a device.
|
|
14
|
+
- Row mappers (`PostgresBundleRow`, `PostgresBundlePatchRow`,
|
|
15
|
+
`bundleToRowValues`, `bundleToPatchRows`) for the `bundles` / `bundle_patches`
|
|
16
|
+
tables.
|
|
17
|
+
|
|
18
|
+
## Configuration (environment)
|
|
19
|
+
|
|
20
|
+
| Variable | Purpose |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| `DATABASE_URL` | Full Postgres connection string (preferred). |
|
|
23
|
+
| `POSTGRES_HOST` / `POSTGRES_PORT` / `POSTGRES_DB` / `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_SSLMODE` | Individual connection fields. |
|
|
24
|
+
| `POSTGRES_SERVING_BASE_URL` | HTTP proxy/base URL in front of the storage table for artifact downloads. |
|
|
25
|
+
| `POSTGRES_BASE_PATH` | Optional object-key prefix. |
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
|
|
29
|
+
MIT.
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
name: flutter_ota_kit_postgres
|
|
2
2
|
description: Postgres plugin for flutter_ota_kit — database backend.
|
|
3
3
|
version: 0.1.0
|
|
4
|
-
publish_to:
|
|
4
|
+
publish_to: https://pub.dev
|
|
5
|
+
homepage: https://github.com/HYPER12755/flutter_ota_kit
|
|
6
|
+
repository: https://github.com/HYPER12755/flutter_ota_kit
|
|
7
|
+
issue_tracker: https://github.com/HYPER12755/flutter_ota_kit/issues
|
|
8
|
+
documentation: https://github.com/HYPER12755/flutter_ota_kit#readme
|
|
9
|
+
license: MIT
|
|
5
10
|
|
|
6
11
|
environment:
|
|
7
12
|
sdk: ">=3.3.0 <4.0.0"
|
|
8
13
|
|
|
9
14
|
dependencies:
|
|
10
|
-
flutter_ota_kit_core:
|
|
11
|
-
|
|
12
|
-
flutter_ota_kit_plugin_core:
|
|
13
|
-
path: ../../plugins/plugin-core
|
|
15
|
+
flutter_ota_kit_core: ^0.1.0
|
|
16
|
+
flutter_ota_kit_plugin_core: ^0.0.1
|
|
14
17
|
postgres: ^3.4.0
|
|
15
18
|
|
|
16
19
|
dev_dependencies:
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xuelinger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# flutter_ota_kit_supabase
|
|
2
|
+
|
|
3
|
+
Supabase backend for [flutter_ota_kit](https://github.com/HYPER12755/flutter_ota_kit).
|
|
4
|
+
Implements the bundle **database** (PostgREST `bundles` table) and **storage**
|
|
5
|
+
(signed URLs) used both by the `flutter-ota` CLI and by the Flutter SDK's
|
|
6
|
+
`FlutterPatcher.configureSupabase(...)`.
|
|
7
|
+
|
|
8
|
+
## What's inside
|
|
9
|
+
|
|
10
|
+
- `supabaseDatabase` / `SupabaseDatabaseConfig` — read/update bundles over the
|
|
11
|
+
Supabase REST (PostgREST) API.
|
|
12
|
+
- `supabaseStorage` / `SupabaseStorageConfig` / `parseSupabaseStorageUri` —
|
|
13
|
+
resolve presigned download URLs.
|
|
14
|
+
- Edge-function variants: `supabaseEdgeFunctionDatabase`,
|
|
15
|
+
`supabaseEdgeFunctionStorage` (extra security boundary via a Supabase Edge
|
|
16
|
+
Function instead of direct client access).
|
|
17
|
+
- `SupabaseClientFactory` / `createSupabaseHttpClient` — test seams / custom
|
|
18
|
+
HTTP clients.
|
|
19
|
+
- `SupabaseServiceRoleConfig` — holds the service-role or anon key with a
|
|
20
|
+
`resolveKey()` helper.
|
|
21
|
+
|
|
22
|
+
## Configuration (environment)
|
|
23
|
+
|
|
24
|
+
| Variable | Purpose |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| `SUPABASE_URL` | Project REST URL (`https://<ref>.supabase.co`). |
|
|
27
|
+
| `SUPABASE_ANON_KEY` | Public anon key (RLS-protected reads). |
|
|
28
|
+
| `SUPABASE_SERVICE_ROLE_KEY` | Service-role key (migrations / writes). |
|
|
29
|
+
| `SUPABASE_BUCKET` | Storage bucket holding patch artifacts. |
|
|
30
|
+
| `SUPABASE_BASE_PATH` | Optional prefix for stored objects. |
|
|
31
|
+
| `SUPABASE_DATABASE_URL` | Connection string for migrations (alternative). |
|
|
32
|
+
|
|
33
|
+
## Usage (Flutter SDK)
|
|
34
|
+
|
|
35
|
+
```dart
|
|
36
|
+
import 'package:flutter_ota_kit/flutter_ota_kit.dart';
|
|
37
|
+
|
|
38
|
+
FlutterPatcher.configureSupabase(SupabaseUpdateConfig(
|
|
39
|
+
supabaseUrl: 'https://<ref>.supabase.co',
|
|
40
|
+
anonKey: '<anon>',
|
|
41
|
+
bucket: 'bundles',
|
|
42
|
+
channel: 'production',
|
|
43
|
+
platform: Platform.android,
|
|
44
|
+
updateStrategy: UpdateStrategy.appVersion,
|
|
45
|
+
appVersion: '1.0.0',
|
|
46
|
+
));
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
See [doc/backends.md](https://github.com/HYPER12755/flutter_ota_kit/blob/main/doc/backends.md).
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
MIT.
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
name: flutter_ota_kit_supabase
|
|
2
2
|
description: Supabase plugin for flutter_ota_kit — database + storage backends.
|
|
3
|
-
version: 0.1.
|
|
4
|
-
publish_to:
|
|
3
|
+
version: 0.1.1
|
|
4
|
+
publish_to: https://pub.dev
|
|
5
|
+
homepage: https://github.com/HYPER12755/flutter_ota_kit
|
|
6
|
+
repository: https://github.com/HYPER12755/flutter_ota_kit
|
|
7
|
+
issue_tracker: https://github.com/HYPER12755/flutter_ota_kit/issues
|
|
8
|
+
documentation: https://github.com/HYPER12755/flutter_ota_kit#readme
|
|
9
|
+
license: MIT
|
|
5
10
|
|
|
6
11
|
environment:
|
|
7
12
|
sdk: ">=3.3.0 <4.0.0"
|
|
8
13
|
|
|
9
14
|
dependencies:
|
|
10
|
-
flutter_ota_kit_core:
|
|
11
|
-
|
|
12
|
-
flutter_ota_kit_plugin_core:
|
|
13
|
-
path: ../../plugins/plugin-core
|
|
15
|
+
flutter_ota_kit_core: ^0.1.0
|
|
16
|
+
flutter_ota_kit_plugin_core: ^0.0.1
|
|
14
17
|
http: ^1.2.0
|
|
15
18
|
supabase: ^2.14.0
|
|
16
19
|
|
|
@@ -46,3 +46,14 @@ ALTER FUNCTION public.get_update_info_by_app_version(
|
|
|
46
46
|
text
|
|
47
47
|
)
|
|
48
48
|
SET search_path = public, pg_catalog;
|
|
49
|
+
|
|
50
|
+
-- OTA update checks are unauthenticated by design (the device only carries the
|
|
51
|
+
-- anon key), so bundle metadata must be publicly readable. hot-updater's hosted
|
|
52
|
+
-- Supabase flow relies on these policies existing.
|
|
53
|
+
CREATE POLICY "allow public read bundles" ON public.bundles FOR SELECT USING (true);
|
|
54
|
+
CREATE POLICY "allow public read bundle_patches" ON public.bundle_patches FOR SELECT USING (true);
|
|
55
|
+
|
|
56
|
+
-- Allow anon/authenticated to resolve signed download URLs for the bundles bucket.
|
|
57
|
+
CREATE POLICY "public bundles read" ON storage.objects
|
|
58
|
+
FOR SELECT TO anon, authenticated
|
|
59
|
+
USING (bucket_id = 'bundles');
|
|
@@ -46,3 +46,14 @@ ALTER FUNCTION public.get_update_info_by_app_version(
|
|
|
46
46
|
text
|
|
47
47
|
)
|
|
48
48
|
SET search_path = public, pg_catalog;
|
|
49
|
+
|
|
50
|
+
-- OTA update checks are unauthenticated by design (the device only carries the
|
|
51
|
+
-- anon key), so bundle metadata must be publicly readable. hot-updater's hosted
|
|
52
|
+
-- Supabase flow relies on these policies existing.
|
|
53
|
+
CREATE POLICY "allow public read bundles" ON public.bundles FOR SELECT USING (true);
|
|
54
|
+
CREATE POLICY "allow public read bundle_patches" ON public.bundle_patches FOR SELECT USING (true);
|
|
55
|
+
|
|
56
|
+
-- Allow anon/authenticated to resolve signed download URLs for the bundles bucket.
|
|
57
|
+
CREATE POLICY "public bundles read" ON storage.objects
|
|
58
|
+
FOR SELECT TO anon, authenticated
|
|
59
|
+
USING (bucket_id = 'bundles');
|
package/package.json
CHANGED