@_nazmiforreal/flutter-ota 0.1.26 → 0.1.28
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/README.md +112 -18
- package/bin/flutter-ota-linux-x64 +0 -0
- package/bin/flutter-ota.js +12 -11
- package/dart-src/packages/cli-tools/README.md +146 -2
- package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +46 -13
- package/dart-src/packages/cli-tools/lib/src/backend.dart +6 -6
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +2 -0
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +7 -7
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +4 -1
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +36 -8
- package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +1 -1
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +1 -1
- package/dart-src/packages/cli-tools/lib/src/config.dart +5 -5
- package/dart-src/packages/cli-tools/lib/src/operations.dart +2 -2
- package/dart-src/packages/cli-tools/pubspec.yaml +8 -21
- package/dart-src/packages/cli-tools/test/backends_test.dart +3 -3
- package/dart-src/packages/cli-tools/test/cli_test.dart +1 -1
- package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +2 -2
- package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +2 -2
- package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +4 -1
- package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +2 -2
- package/dart-src/packages/cli-tools/test/pocketbase_smoke_test.dart +1 -1
- package/dart-src/root/README.md +334 -0
- package/dart-src/root/analysis_options.yaml +21 -0
- package/dart-src/root/android/build.gradle +116 -0
- package/dart-src/root/android/consumer-rules.pro +23 -0
- package/dart-src/root/android/settings.gradle +1 -0
- package/dart-src/root/android/src/main/AndroidManifest.xml +21 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/ApplyResult.kt +52 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BlacklistStore.kt +154 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BootDiagnosticStore.kt +150 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/CrashGuard.kt +271 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherApplication.kt +201 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherAutoInitProvider.kt +93 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherPlugin.kt +245 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/LoaderHook.kt +298 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatchManager.kt +1579 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfig.kt +173 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/SignatureVerifier.kt +177 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/CrashGuardTest.kt +61 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/LoaderHookTest.kt +25 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatchManagerTest.kt +401 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfigTest.kt +30 -0
- package/dart-src/root/bin/pack.dart +182 -0
- package/dart-src/root/lib/flutter_ota_kit.dart +1170 -0
- package/dart-src/root/lib/src/app_version_resolver.dart +36 -0
- package/dart-src/root/lib/src/aws_update_source.dart +97 -0
- package/dart-src/root/lib/src/blacklist.dart +54 -0
- package/dart-src/root/lib/src/boot_diagnostic.dart +138 -0
- package/dart-src/root/lib/src/cloudflare_update_source.dart +97 -0
- package/dart-src/root/lib/src/flutter_ota_app.dart +69 -0
- package/dart-src/root/lib/src/io.dart +48 -0
- package/dart-src/root/lib/src/io_stub.dart +19 -0
- package/dart-src/root/lib/src/ota_progress_overlay.dart +1186 -0
- package/dart-src/root/lib/src/pack/pack.dart +434 -0
- package/dart-src/root/lib/src/patch_info.dart +12 -0
- package/dart-src/root/lib/src/patcher_channel.dart +160 -0
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_database.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_s3_client.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage_profile.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/with_cloudfront_signed_url.dart +1 -1
- package/dart-src/root/lib/src/pkg/client/flutter_ota_kit_client.dart +1 -0
- package/dart-src/root/lib/src/pkg/client/src/server_update_source.dart +46 -0
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/cloudflare_worker_database.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_build_where.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_bundle_mapper.dart +3 -3
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database_plugin.dart +2 -2
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_s3_client.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage_profile.dart +1 -1
- package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/status.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/flutter_ota_kit_plugin_core.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_blob_database_plugin.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/filter_compatible_app_versions.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/get_update_info.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/request_update_bundle_state.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/resolve_update_info_from_bundles.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/types.dart +2 -2
- package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_bundle_mapper.dart +2 -2
- package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_database.dart +26 -15
- package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_storage.dart +1 -1
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_bundle_mapper.dart +2 -2
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_database.dart +2 -2
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_get_update_info.dart +2 -2
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_storage.dart +1 -1
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_types.dart +1 -1
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_bundle_mapper.dart +2 -2
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_database.dart +2 -2
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_database.dart +1 -1
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_storage.dart +1 -1
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_storage.dart +1 -1
- package/dart-src/root/lib/src/pocketbase_update_source.dart +107 -0
- package/dart-src/root/lib/src/postgres_update_source.dart +95 -0
- package/dart-src/root/lib/src/shared_update_check.dart +131 -0
- package/dart-src/root/lib/src/supabase_update_source.dart +93 -0
- package/dart-src/root/pubspec.yaml +54 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +17 -13
- package/scripts/vendor.sh +22 -7
- package/dart-src/packages/core/CHANGELOG.md +0 -21
- package/dart-src/packages/core/README.md +0 -23
- package/dart-src/packages/core/example/lib/main.dart +0 -69
- package/dart-src/packages/core/pubspec.yaml +0 -13
- package/dart-src/packages/core/test/rollout_test.dart +0 -135
- package/dart-src/packages/core/test/semver_test.dart +0 -55
- package/dart-src/packages/core/test/semver_vectors.json +0 -1
- package/dart-src/packages/core/test/types_test.dart +0 -131
- package/dart-src/packages/core/test/uuid_test.dart +0 -40
- package/dart-src/packages/core/tool/dbg.dart +0 -7
- package/dart-src/plugins/aws/.gitkeep +0 -0
- package/dart-src/plugins/aws/CHANGELOG.md +0 -23
- package/dart-src/plugins/aws/LICENSE +0 -21
- package/dart-src/plugins/aws/README.md +0 -28
- package/dart-src/plugins/aws/example/lib/main.dart +0 -60
- package/dart-src/plugins/aws/pubspec.yaml +0 -30
- package/dart-src/plugins/aws/test/aws_database_test.dart +0 -161
- package/dart-src/plugins/aws/test/aws_storage_test.dart +0 -73
- package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +0 -78
- package/dart-src/plugins/cloudflare/.gitkeep +0 -0
- package/dart-src/plugins/cloudflare/CHANGELOG.md +0 -23
- package/dart-src/plugins/cloudflare/LICENSE +0 -21
- package/dart-src/plugins/cloudflare/README.md +0 -25
- package/dart-src/plugins/cloudflare/example/lib/main.dart +0 -62
- package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +0 -16
- package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +0 -8
- package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +0 -38
- package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +0 -11
- package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +0 -24
- package/dart-src/plugins/cloudflare/pubspec.yaml +0 -28
- package/dart-src/plugins/cloudflare/sql/bundles.sql +0 -42
- package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +0 -126
- package/dart-src/plugins/cloudflare/test/d1_bundle_mapper_test.dart +0 -106
- package/dart-src/plugins/cloudflare/test/d1_database_test.dart +0 -200
- package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +0 -261
- package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +0 -78
- package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +0 -74
- package/dart-src/plugins/plugin-core/CHANGELOG.md +0 -20
- package/dart-src/plugins/plugin-core/LICENSE +0 -21
- package/dart-src/plugins/plugin-core/README.md +0 -21
- package/dart-src/plugins/plugin-core/example/lib/main.dart +0 -61
- package/dart-src/plugins/plugin-core/pubspec.yaml +0 -23
- package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +0 -170
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_store_test.dart +0 -74
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +0 -345
- package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +0 -843
- package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +0 -298
- package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +0 -195
- package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +0 -56
- package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +0 -1589
- package/dart-src/plugins/plugin-core/test/helpers_test.dart +0 -283
- package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +0 -202
- package/dart-src/plugins/plugin-core/test/request_update_bundle_state_test.dart +0 -86
- package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +0 -88
- package/dart-src/plugins/pocketbase/CHANGELOG.md +0 -23
- package/dart-src/plugins/pocketbase/LICENSE +0 -21
- package/dart-src/plugins/pocketbase/README.md +0 -54
- package/dart-src/plugins/pocketbase/example/lib/main.dart +0 -62
- package/dart-src/plugins/pocketbase/pubspec.yaml +0 -26
- package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +0 -12
- package/dart-src/plugins/postgres/.gitkeep +0 -0
- package/dart-src/plugins/postgres/CHANGELOG.md +0 -23
- package/dart-src/plugins/postgres/LICENSE +0 -21
- package/dart-src/plugins/postgres/README.md +0 -29
- package/dart-src/plugins/postgres/example/lib/main.dart +0 -67
- package/dart-src/plugins/postgres/pubspec.yaml +0 -26
- package/dart-src/plugins/postgres/sql/bundles.sql +0 -45
- package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +0 -21
- package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +0 -171
- package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +0 -126
- package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +0 -125
- package/dart-src/plugins/postgres/sql/hash_user_id.sql +0 -30
- package/dart-src/plugins/postgres/sql/is_device_eligible.sql +0 -237
- package/dart-src/plugins/postgres/sql/prepareSql.ts +0 -11
- package/dart-src/plugins/postgres/test/mock_postgres_client.dart +0 -320
- package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +0 -139
- package/dart-src/plugins/postgres/test/postgres_database_test.dart +0 -210
- package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +0 -60
- package/dart-src/plugins/supabase/CHANGELOG.md +0 -24
- package/dart-src/plugins/supabase/LICENSE +0 -21
- package/dart-src/plugins/supabase/README.md +0 -53
- package/dart-src/plugins/supabase/example/lib/main.dart +0 -63
- package/dart-src/plugins/supabase/pubspec.yaml +0 -27
- package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +0 -44
- package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +0 -968
- package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +0 -232
- package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +0 -137
- package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +0 -206
- package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +0 -186
- package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +0 -505
- package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +0 -50
- package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +0 -20
- package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +0 -75
- package/dart-src/plugins/supabase/test/mock_supabase_client.dart +0 -654
- package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +0 -115
- package/dart-src/plugins/supabase/test/supabase_database_test.dart +0 -198
- package/dart-src/plugins/supabase/test/supabase_storage_test.dart +0 -168
- /package/dart-src/{packages/core → root}/LICENSE +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/flutter_ota_kit_aws.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_client.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_signer.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_config.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_ssm_client.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/flutter_ota_kit_cloudflare.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_client.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_config.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_config.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/flutter_ota_kit_core.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/app_update_info.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_artifacts.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_patch_artifact.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/changed_asset.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/get_bundles_args.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/metadata.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/patch_info.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/platform.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/rollout.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_range.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_version.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/strategy.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/update_bundle_params.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/uuid.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/asset_storage_layout.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_helpers.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_storage_layout.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work_store.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/calculate_pagination.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/compression_format.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/content_addressed_assets.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_database_plugin.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_key_builder.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_plugin.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/generate_min_bundle_id.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/is_object.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/legacy_asset_storage_layout.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/paginate_bundles.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/parse_storage_uri.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/query_bundles.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_helpers.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_profile.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/uuidv7.dart +0 -0
- /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/flutter_ota_kit_pocketbase.dart +0 -0
- /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_client.dart +0 -0
- /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_config.dart +0 -0
- /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/flutter_ota_kit_postgres.dart +0 -0
- /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_client.dart +0 -0
- /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_config.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/edge.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/flutter_ota_kit_supabase.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/error_message.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_adapter.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_http.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_config.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_signed_url_batcher.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/types.dart +0 -0
|
@@ -314,24 +314,52 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
314
314
|
warn('pubspec.yaml not found; skipping dependency injection.');
|
|
315
315
|
return;
|
|
316
316
|
}
|
|
317
|
-
|
|
317
|
+
final content = pubspec.readAsStringSync();
|
|
318
318
|
if (content.contains(RegExp(r'^\s*flutter_ota_kit\s*:', multiLine: true))) {
|
|
319
319
|
step('pubspec.yaml already references flutter_ota_kit');
|
|
320
320
|
return;
|
|
321
321
|
}
|
|
322
|
-
|
|
322
|
+
// Prefer `flutter pub add`, which resolves the LATEST published version and
|
|
323
|
+
// writes a proper caret constraint — so the CLI never hardcodes (and never
|
|
324
|
+
// goes stale on) a version number.
|
|
325
|
+
final exe = _flutterOrDart();
|
|
326
|
+
if (exe != null) {
|
|
327
|
+
final res = Process.runSync(exe, ['pub', 'add', 'flutter_ota_kit']);
|
|
328
|
+
if (res.exitCode == 0) {
|
|
329
|
+
step('Added `flutter_ota_kit` (latest) via `$exe pub add`');
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
warn('`$exe pub add flutter_ota_kit` failed; falling back to a manual '
|
|
333
|
+
'edit. Run it yourself if the constraint looks wrong.');
|
|
334
|
+
}
|
|
335
|
+
// Fallback: append an unpinned dependency so `pub get` picks the latest
|
|
336
|
+
// compatible release. Still no hardcoded version.
|
|
323
337
|
final marker = '\ndependencies:';
|
|
324
338
|
final idx = content.indexOf(marker);
|
|
325
339
|
if (idx == -1) {
|
|
326
|
-
warn(
|
|
327
|
-
|
|
328
|
-
);
|
|
340
|
+
warn('Could not find a `dependencies:` block; add `flutter_ota_kit` '
|
|
341
|
+
'manually (run `flutter pub add flutter_ota_kit`).');
|
|
329
342
|
return;
|
|
330
343
|
}
|
|
331
344
|
final insertAt = idx + marker.length;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
345
|
+
final updated =
|
|
346
|
+
content.replaceRange(insertAt, insertAt, '\n flutter_ota_kit: any\n');
|
|
347
|
+
pubspec.writeAsStringSync(updated);
|
|
348
|
+
step('Added `flutter_ota_kit` dependency to pubspec.yaml '
|
|
349
|
+
'(run `flutter pub get` to resolve the latest version)');
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/// Resolve the `flutter` executable (preferred) or `dart` for `pub add`.
|
|
353
|
+
String? _flutterOrDart() {
|
|
354
|
+
for (final exe in const ['flutter', 'dart']) {
|
|
355
|
+
try {
|
|
356
|
+
final r = Process.runSync(exe, ['--version']);
|
|
357
|
+
if (r.exitCode == 0) return exe;
|
|
358
|
+
} catch (_) {
|
|
359
|
+
// not on PATH; try next
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return null;
|
|
335
363
|
}
|
|
336
364
|
|
|
337
365
|
void _addInternetPermission() {
|
|
@@ -6,7 +6,7 @@ import 'dart:convert';
|
|
|
6
6
|
import 'dart:io';
|
|
7
7
|
|
|
8
8
|
import 'package:args/args.dart';
|
|
9
|
-
import 'package:
|
|
9
|
+
import 'package:flutter_ota_kit/src/pkg/pocketbase/flutter_ota_kit_pocketbase.dart';
|
|
10
10
|
import 'package:path/path.dart' as p;
|
|
11
11
|
|
|
12
12
|
import '../cli_base.dart';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import 'dart:convert';
|
|
2
2
|
import 'dart:io';
|
|
3
3
|
|
|
4
|
-
import 'package:
|
|
4
|
+
import 'package:flutter_ota_kit/src/pkg/aws/flutter_ota_kit_aws.dart';
|
|
5
5
|
import 'package:postgres/postgres.dart';
|
|
6
|
-
import 'package:
|
|
7
|
-
import 'package:
|
|
8
|
-
import 'package:
|
|
6
|
+
import 'package:flutter_ota_kit/src/pkg/cloudflare/flutter_ota_kit_cloudflare.dart';
|
|
7
|
+
import 'package:flutter_ota_kit/src/pkg/pocketbase/flutter_ota_kit_pocketbase.dart';
|
|
8
|
+
import 'package:flutter_ota_kit/src/pkg/postgres/flutter_ota_kit_postgres.dart';
|
|
9
9
|
|
|
10
|
-
import 'package:
|
|
10
|
+
import 'package:flutter_ota_kit/src/pkg/supabase/flutter_ota_kit_supabase.dart';
|
|
11
11
|
import 'package:path/path.dart' as p;
|
|
12
12
|
|
|
13
13
|
/// Resolved configuration for the flutter_ota_kit CLI.
|
|
@@ -2,8 +2,8 @@ import 'dart:convert';
|
|
|
2
2
|
import 'dart:io';
|
|
3
3
|
|
|
4
4
|
import 'package:crypto/crypto.dart';
|
|
5
|
-
import 'package:
|
|
6
|
-
import 'package:
|
|
5
|
+
import 'package:flutter_ota_kit/src/pkg/core/flutter_ota_kit_core.dart';
|
|
6
|
+
import 'package:flutter_ota_kit/src/pkg/plugin_core/flutter_ota_kit_plugin_core.dart';
|
|
7
7
|
|
|
8
8
|
import 'backend.dart';
|
|
9
9
|
import 'sign.dart';
|
|
@@ -8,6 +8,7 @@ publish_to: none
|
|
|
8
8
|
|
|
9
9
|
environment:
|
|
10
10
|
sdk: '>=3.13.2 <4.0.0'
|
|
11
|
+
flutter: '>=3.47.2'
|
|
11
12
|
|
|
12
13
|
dependencies:
|
|
13
14
|
args: '>=2.4.0 <3.0.0'
|
|
@@ -17,13 +18,11 @@ dependencies:
|
|
|
17
18
|
collection: ^1.18.0
|
|
18
19
|
cryptography: ^2.7.0
|
|
19
20
|
postgres: ^3.4.0
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
flutter_ota_kit_aws: ^0.1.7
|
|
26
|
-
flutter_ota_kit_pocketbase: ^0.1.4
|
|
21
|
+
# All former sub-packages (core, plugin-core, backends) now live inside the
|
|
22
|
+
# merged `flutter_ota_kit` package under lib/src/pkg/. The CLI depends on the
|
|
23
|
+
# single package instead of eight. NOTE: `flutter_ota_kit` is a Flutter plugin,
|
|
24
|
+
# so building the CLI now requires the Flutter SDK (not just the Dart SDK).
|
|
25
|
+
flutter_ota_kit: ^0.2.1
|
|
27
26
|
http: ^1.2.0
|
|
28
27
|
shelf: ^1.4.0
|
|
29
28
|
|
|
@@ -31,17 +30,5 @@ dev_dependencies:
|
|
|
31
30
|
test: ^1.25.0
|
|
32
31
|
|
|
33
32
|
dependency_overrides:
|
|
34
|
-
|
|
35
|
-
path:
|
|
36
|
-
flutter_ota_kit_plugin_core:
|
|
37
|
-
path: ../../plugins/plugin-core
|
|
38
|
-
flutter_ota_kit_supabase:
|
|
39
|
-
path: ../../plugins/supabase
|
|
40
|
-
flutter_ota_kit_postgres:
|
|
41
|
-
path: ../../plugins/postgres
|
|
42
|
-
flutter_ota_kit_cloudflare:
|
|
43
|
-
path: ../../plugins/cloudflare
|
|
44
|
-
flutter_ota_kit_aws:
|
|
45
|
-
path: ../../plugins/aws
|
|
46
|
-
flutter_ota_kit_pocketbase:
|
|
47
|
-
path: ../../plugins/pocketbase
|
|
33
|
+
flutter_ota_kit:
|
|
34
|
+
path: ../../root
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import 'dart:io';
|
|
2
2
|
|
|
3
|
-
import 'package:
|
|
3
|
+
import 'package:flutter_ota_kit/src/pkg/aws/flutter_ota_kit_aws.dart';
|
|
4
4
|
import 'package:flutter_ota_kit_cli/flutter_ota_kit_cli.dart';
|
|
5
|
-
import 'package:
|
|
6
|
-
import 'package:
|
|
5
|
+
import 'package:flutter_ota_kit/src/pkg/cloudflare/flutter_ota_kit_cloudflare.dart';
|
|
6
|
+
import 'package:flutter_ota_kit/src/pkg/plugin_core/flutter_ota_kit_plugin_core.dart';
|
|
7
7
|
import 'package:test/test.dart';
|
|
8
8
|
|
|
9
9
|
import 'mocks/mock_aws_s3_client.dart' as aws;
|
|
@@ -2,7 +2,7 @@ import 'dart:io';
|
|
|
2
2
|
import 'dart:typed_data';
|
|
3
3
|
|
|
4
4
|
import 'package:flutter_ota_kit_cli/flutter_ota_kit_cli.dart';
|
|
5
|
-
import 'package:
|
|
5
|
+
import 'package:flutter_ota_kit/src/pkg/supabase/src/supabase_client_adapter.dart';
|
|
6
6
|
import 'package:path/path.dart' as p;
|
|
7
7
|
import 'package:test/test.dart';
|
|
8
8
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import 'dart:convert' show utf8;
|
|
2
2
|
|
|
3
|
-
import 'package:
|
|
3
|
+
import 'package:flutter_ota_kit/src/pkg/plugin_core/flutter_ota_kit_plugin_core.dart'
|
|
4
4
|
show StorageObject;
|
|
5
5
|
|
|
6
|
-
import 'package:
|
|
6
|
+
import 'package:flutter_ota_kit/src/pkg/aws/flutter_ota_kit_aws.dart'
|
|
7
7
|
show AwsS3ClientLike, AwsS3StorageConfig;
|
|
8
8
|
|
|
9
9
|
/// In-memory store backing [MockAwsS3Client].
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import 'dart:convert' show jsonDecode;
|
|
2
2
|
|
|
3
|
-
import 'package:
|
|
3
|
+
import 'package:flutter_ota_kit/src/pkg/cloudflare/flutter_ota_kit_cloudflare.dart'
|
|
4
4
|
show
|
|
5
5
|
CloudflareWorkerDatabaseConfig,
|
|
6
6
|
D1ClientLike,
|
|
7
7
|
D1DatabaseConfig,
|
|
8
8
|
cloudflareWorkerDatabase,
|
|
9
9
|
d1Database;
|
|
10
|
-
import 'package:
|
|
10
|
+
import 'package:flutter_ota_kit/src/pkg/plugin_core/flutter_ota_kit_plugin_core.dart'
|
|
11
11
|
show DatabasePlugin;
|
|
12
12
|
|
|
13
13
|
/// In-memory store backing [MockD1Client], mirroring the D1 tables.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/// `backends_test.dart`.
|
|
6
6
|
library;
|
|
7
7
|
|
|
8
|
-
import 'package:
|
|
8
|
+
import 'package:flutter_ota_kit/src/pkg/pocketbase/flutter_ota_kit_pocketbase.dart';
|
|
9
9
|
import 'package:http/http.dart' as http;
|
|
10
10
|
|
|
11
11
|
class MockPocketBaseClient implements PocketBaseClient {
|
|
@@ -18,6 +18,9 @@ class MockPocketBaseClient implements PocketBaseClient {
|
|
|
18
18
|
String? _email;
|
|
19
19
|
String? _password;
|
|
20
20
|
|
|
21
|
+
@override
|
|
22
|
+
String get authToken => (_email != null && _password != null) ? 'mock-token' : '';
|
|
23
|
+
|
|
21
24
|
@override
|
|
22
25
|
void adminCredentials(String email, String password) {
|
|
23
26
|
_email = email;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import 'dart:typed_data';
|
|
2
2
|
|
|
3
|
-
import 'package:
|
|
3
|
+
import 'package:flutter_ota_kit/src/pkg/plugin_core/flutter_ota_kit_plugin_core.dart';
|
|
4
4
|
|
|
5
|
-
import 'package:
|
|
5
|
+
import 'package:flutter_ota_kit/src/pkg/postgres/flutter_ota_kit_postgres.dart';
|
|
6
6
|
|
|
7
7
|
/// In-memory store backing [MockPostgresClient]. Mirrors the `bundles` and
|
|
8
8
|
/// `bundle_patches` tables (snake_case row maps).
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# flutter_ota_kit
|
|
2
|
+
|
|
3
|
+
[](https://pub.dev/packages/flutter_ota_kit)
|
|
4
|
+
[](https://flutter.dev)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
### Ship Dart & asset hotfixes over the air. Your backend. No store review. No monthly bill.
|
|
8
|
+
|
|
9
|
+
**flutter_ota_kit** is open-source code push for **Flutter Android**. Push a bug
|
|
10
|
+
fix or a new screen, and your users get it on the next cold start — no Play
|
|
11
|
+
Store round-trip, no vendor lock-in, no per-seat pricing. The patch lives on
|
|
12
|
+
**your** infrastructure (Supabase, Postgres, Cloudflare, AWS S3, PocketBase, or
|
|
13
|
+
your own CDN), signed and integrity-checked, with automatic crash rollback if
|
|
14
|
+
anything goes wrong.
|
|
15
|
+
|
|
16
|
+
If you've used [Shorebird](https://shorebird.dev/),
|
|
17
|
+
[CodePush](https://learn.microsoft.com/en-us/appcenter/distribution/codepush/),
|
|
18
|
+
or [Expo EAS Update](https://docs.expo.dev/eas-update/introduction/), this is the
|
|
19
|
+
same idea for Flutter Android — MIT-licensed, self-hosted, and yours to keep.
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
dependencies:
|
|
25
|
+
flutter_ota_kit: ^0.2.0 # one package — every backend included
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> **v0.2.0 is a single package.** Everything — the device SDK plus all five
|
|
29
|
+
> backends — now ships inside `flutter_ota_kit`. There are no more
|
|
30
|
+
> `flutter_ota_kit_supabase` / `_postgres` / `_cloudflare` / `_aws` /
|
|
31
|
+
> `_pocketbase` packages to add. One import, one dependency.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Why teams pick it
|
|
36
|
+
|
|
37
|
+
- **You own the pipeline.** Patches sit in your Supabase project, your S3
|
|
38
|
+
bucket, your Cloudflare account — not someone else's cloud. No third party
|
|
39
|
+
sees your code or your users.
|
|
40
|
+
- **Zero recurring cost.** MIT-licensed. The only bill is your own storage,
|
|
41
|
+
which for OTA payloads is pennies.
|
|
42
|
+
- **Ships in one dependency.** Add `flutter_ota_kit`, run `flutter-ota init`,
|
|
43
|
+
deploy. Five backends are built in; you configure the one you use.
|
|
44
|
+
- **Safe by default.** MD5 + Ed25519 verification, automatic crash rollback,
|
|
45
|
+
a bad-patch blacklist, and staged rollouts — all on by default.
|
|
46
|
+
- **Zero-code forced-update UI.** Opt in and the SDK renders a polished
|
|
47
|
+
full-screen install progress screen for you. Write no UI.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## How it compares
|
|
52
|
+
|
|
53
|
+
| | flutter_ota_kit | Shorebird | CodePush (React Native) |
|
|
54
|
+
|--------------------------|----------------------------------------------------------|-------------------------------|------------------------------|
|
|
55
|
+
| **Framework** | Flutter | Flutter | React Native |
|
|
56
|
+
| **Platforms** | Android | Android + iOS | Android + iOS (retired 2025) |
|
|
57
|
+
| **Hosting** | Your backend / your CDN — see [Backends](doc/backends.md) | Shorebird cloud (managed) | AppCenter cloud (deprecated) |
|
|
58
|
+
| **Update scope** | Dart AOT + Flutter assets | Dart code (engine-level diff) | JS bundle |
|
|
59
|
+
| **Takes effect** | Next cold start | Next restart | Next restart |
|
|
60
|
+
| **Cost** | Free (MIT) — you pay only your own storage | Free tier + paid plans | — |
|
|
61
|
+
| **Backend flexibility** | Supabase / Postgres / Cloudflare / AWS / PocketBase / BYO CDN | Cloud-managed only | — |
|
|
62
|
+
| **Forced updates** | Yes — built-in progress UI | Yes | Yes |
|
|
63
|
+
| **Crash rollback** | Automatic + bad-patch blacklist | Yes | Yes |
|
|
64
|
+
| **Signing** | Ed25519 (Android 13+) + MD5 | Yes | — |
|
|
65
|
+
|
|
66
|
+
**Choose Shorebird** if you need iOS today or want a fully managed cloud.
|
|
67
|
+
|
|
68
|
+
**Choose flutter_ota_kit** if you want OTA on infrastructure you control —
|
|
69
|
+
enterprise apps, regional distribution, air-gapped deployments, or non-Play
|
|
70
|
+
channels. Bring your own backend, CDN, and auth.
|
|
71
|
+
|
|
72
|
+
> Google Play and some stores restrict downloading executable code at runtime.
|
|
73
|
+
> flutter_ota_kit targets self-controlled, enterprise, or permissive
|
|
74
|
+
> distribution channels. Verify your channel's policy before shipping.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Features
|
|
79
|
+
|
|
80
|
+
- **OTA code push** — replaces the Dart AOT `libapp.so` and Flutter assets on
|
|
81
|
+
the next cold start, with MD5 + Ed25519 integrity checks.
|
|
82
|
+
- **Five backends, built in** — Supabase (fully automated), Postgres,
|
|
83
|
+
Cloudflare (D1 + R2), AWS (S3 + optional CloudFront), and **PocketBase**
|
|
84
|
+
(single-binary, self-hosted). Or point at your own CDN.
|
|
85
|
+
- **Automatic crash rollback** — a patch that fails to boot is rolled back and
|
|
86
|
+
blacklisted so it's never retried. Tunable via `maxCrashCount` /
|
|
87
|
+
`verifyAfter`. A runtime crash *hours* after a healthy boot no longer reverts
|
|
88
|
+
a good patch (bounded crash-attribution window).
|
|
89
|
+
- **Forced updates with a built-in UI** — set `autoApplyUpdates: true` and wrap
|
|
90
|
+
your app in `FlutterOtaApp`; the SDK shows a full-screen terminal-style
|
|
91
|
+
install screen (steps, live progress bar, speed/ETA, server message) and
|
|
92
|
+
cold-restarts into the new build. You write no UI.
|
|
93
|
+
- **Staged rollout** — server-side cohort math (per-mille + target cohorts) lets
|
|
94
|
+
you ship 1% → 5% → 20% → 50% → 100% safely.
|
|
95
|
+
- **Batteries-included tooling** — the `flutter-ota` CLI (`init`, `build`,
|
|
96
|
+
`deploy`, `bundle`, `channel`, `rollback`, `migrate`, `doctor`, and a bundled
|
|
97
|
+
PocketBase manager), plus runtime diagnostics and a sample app.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Try it in 5 minutes (no server needed)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
git clone https://github.com/HYPER12755/flutter-ota-kit.git
|
|
105
|
+
cd flutter-ota-kit/example
|
|
106
|
+
flutter pub get
|
|
107
|
+
flutter build apk --release
|
|
108
|
+
flutter install
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
1. Launch the app — it shows the **original** demo screen.
|
|
112
|
+
2. Tap **Apply patch**.
|
|
113
|
+
3. Swipe the app away from recents and reopen.
|
|
114
|
+
4. The screen has changed — the patch took effect.
|
|
115
|
+
5. Tap **Rollback** → restart → the original is back.
|
|
116
|
+
|
|
117
|
+
The example bundles a precompiled patch; everything runs offline on the device.
|
|
118
|
+
For a full backend walkthrough, see the [Beginner Guide](doc/beginner-guide.md)
|
|
119
|
+
or [Getting Started](doc/getting-started.md).
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Requirements
|
|
124
|
+
|
|
125
|
+
| Item | Requirement |
|
|
126
|
+
|--------------------------|----------------------------------------------------------|
|
|
127
|
+
| **Platform** | Android only (iOS / macOS / Windows / Linux / Web are no-ops) |
|
|
128
|
+
| **Dart SDK** | `>=3.13.2 <4.0.0` |
|
|
129
|
+
| **Flutter** | `>=3.47.2` |
|
|
130
|
+
| **Android `minSdk`** | 24 |
|
|
131
|
+
| **Android `compileSdk`** | 36 |
|
|
132
|
+
| **ABI** | `armeabi-v7a` / `arm64-v8a` / `x86_64` |
|
|
133
|
+
| **NDK** | 27.0.12077973+ |
|
|
134
|
+
| **Java / JVM** | 17 |
|
|
135
|
+
|
|
136
|
+
On non-Android platforms every API is safe to call but is a **no-op** — the
|
|
137
|
+
plugin logs a one-time warning and returns safe defaults, so you can ship
|
|
138
|
+
`flutter_ota_kit` in cross-platform code without guarding every call site.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Quick start
|
|
143
|
+
|
|
144
|
+
### 1. Install
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
dependencies:
|
|
148
|
+
flutter_ota_kit: ^0.2.0
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 2. Scaffold your backend (CLI)
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# installs the flutter-ota CLI globally
|
|
155
|
+
npm i -g @_nazmiforreal/flutter-ota
|
|
156
|
+
|
|
157
|
+
# scaffold config + a generated setup file for your backend
|
|
158
|
+
flutter-ota init supabase # or: postgres | cloudflare | aws | pocketbase
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`init` writes a `.flutter_ota_kit/config.json`, an `.env` template for secrets,
|
|
162
|
+
and a `lib/flutter_ota_kit_setup.dart` helper you call from `main()`.
|
|
163
|
+
|
|
164
|
+
### 3. Initialize in your app
|
|
165
|
+
|
|
166
|
+
Call the generated `setupFlutterOta()` (or wire it by hand) before `runApp`:
|
|
167
|
+
|
|
168
|
+
```dart
|
|
169
|
+
import 'package:flutter/material.dart';
|
|
170
|
+
import 'package:flutter_ota_kit/flutter_ota_kit.dart';
|
|
171
|
+
import 'flutter_ota_kit_setup.dart'; // generated by `flutter-ota init`
|
|
172
|
+
|
|
173
|
+
Future<void> main() async {
|
|
174
|
+
WidgetsFlutterBinding.ensureInitialized();
|
|
175
|
+
await setupFlutterOta(); // configures the backend + forced updates
|
|
176
|
+
runApp(const FlutterOtaApp(child: MyApp()));
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`FlutterOtaApp` is the zero-code wrapper that lets the SDK render its built-in
|
|
181
|
+
forced-update screen. Disable it app-wide with
|
|
182
|
+
`FlutterPatcher.showUpdateUi = false` (or `FlutterOtaApp(showUpdateUi: false)`),
|
|
183
|
+
or host it yourself by setting `MaterialApp.navigatorKey =
|
|
184
|
+
FlutterPatcher.navigatorKey`.
|
|
185
|
+
|
|
186
|
+
Prefer wiring the backend directly? Every backend has a `configureX` helper:
|
|
187
|
+
|
|
188
|
+
```dart
|
|
189
|
+
FlutterPatcher.configureSupabase(SupabaseUpdateConfig(
|
|
190
|
+
supabaseUrl: 'https://<ref>.supabase.co',
|
|
191
|
+
anonKey: '<anon-key>', // public, RLS-protected reads
|
|
192
|
+
bucket: 'bundles',
|
|
193
|
+
channel: 'production',
|
|
194
|
+
platform: Platform.android,
|
|
195
|
+
updateStrategy: UpdateStrategy.appVersion,
|
|
196
|
+
appVersion: '1.0.0', // or omit — auto-detected from versionName
|
|
197
|
+
));
|
|
198
|
+
await FlutterPatcher.init(autoApplyUpdates: true);
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### 4. Build a patch
|
|
202
|
+
|
|
203
|
+
Rebuild the release APK, then pack it into a device-ready `patch.zip`:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
flutter build apk --release
|
|
207
|
+
flutter-ota build \
|
|
208
|
+
--apk build/app/outputs/flutter-apk/app-release.apk \
|
|
209
|
+
--version 1.0.1 \
|
|
210
|
+
--target-version-code 100
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Every ABI in the APK is included, so one bundle serves all devices. Add
|
|
214
|
+
`--assets assets/hero.png,assets/strings/zh.json` to overlay updated assets.
|
|
215
|
+
|
|
216
|
+
### 5. Deploy
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
flutter-ota deploy \
|
|
220
|
+
--source dist \
|
|
221
|
+
--channel production \
|
|
222
|
+
--target-app-version 1.0.0 \
|
|
223
|
+
--force
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
That's it — devices pick up the patch on their next check.
|
|
227
|
+
|
|
228
|
+
### 6. Roll back (server side)
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
flutter-ota rollback --channel production
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Or on the device, `FlutterPatcher.rollback()` deletes the local patch and
|
|
235
|
+
reverts to the APK's built-in version on the next cold start.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## How it works
|
|
240
|
+
|
|
241
|
+
```text
|
|
242
|
+
┌─────────────────┐ Supabase / Postgres / Cloudflare / AWS / PocketBase / CDN
|
|
243
|
+
│ Your backend │ stores: patch.zip (the diff) + manifest.json (metadata)
|
|
244
|
+
└────────▲────────┘
|
|
245
|
+
│ 1. check-for-update on launch
|
|
246
|
+
│ 2. backend returns PatchInfo (url, md5, signature, force flag)
|
|
247
|
+
┌────────┴────────┐
|
|
248
|
+
│ The app │ 3. download patch.zip
|
|
249
|
+
│ │ 4. verify MD5 + Ed25519 signature
|
|
250
|
+
│ │ 5. write to local patch dir (atomic rename)
|
|
251
|
+
│ │ 6. cold-restart (forced) OR next cold start (staged)
|
|
252
|
+
│ │ 7. loader hook reads patched libapp.so + asset overlays
|
|
253
|
+
│ │ 8. boot ok → keep · boot fails → auto-rollback + blacklist
|
|
254
|
+
└─────────────────┘
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
A patch is a byte-for-byte diff of the new APK's `libapp.so` against the
|
|
258
|
+
version the user has installed; asset patches overlay `flutter_assets/` by path.
|
|
259
|
+
Nothing is swapped into the running process — it loads on the next cold start.
|
|
260
|
+
|
|
261
|
+
Full internals: [Architecture](doc/architecture.md).
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## What can and can't be patched
|
|
266
|
+
|
|
267
|
+
| ✅ Hot-patchable | ❌ Not hot-patchable |
|
|
268
|
+
|------------------------------------------------------|----------------------------------------------------------|
|
|
269
|
+
| Anything in `lib/` — widgets, logic, routes, constants | Native code (Kotlin / Java / C++ in `android/src/main/`) |
|
|
270
|
+
| Pure-Dart package upgrades (same native side) | `AndroidManifest.xml` changes |
|
|
271
|
+
| Flutter asset files registered in `pubspec.yaml` | APK `res/` (icons, layouts, strings.xml) |
|
|
272
|
+
| New `Image.asset()` / `rootBundle.load()` calls | Flutter Engine upgrades |
|
|
273
|
+
| | Adding or removing native plugins |
|
|
274
|
+
| | Removing assets that exist in the base APK |
|
|
275
|
+
|
|
276
|
+
See [API Reference → What can be patched](doc/api-reference.md#what-can-and-cannot-be-patched)
|
|
277
|
+
for edge cases (ProGuard/R8, multi-ABI/flavors, state migrations).
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Safety
|
|
282
|
+
|
|
283
|
+
- **Crash protection (on by default).** If a patch fails to boot, the SDK
|
|
284
|
+
auto-rolls back and blacklists the offending version. Tune with
|
|
285
|
+
`maxCrashCount` (default 1) and `verifyAfter` (default 5s). Crash attribution
|
|
286
|
+
is bounded to a boot window, so an unrelated crash long after a healthy boot
|
|
287
|
+
won't revert a working patch. See [Crash Protection](doc/crash-protection.md).
|
|
288
|
+
- **Integrity & signing.** MD5 is strongly recommended; Ed25519 signature
|
|
289
|
+
verification is available on Android 13+ (falls back to MD5-only below, with a
|
|
290
|
+
warning). Patches are bound to the host APK's `versionCode`, so stale patches
|
|
291
|
+
expire after an app upgrade. Keep private signing keys on your server, never
|
|
292
|
+
in the app bundle.
|
|
293
|
+
- **Staged rollout.** Ship 1% → 5% → 20% → 50% → 100% and watch crash rate at
|
|
294
|
+
each stage. The SDK records `lastBootDiagnostic` per device.
|
|
295
|
+
|
|
296
|
+
Full release workflow: [Production Playbook](doc/production-playbook.md).
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Documentation
|
|
301
|
+
|
|
302
|
+
**Guides**
|
|
303
|
+
|
|
304
|
+
- [Beginner Guide](doc/beginner-guide.md) — zero to first OTA, narrated
|
|
305
|
+
- [Getting Started](doc/getting-started.md) — scaffold → build → deploy
|
|
306
|
+
- [Developer Guide](doc/developer-guide.md) — full workflow reference
|
|
307
|
+
- [Configuration](doc/configuration.md) — env vars, `.env`, resolution order
|
|
308
|
+
- [Backends](doc/backends.md) — Supabase / Postgres / Cloudflare / AWS / PocketBase setup
|
|
309
|
+
- [Production Playbook](doc/production-playbook.md) — staged rollout, diagnostics, rollback
|
|
310
|
+
|
|
311
|
+
**Reference**
|
|
312
|
+
|
|
313
|
+
- [API Reference](doc/api-reference.md) — `FlutterPatcher`, configs, overlay, error codes
|
|
314
|
+
- [Architecture](doc/architecture.md) — internals, protocol, signing
|
|
315
|
+
- [CLI Reference](doc/cli-reference.md) — every command and flag
|
|
316
|
+
- [Crash Protection](doc/crash-protection.md) — rollback, blacklist, Android differences
|
|
317
|
+
- [FAQ](doc/faq.md) — versioning, cold start, store policy
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Contributing
|
|
322
|
+
|
|
323
|
+
Issues and PRs welcome. Before submitting:
|
|
324
|
+
|
|
325
|
+
- `flutter analyze` reports no issues
|
|
326
|
+
- `flutter test` is fully green
|
|
327
|
+
- If you touched native code, run a real-device patch / rollback end-to-end
|
|
328
|
+
- Document new public APIs (they surface on pub.dev)
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Root analysis options for the flutter_ota_kit monorepo.
|
|
2
|
+
#
|
|
3
|
+
# The repo is a monorepo: `packages/*`, `plugins/*`, and `example/` are each
|
|
4
|
+
# standalone Dart/Flutter packages that are analyzed (and lint-configured)
|
|
5
|
+
# independently. Excluding them here keeps `dart analyze .` focused on the root
|
|
6
|
+
# device-SDK package (`lib/` + `test/`), which is the only thing the root
|
|
7
|
+
# package owns. Each sub-package is analyzed on its own and is already clean.
|
|
8
|
+
analyzer:
|
|
9
|
+
exclude:
|
|
10
|
+
- packages/**
|
|
11
|
+
- plugins/**
|
|
12
|
+
- example/**
|
|
13
|
+
- node_modules/**
|
|
14
|
+
- npm/**
|
|
15
|
+
- build/**
|
|
16
|
+
- android/**
|
|
17
|
+
- ios/**
|
|
18
|
+
- web/**
|
|
19
|
+
- windows/**
|
|
20
|
+
- macos/**
|
|
21
|
+
- linux/**
|