@_nazmiforreal/flutter-ota 0.1.19 → 0.1.21
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/lib/flutter_ota_kit_cli.dart +4 -3
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +3 -2
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +22 -10
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +16 -5
- package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +3 -0
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +7 -8
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +12 -3
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +129 -6
- package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +2 -1
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +23 -14
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +4 -0
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +266 -26
- package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +1422 -16
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +4 -1
- package/dart-src/packages/cli-tools/lib/src/commands/storage.dart +10 -9
- package/dart-src/packages/cli-tools/lib/src/config.dart +14 -19
- package/dart-src/packages/cli-tools/lib/src/operations.dart +1 -1
- package/dart-src/packages/cli-tools/lib/src/pocketbase/installer.dart +1 -1
- package/dart-src/packages/cli-tools/lib/src/pocketbase/process_manager.dart +113 -10
- package/dart-src/packages/cli-tools/lib/src/pocketbase/schema_installer.dart +59 -6
- package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +2 -2
- package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +149 -0
- package/dart-src/packages/cli-tools/test/pocketbase_schema_test.dart +14 -9
- package/dart-src/packages/core/CHANGELOG.md +6 -1
- package/dart-src/packages/core/lib/flutter_ota_kit_core.dart +2 -0
- package/dart-src/packages/core/lib/src/app_update_info.dart +73 -1
- package/dart-src/packages/core/lib/src/bundle.dart +62 -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/plugins/aws/CHANGELOG.md +5 -1
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +2 -2
- package/dart-src/plugins/cloudflare/CHANGELOG.md +5 -1
- package/dart-src/plugins/plugin-core/CHANGELOG.md +5 -1
- 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/pocketbase/CHANGELOG.md +5 -1
- package/dart-src/plugins/pocketbase/lib/flutter_ota_kit_pocketbase.dart +2 -1
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_bundle_mapper.dart +10 -2
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_client.dart +617 -4
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_database.dart +262 -42
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_storage.dart +9 -2
- package/dart-src/plugins/postgres/CHANGELOG.md +5 -1
- package/dart-src/plugins/supabase/CHANGELOG.md +6 -1
- package/dart-src/plugins/supabase/lib/src/supabase_database.dart +4 -0
- package/package.json +2 -2
- package/bin/flutter-ota-linux-x64 +0 -0
- package/bin/flutter-ota.js +0 -36
- package/dart-src/packages/cli-tools/.dart_tool/package_config.json +0 -458
- package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +0 -707
- package/dart-src/packages/cli-tools/pubspec.lock +0 -590
|
@@ -2,6 +2,7 @@ import 'dart:convert' show utf8;
|
|
|
2
2
|
import 'dart:typed_data' show Uint8List;
|
|
3
3
|
|
|
4
4
|
import 'package:crypto/crypto.dart' show Hmac, sha256;
|
|
5
|
+
import 'package:flutter/foundation.dart' show debugPrint;
|
|
5
6
|
import 'package:http/http.dart' show Client, Request, Response;
|
|
6
7
|
|
|
7
8
|
const String _cloudfrontService = 'cloudfront';
|
|
@@ -91,8 +92,7 @@ class AwsCloudFrontClient implements AwsCloudFrontClientLike {
|
|
|
91
92
|
if (shouldWait) rethrow;
|
|
92
93
|
// When not waiting, surface invalidation failures as warnings and
|
|
93
94
|
// continue (mirrors hot-updater's s3Database behaviour).
|
|
94
|
-
|
|
95
|
-
print(
|
|
95
|
+
debugPrint(
|
|
96
96
|
'[flutter_ota_kit/aws] CloudFront invalidation failed for '
|
|
97
97
|
'$distributionId; continuing without cache invalidation: $error',
|
|
98
98
|
);
|
|
@@ -5,7 +5,10 @@ import 'legacy_asset_storage_layout.dart'
|
|
|
5
5
|
/// The two supported asset storage layout strategies.
|
|
6
6
|
typedef AssetStorageLayout = String;
|
|
7
7
|
|
|
8
|
+
/// Content-addressed layout: assets stored by SHA-256 hash.
|
|
8
9
|
const String assetLayoutContentAddressed = 'content-addressed';
|
|
10
|
+
|
|
11
|
+
/// Legacy layout: assets stored by original file path.
|
|
9
12
|
const String assetLayoutLegacyFiles = 'legacy-files';
|
|
10
13
|
|
|
11
14
|
/// Whether [assetPath] is a brotli-compressed manifest path
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
/// Parsed components of a storage URI.
|
|
2
|
+
///
|
|
3
|
+
/// Storage URIs follow a protocol-specific scheme that maps to a backend
|
|
4
|
+
/// storage service. Supported protocols:
|
|
5
|
+
///
|
|
6
|
+
/// - `s3://bucket/key` — AWS S3
|
|
7
|
+
/// - `r2://bucket/key` — Cloudflare R2
|
|
8
|
+
/// - `supabase-storage://bucket/key` — Supabase Storage
|
|
9
|
+
/// - `postgres://host/db` — Postgres (via HTTP API)
|
|
10
|
+
/// - `pocketbase://host/api/files/bucket/key` — PocketBase
|
|
11
|
+
///
|
|
12
|
+
/// The [bucket] is the storage container, and [key] is the object path
|
|
13
|
+
/// within that container.
|
|
2
14
|
class ParsedStorageUri {
|
|
3
15
|
const ParsedStorageUri({
|
|
4
16
|
required this.protocol,
|
|
@@ -6,8 +18,13 @@ class ParsedStorageUri {
|
|
|
6
18
|
required this.key,
|
|
7
19
|
});
|
|
8
20
|
|
|
21
|
+
/// Protocol scheme (e.g. `s3`, `r2`, `supabase-storage`).
|
|
9
22
|
final String protocol;
|
|
23
|
+
|
|
24
|
+
/// Storage container / bucket name.
|
|
10
25
|
final String bucket;
|
|
26
|
+
|
|
27
|
+
/// Object path within the bucket (percent-decoded).
|
|
11
28
|
final String key;
|
|
12
29
|
}
|
|
13
30
|
|
|
@@ -128,36 +128,65 @@ class DatabaseBundleQueryOptions {
|
|
|
128
128
|
// Database plugin
|
|
129
129
|
// ---------------------------------------------------------------------------
|
|
130
130
|
|
|
131
|
+
/// Database abstraction for bundle storage and querying.
|
|
132
|
+
///
|
|
133
|
+
/// Implementations handle the backend-specific SQL/API calls while the
|
|
134
|
+
/// plugin-core layer manages pagination, unit-of-work tracking, and
|
|
135
|
+
/// hook callbacks.
|
|
136
|
+
///
|
|
137
|
+
/// Lifecycle:
|
|
138
|
+
/// 1. Plugin is created via [createDatabasePlugin] factory.
|
|
139
|
+
/// 2. Methods are called by the CLI (deploy, migrate, bundle management)
|
|
140
|
+
/// or the console (bundle listing, editing).
|
|
141
|
+
/// 3. [onUnmount] is called when the plugin is disposed.
|
|
131
142
|
abstract class DatabasePlugin {
|
|
143
|
+
/// Human-readable plugin name (e.g. `supabaseDatabase`).
|
|
132
144
|
String get name;
|
|
133
145
|
|
|
146
|
+
/// List all distinct channel values across all bundles.
|
|
134
147
|
Future<List<String>> getChannels();
|
|
135
148
|
|
|
149
|
+
/// Fetch a single bundle by its UUID. Returns null if not found.
|
|
136
150
|
Future<Bundle?> getBundleById(String bundleId);
|
|
137
151
|
|
|
152
|
+
/// Resolve the update decision for a device's update-check request.
|
|
153
|
+
///
|
|
154
|
+
/// Returns the eligible [UpdateInfo] (update or rollback), or null if
|
|
155
|
+
/// the device is already on the latest bundle.
|
|
138
156
|
Future<UpdateInfo?> getUpdateInfo(GetBundlesArgs args);
|
|
139
157
|
|
|
158
|
+
/// Query bundles with filtering, pagination, and ordering.
|
|
140
159
|
Future<Paginated<List<Bundle>>> getBundles(
|
|
141
160
|
DatabaseBundleQueryOptions options,
|
|
142
161
|
);
|
|
143
162
|
|
|
163
|
+
/// Update specific fields of an existing bundle.
|
|
164
|
+
///
|
|
165
|
+
/// [newBundle] contains only the changed fields (partial update).
|
|
144
166
|
Future<void> updateBundle(
|
|
145
167
|
String targetBundleId,
|
|
146
168
|
Map<String, Object?> newBundle,
|
|
147
169
|
);
|
|
148
170
|
|
|
171
|
+
/// Insert a new bundle. The bundle is staged in the unit-of-work
|
|
172
|
+
/// until [commitBundle] is called.
|
|
149
173
|
Future<void> appendBundle(Bundle insertBundle);
|
|
150
174
|
|
|
175
|
+
/// Flush all staged changes (inserts, updates, deletes) to the backend.
|
|
151
176
|
Future<void> commitBundle();
|
|
152
177
|
|
|
178
|
+
/// Delete a bundle and its associated storage artifacts.
|
|
153
179
|
Future<void> deleteBundle(Bundle deleteBundle);
|
|
154
180
|
|
|
181
|
+
/// Release resources (close connections, cancel timers).
|
|
155
182
|
Future<void> onUnmount();
|
|
156
183
|
}
|
|
157
184
|
|
|
185
|
+
/// Optional lifecycle hooks for database plugin events.
|
|
158
186
|
class DatabasePluginHooks {
|
|
159
187
|
const DatabasePluginHooks({this.onDatabaseUpdated});
|
|
160
188
|
|
|
189
|
+
/// Called after a bundle mutation is committed to the database.
|
|
161
190
|
final Future<void> Function()? onDatabaseUpdated;
|
|
162
191
|
}
|
|
163
192
|
|
|
@@ -209,25 +238,51 @@ abstract class RuntimeStorageProfile {
|
|
|
209
238
|
Future<String?> readText(String storageUri);
|
|
210
239
|
}
|
|
211
240
|
|
|
241
|
+
/// Profiles for accessing storage in different contexts.
|
|
242
|
+
///
|
|
243
|
+
/// A storage plugin may support one or both profiles:
|
|
244
|
+
/// - [node]: For CLI/deploy operations (file upload, delete, list).
|
|
245
|
+
/// - [runtime]: For device update-check operations (signed URLs, manifest read).
|
|
212
246
|
class StoragePluginProfiles {
|
|
213
247
|
const StoragePluginProfiles({this.node, this.runtime});
|
|
214
248
|
|
|
249
|
+
/// Node profile for CLI/deploy operations. Null if not supported.
|
|
215
250
|
final NodeStorageProfile? node;
|
|
251
|
+
|
|
252
|
+
/// Runtime profile for device update-checks. Null if not supported.
|
|
216
253
|
final RuntimeStorageProfile? runtime;
|
|
217
254
|
|
|
255
|
+
/// Whether this plugin supports CLI/deploy operations.
|
|
218
256
|
bool get hasNode => node != null;
|
|
257
|
+
|
|
258
|
+
/// Whether this plugin supports device update-check operations.
|
|
219
259
|
bool get hasRuntime => runtime != null;
|
|
220
260
|
}
|
|
221
261
|
|
|
262
|
+
/// Storage backend abstraction for artifact upload/download.
|
|
263
|
+
///
|
|
264
|
+
/// Each backend (S3, R2, Supabase Storage, PocketBase) implements this
|
|
265
|
+
/// interface to provide a unified storage API.
|
|
266
|
+
///
|
|
267
|
+
/// Storage URIs follow the scheme defined in [parseStorageUri]:
|
|
268
|
+
/// `protocol://bucket/key` where protocol is `s3`, `r2`, `supabase-storage`,
|
|
269
|
+
/// or `pocketbase`.
|
|
222
270
|
abstract class StoragePlugin {
|
|
271
|
+
/// Human-readable plugin name (e.g. `s3Storage`, `r2Storage`).
|
|
223
272
|
String get name;
|
|
273
|
+
|
|
274
|
+
/// Protocol scheme this plugin handles (e.g. `s3`, `r2`, `supabase-storage`).
|
|
224
275
|
String get supportedProtocol;
|
|
276
|
+
|
|
277
|
+
/// Access profiles for node (CLI) and runtime (device) operations.
|
|
225
278
|
StoragePluginProfiles get profiles;
|
|
226
279
|
}
|
|
227
280
|
|
|
281
|
+
/// Optional lifecycle hooks for storage plugin events.
|
|
228
282
|
class StoragePluginHooks {
|
|
229
283
|
const StoragePluginHooks({this.onStorageUploaded});
|
|
230
284
|
|
|
285
|
+
/// Called after a file is successfully uploaded to storage.
|
|
231
286
|
final Future<void> Function()? onStorageUploaded;
|
|
232
287
|
}
|
|
233
288
|
|
|
@@ -235,6 +290,7 @@ class StoragePluginHooks {
|
|
|
235
290
|
// Build plugin
|
|
236
291
|
// ---------------------------------------------------------------------------
|
|
237
292
|
|
|
293
|
+
/// Result of a successful build operation.
|
|
238
294
|
class BuildPluginResult {
|
|
239
295
|
const BuildPluginResult({
|
|
240
296
|
required this.buildPath,
|
|
@@ -242,17 +298,32 @@ class BuildPluginResult {
|
|
|
242
298
|
this.stdout,
|
|
243
299
|
});
|
|
244
300
|
|
|
301
|
+
/// Path to the built artifact (e.g. `build/app/outputs/flutter-apk/app-release.apk`).
|
|
245
302
|
final String buildPath;
|
|
303
|
+
|
|
304
|
+
/// Generated bundle UUID for this build.
|
|
246
305
|
final String bundleId;
|
|
306
|
+
|
|
307
|
+
/// Raw build output (stdout + stderr combined).
|
|
247
308
|
final String? stdout;
|
|
248
309
|
}
|
|
249
310
|
|
|
311
|
+
/// Build system abstraction for creating app artifacts.
|
|
312
|
+
///
|
|
313
|
+
/// Implementations handle the platform-specific build commands (Gradle,
|
|
314
|
+
/// Xcodebuild) while the plugin-core layer manages versioning and artifact
|
|
315
|
+
/// packaging.
|
|
250
316
|
abstract class BuildPlugin {
|
|
317
|
+
/// Human-readable plugin name (e.g. `androidBuild`, `iosBuild`).
|
|
251
318
|
String get name;
|
|
252
319
|
|
|
320
|
+
/// Execute the build for the given [platform] and return the artifact path.
|
|
253
321
|
Future<BuildPluginResult> build(Platform platform);
|
|
254
322
|
|
|
323
|
+
/// Pre-build hook (e.g. clean, dependency resolution).
|
|
255
324
|
Future<void> prebuild(Platform platform);
|
|
325
|
+
|
|
326
|
+
/// Post-build hook (e.g. signing, obfuscation).
|
|
256
327
|
Future<void> postbuild(Platform platform);
|
|
257
328
|
}
|
|
258
329
|
|
|
@@ -341,14 +412,14 @@ class NativeBuildArgs {
|
|
|
341
412
|
class SigningConfigDisabled {
|
|
342
413
|
const SigningConfigDisabled({this.privateKeyPath});
|
|
343
414
|
|
|
344
|
-
|
|
415
|
+
static const bool enabled = false;
|
|
345
416
|
final String? privateKeyPath;
|
|
346
417
|
}
|
|
347
418
|
|
|
348
419
|
class SigningConfigEnabled {
|
|
349
420
|
const SigningConfigEnabled({required this.privateKeyPath});
|
|
350
421
|
|
|
351
|
-
|
|
422
|
+
static const bool enabled = true;
|
|
352
423
|
final String privateKeyPath;
|
|
353
424
|
}
|
|
354
425
|
|
|
@@ -450,6 +521,26 @@ class NativeBuildOptions {
|
|
|
450
521
|
// ---------------------------------------------------------------------------
|
|
451
522
|
|
|
452
523
|
/// Opaque context object carried through a request lifecycle.
|
|
453
|
-
///
|
|
524
|
+
///
|
|
525
|
+
/// Plugins may attach metadata to this map during processing. Known keys:
|
|
526
|
+
///
|
|
527
|
+
/// - `bundleId` (String) — Current bundle ID being processed
|
|
528
|
+
/// - `channel` (String) — Target release channel
|
|
529
|
+
/// - `platform` (String) — Target platform (`ios`, `android`)
|
|
530
|
+
/// - `appVersion` (String) — Current app version (semver)
|
|
531
|
+
/// - `fingerprintHash` (String) — Device fingerprint hash
|
|
532
|
+
/// - `cohort` (String) — Rollout cohort identifier
|
|
533
|
+
/// - `storageUri` (String) — Resolved storage URI for the artifact
|
|
534
|
+
/// - `signedUrl` (String) — Pre-signed download URL
|
|
535
|
+
///
|
|
536
|
+
/// Extensions should use namespaced keys (e.g. `myPlugin.fieldName`) to
|
|
537
|
+
/// avoid collisions.
|
|
454
538
|
typedef HotUpdaterContext = Map<String, Object?>;
|
|
539
|
+
|
|
540
|
+
/// Context for resolving storage URIs to download URLs.
|
|
541
|
+
///
|
|
542
|
+
/// Inherits all keys from [HotUpdaterContext] and adds:
|
|
543
|
+
///
|
|
544
|
+
/// - `storageProtocol` (String) — Expected protocol (`s3`, `r2`, `supabase-storage`, etc.)
|
|
545
|
+
/// - `storageBucket` (String) — Target bucket name
|
|
455
546
|
typedef StorageResolveContext = HotUpdaterContext;
|
|
@@ -16,7 +16,8 @@ export 'src/pocketbase_client.dart'
|
|
|
16
16
|
PocketBaseClient,
|
|
17
17
|
PocketBaseClientFactory,
|
|
18
18
|
PocketBaseException,
|
|
19
|
-
PocketBaseList
|
|
19
|
+
PocketBaseList,
|
|
20
|
+
PocketBaseBackup;
|
|
20
21
|
export 'src/pocketbase_database.dart'
|
|
21
22
|
show pocketbaseDatabase, PocketBaseDatabaseConfig;
|
|
22
23
|
export 'src/pocketbase_storage.dart'
|
|
@@ -96,8 +96,16 @@ class PocketBaseBundleRow {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/// Convert a [PocketBaseBundleRow] into a [Bundle].
|
|
99
|
-
|
|
99
|
+
///
|
|
100
|
+
/// If [patches] is provided, they are attached as [BundlePatchArtifact]s.
|
|
101
|
+
Bundle mapRowToBundle(
|
|
102
|
+
PocketBaseBundleRow row, {
|
|
103
|
+
List<Map<String, dynamic>>? patches,
|
|
104
|
+
}) {
|
|
100
105
|
final rawMetadata = normalizeMetadata(row.metadata);
|
|
106
|
+
final patchArtifacts = patches
|
|
107
|
+
?.map((p) => BundlePatchArtifact.fromJson(p.cast<String, dynamic>()))
|
|
108
|
+
.toList();
|
|
101
109
|
return Bundle(
|
|
102
110
|
id: row.id,
|
|
103
111
|
channel: row.channel,
|
|
@@ -114,7 +122,7 @@ Bundle mapRowToBundle(PocketBaseBundleRow row) {
|
|
|
114
122
|
manifestStorageUri: row.manifestStorageUri,
|
|
115
123
|
manifestFileHash: row.manifestFileHash,
|
|
116
124
|
assetBaseStorageUri: row.assetBaseStorageUri,
|
|
117
|
-
patches:
|
|
125
|
+
patches: patchArtifacts,
|
|
118
126
|
rolloutCohortCount: row.rolloutCohortCount,
|
|
119
127
|
targetCohorts: row.targetCohorts,
|
|
120
128
|
);
|