@_nazmiforreal/flutter-ota 0.1.12 → 0.1.13
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 +1 -1
- package/bin/flutter-ota-linux-x64 +0 -0
- package/dart-src/packages/cli-tools/.dart_tool/package_config.json +46 -76
- package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +140 -166
- package/dart-src/packages/cli-tools/README.md +1 -1
- package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +30 -17
- package/dart-src/packages/cli-tools/lib/src/backend.dart +30 -31
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +3 -1
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +44 -43
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +265 -239
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +75 -66
- package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +39 -35
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +30 -27
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +88 -72
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +66 -29
- package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +10 -6
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +290 -181
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +24 -23
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +111 -97
- package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +237 -0
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +43 -34
- package/dart-src/packages/cli-tools/lib/src/commands/storage.dart +57 -53
- package/dart-src/packages/cli-tools/lib/src/config.dart +227 -93
- package/dart-src/packages/cli-tools/lib/src/operations.dart +9 -8
- package/dart-src/packages/cli-tools/lib/src/pack.dart +26 -25
- package/dart-src/packages/cli-tools/lib/src/pocketbase/data_bootstrap.dart +39 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/hooks/bundles.pb.js +56 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/installer.dart +189 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/process_manager.dart +123 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/schema_installer.dart +237 -0
- package/dart-src/packages/cli-tools/lib/src/runner.dart +36 -15
- package/dart-src/packages/cli-tools/lib/src/sign.dart +13 -5
- package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +10 -13
- package/dart-src/packages/cli-tools/lib/src/util.dart +3 -1
- package/dart-src/packages/cli-tools/pubspec.lock +69 -116
- package/dart-src/packages/cli-tools/pubspec.yaml +25 -7
- package/dart-src/packages/cli-tools/test/backends_test.dart +62 -32
- package/dart-src/packages/cli-tools/test/build_pack_test.dart +4 -4
- package/dart-src/packages/cli-tools/test/cli_test.dart +89 -42
- package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +8 -13
- package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +23 -18
- package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +197 -0
- package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +70 -46
- package/dart-src/packages/cli-tools/test/pocketbase_installer_test.dart +72 -0
- package/dart-src/packages/cli-tools/test/pocketbase_schema_test.dart +107 -0
- package/dart-src/packages/cli-tools/test/pocketbase_smoke_test.dart +59 -0
- package/dart-src/packages/core/README.md +2 -2
- package/dart-src/packages/core/example/lib/main.dart +69 -0
- package/dart-src/packages/core/lib/flutter_ota_kit_core.dart +1 -1
- package/dart-src/packages/core/lib/src/app_update_info.dart +17 -15
- package/dart-src/packages/core/lib/src/bundle.dart +102 -94
- package/dart-src/packages/core/lib/src/bundle_artifacts.dart +1 -2
- package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +15 -16
- package/dart-src/packages/core/lib/src/changed_asset.dart +24 -22
- package/dart-src/packages/core/lib/src/get_bundles_args.dart +13 -13
- package/dart-src/packages/core/lib/src/metadata.dart +9 -7
- package/dart-src/packages/core/lib/src/patch_info.dart +8 -7
- package/dart-src/packages/core/lib/src/platform.dart +4 -3
- package/dart-src/packages/core/lib/src/rollout.dart +14 -11
- package/dart-src/packages/core/lib/src/semver_range.dart +49 -39
- package/dart-src/packages/core/lib/src/semver_version.dart +8 -7
- package/dart-src/packages/core/lib/src/status.dart +4 -3
- package/dart-src/packages/core/lib/src/update_bundle_params.dart +7 -7
- package/dart-src/packages/core/lib/src/uuid.dart +1 -2
- package/dart-src/packages/core/pubspec.yaml +6 -7
- package/dart-src/packages/core/test/rollout_test.dart +15 -8
- package/dart-src/packages/core/test/semver_test.dart +5 -2
- package/dart-src/packages/core/test/types_test.dart +26 -24
- package/dart-src/packages/core/test/uuid_test.dart +3 -1
- package/dart-src/packages/core/tool/dbg.dart +1 -0
- package/dart-src/plugins/aws/CHANGELOG.md +6 -0
- package/dart-src/plugins/aws/example/lib/main.dart +60 -0
- package/dart-src/plugins/aws/lib/flutter_ota_kit_aws.dart +5 -1
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +11 -16
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_signer.dart +7 -2
- package/dart-src/plugins/aws/lib/src/aws_database.dart +14 -18
- package/dart-src/plugins/aws/lib/src/aws_s3_client.dart +23 -18
- package/dart-src/plugins/aws/lib/src/aws_ssm_client.dart +5 -4
- package/dart-src/plugins/aws/lib/src/aws_storage.dart +2 -3
- package/dart-src/plugins/aws/lib/src/aws_storage_profile.dart +10 -16
- package/dart-src/plugins/aws/lib/src/with_cloudfront_signed_url.dart +6 -9
- package/dart-src/plugins/aws/pubspec.yaml +15 -10
- package/dart-src/plugins/aws/test/aws_database_test.dart +51 -57
- package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +8 -13
- package/dart-src/plugins/cloudflare/CHANGELOG.md +7 -0
- package/dart-src/plugins/cloudflare/example/lib/main.dart +62 -0
- package/dart-src/plugins/cloudflare/lib/flutter_ota_kit_cloudflare.dart +4 -2
- package/dart-src/plugins/cloudflare/lib/src/cloudflare_worker_database.dart +3 -3
- package/dart-src/plugins/cloudflare/lib/src/d1_build_where.dart +3 -2
- package/dart-src/plugins/cloudflare/lib/src/d1_bundle_mapper.dart +24 -27
- package/dart-src/plugins/cloudflare/lib/src/d1_database_plugin.dart +22 -30
- package/dart-src/plugins/cloudflare/lib/src/r2_config.dart +1 -1
- package/dart-src/plugins/cloudflare/lib/src/r2_s3_client.dart +21 -29
- package/dart-src/plugins/cloudflare/lib/src/r2_storage.dart +7 -7
- package/dart-src/plugins/cloudflare/lib/src/r2_storage_profile.dart +7 -15
- package/dart-src/plugins/cloudflare/pubspec.yaml +14 -9
- package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +46 -42
- package/dart-src/plugins/cloudflare/test/d1_database_test.dart +100 -90
- package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +23 -18
- package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +8 -13
- package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +1 -4
- package/dart-src/plugins/plugin-core/CHANGELOG.md +16 -0
- package/dart-src/plugins/plugin-core/example/lib/main.dart +61 -0
- package/dart-src/plugins/plugin-core/lib/flutter_ota_kit_plugin_core.dart +3 -7
- package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +5 -5
- package/dart-src/plugins/plugin-core/lib/src/bundle_helpers.dart +23 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_storage_layout.dart +16 -10
- package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work.dart +31 -26
- package/dart-src/plugins/plugin-core/lib/src/calculate_pagination.dart +5 -1
- package/dart-src/plugins/plugin-core/lib/src/content_addressed_assets.dart +1 -1
- package/dart-src/plugins/plugin-core/lib/src/create_blob_database_plugin.dart +78 -71
- package/dart-src/plugins/plugin-core/lib/src/create_database_plugin.dart +56 -35
- package/dart-src/plugins/plugin-core/lib/src/create_storage_plugin.dart +10 -13
- package/dart-src/plugins/plugin-core/lib/src/paginate_bundles.dart +30 -17
- package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +3 -1
- package/dart-src/plugins/plugin-core/lib/src/query_bundles.dart +5 -3
- package/dart-src/plugins/plugin-core/lib/src/request_update_bundle_state.dart +2 -4
- package/dart-src/plugins/plugin-core/lib/src/storage_helpers.dart +18 -0
- package/dart-src/plugins/plugin-core/lib/src/types.dart +7 -6
- package/dart-src/plugins/plugin-core/pubspec.yaml +12 -9
- package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +13 -9
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +28 -15
- package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +254 -122
- package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +36 -32
- package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +18 -15
- package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +8 -3
- package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +682 -693
- package/dart-src/plugins/plugin-core/test/helpers_test.dart +45 -21
- package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +76 -13
- package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +20 -9
- package/dart-src/plugins/pocketbase/CHANGELOG.md +8 -0
- package/dart-src/plugins/pocketbase/LICENSE +21 -0
- package/dart-src/plugins/pocketbase/README.md +54 -0
- package/dart-src/plugins/pocketbase/example/lib/main.dart +62 -0
- package/dart-src/plugins/pocketbase/lib/flutter_ota_kit_pocketbase.dart +28 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_bundle_mapper.dart +121 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_client.dart +349 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_config.dart +61 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_database.dart +278 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_storage.dart +255 -0
- package/dart-src/plugins/pocketbase/pubspec.yaml +26 -0
- package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +12 -0
- package/dart-src/plugins/postgres/CHANGELOG.md +6 -0
- package/dart-src/plugins/postgres/example/lib/main.dart +67 -0
- package/dart-src/plugins/postgres/lib/flutter_ota_kit_postgres.dart +3 -6
- package/dart-src/plugins/postgres/lib/src/postgres_bundle_mapper.dart +32 -52
- package/dart-src/plugins/postgres/lib/src/postgres_config.dart +3 -1
- package/dart-src/plugins/postgres/lib/src/postgres_database.dart +28 -38
- package/dart-src/plugins/postgres/lib/src/postgres_get_update_info.dart +16 -14
- package/dart-src/plugins/postgres/lib/src/postgres_storage.dart +26 -14
- package/dart-src/plugins/postgres/lib/src/postgres_types.dart +27 -28
- package/dart-src/plugins/postgres/pubspec.yaml +14 -9
- package/dart-src/plugins/postgres/test/mock_postgres_client.dart +62 -39
- package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +3 -4
- package/dart-src/plugins/postgres/test/postgres_database_test.dart +102 -73
- package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +60 -0
- package/dart-src/plugins/supabase/CHANGELOG.md +17 -0
- package/dart-src/plugins/supabase/README.md +2 -2
- package/dart-src/plugins/supabase/example/lib/main.dart +63 -0
- package/dart-src/plugins/supabase/lib/flutter_ota_kit_supabase.dart +2 -0
- package/dart-src/plugins/supabase/lib/src/error_message.dart +11 -0
- package/dart-src/plugins/supabase/lib/src/supabase_bundle_mapper.dart +34 -58
- package/dart-src/plugins/supabase/lib/src/supabase_client_adapter.dart +3 -13
- package/dart-src/plugins/supabase/lib/src/supabase_client_http.dart +34 -52
- package/dart-src/plugins/supabase/lib/src/supabase_database.dart +38 -30
- package/dart-src/plugins/supabase/lib/src/supabase_edge_function_storage.dart +21 -29
- package/dart-src/plugins/supabase/lib/src/supabase_signed_url_batcher.dart +7 -19
- package/dart-src/plugins/supabase/lib/src/supabase_storage.dart +18 -29
- package/dart-src/plugins/supabase/lib/src/types.dart +26 -26
- package/dart-src/plugins/supabase/pubspec.yaml +14 -9
- package/dart-src/plugins/supabase/test/mock_supabase_client.dart +57 -57
- package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +30 -27
- package/dart-src/plugins/supabase/test/supabase_database_test.dart +26 -35
- package/dart-src/plugins/supabase/test/supabase_storage_test.dart +27 -34
- package/package.json +3 -3
- package/scripts/vendor.sh +1 -1
|
@@ -16,13 +16,17 @@ class FingerprintCommand extends FlutterPatcherCommand {
|
|
|
16
16
|
|
|
17
17
|
@override
|
|
18
18
|
ArgParser get argParser => ArgParser()
|
|
19
|
-
..addOption(
|
|
20
|
-
|
|
19
|
+
..addOption(
|
|
20
|
+
'source',
|
|
21
|
+
abbr: 's',
|
|
22
|
+
defaultsTo: './dist',
|
|
23
|
+
help: 'Directory to fingerprint.',
|
|
24
|
+
);
|
|
21
25
|
|
|
22
26
|
@override
|
|
23
27
|
Future<int> run() => runGuarded(() async {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
final source = argResults!['source'] as String;
|
|
29
|
+
final hash = generateFingerprint(source);
|
|
30
|
+
stdout.writeln(hash);
|
|
31
|
+
});
|
|
28
32
|
}
|
|
@@ -29,18 +29,34 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
29
29
|
@override
|
|
30
30
|
String get description =>
|
|
31
31
|
'Scaffold flutter_ota_kit integration files + .flutter_ota_kit config for the current '
|
|
32
|
-
'Flutter project. Usage: flutter_ota_kit init <supabase|postgres|cloudflare|aws>';
|
|
32
|
+
'Flutter project. Usage: flutter_ota_kit init <supabase|postgres|cloudflare|aws|pocketbase>';
|
|
33
33
|
|
|
34
34
|
@override
|
|
35
35
|
ArgParser get argParser => ArgParser()
|
|
36
|
-
..addOption(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
..addOption(
|
|
37
|
+
'provider',
|
|
38
|
+
abbr: 'p',
|
|
39
|
+
defaultsTo: 'supabase',
|
|
40
|
+
help:
|
|
41
|
+
'Backend provider (also accepted as the first positional argument).',
|
|
42
|
+
)
|
|
43
|
+
..addOption(
|
|
44
|
+
'channel',
|
|
45
|
+
abbr: 'c',
|
|
46
|
+
defaultsTo: 'production',
|
|
47
|
+
help: 'Default channel.',
|
|
48
|
+
)
|
|
41
49
|
..addOption('platform', defaultsTo: 'android', help: 'Default platform.')
|
|
42
|
-
..addOption(
|
|
43
|
-
|
|
50
|
+
..addOption(
|
|
51
|
+
'source',
|
|
52
|
+
abbr: 's',
|
|
53
|
+
defaultsTo: './dist',
|
|
54
|
+
help: 'Default deploy source.',
|
|
55
|
+
)
|
|
56
|
+
..addFlag(
|
|
57
|
+
'global',
|
|
58
|
+
help: 'Write to the global ~/.flutter_ota_kit config (no scaffolding).',
|
|
59
|
+
)
|
|
44
60
|
..addFlag('force', abbr: 'f', help: 'Overwrite an existing config.');
|
|
45
61
|
|
|
46
62
|
String? _p(String label, String? current) {
|
|
@@ -58,6 +74,7 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
58
74
|
'postgres',
|
|
59
75
|
'cloudflare',
|
|
60
76
|
'aws',
|
|
77
|
+
'pocketbase',
|
|
61
78
|
];
|
|
62
79
|
|
|
63
80
|
/// Interactive backend picker. Returns the chosen provider, defaulting to
|
|
@@ -80,168 +97,208 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
80
97
|
|
|
81
98
|
@override
|
|
82
99
|
Future<int> run() => runGuarded(() async {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
100
|
+
// A positional backend (`flutter_ota_kit init supabase`) takes precedence
|
|
101
|
+
// over the --provider flag.
|
|
102
|
+
final positional = argResults!.rest;
|
|
103
|
+
final requested = positional.isNotEmpty
|
|
104
|
+
? positional.first
|
|
105
|
+
: (argResults!['provider'] as String);
|
|
106
|
+
final provider = stdin.hasTerminal ? _chooseProvider(requested) : requested;
|
|
107
|
+
if (!_providers.contains(provider)) {
|
|
108
|
+
throw PackException(
|
|
109
|
+
'Unknown provider "$provider". Choose one of: '
|
|
110
|
+
'${_providers.join(', ')}.',
|
|
111
|
+
64,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
final global = argResults!['global'] as bool;
|
|
116
|
+
final file = global ? configCandidates().last : configCandidates().first;
|
|
117
|
+
if (file.existsSync() && !(argResults!['force'] as bool)) {
|
|
118
|
+
throw PackException(
|
|
119
|
+
'Config already exists at ${file.path}. Use --force to overwrite.',
|
|
120
|
+
64,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
final channel = argResults!['channel'] as String? ?? 'production';
|
|
125
|
+
final platform = argResults!['platform'] as String? ?? 'android';
|
|
126
|
+
final source = argResults!['source'] as String? ?? './dist';
|
|
127
|
+
final env = Platform.environment;
|
|
128
|
+
|
|
129
|
+
banner('init');
|
|
130
|
+
|
|
131
|
+
late FlutterPatcherConfig cfg;
|
|
132
|
+
switch (provider) {
|
|
133
|
+
case 'postgres':
|
|
134
|
+
cfg = FlutterPatcherConfig(
|
|
135
|
+
provider: provider,
|
|
136
|
+
channel: channel,
|
|
137
|
+
platform: platform,
|
|
138
|
+
source: source,
|
|
139
|
+
supabase: const SupabaseConfigJson(),
|
|
140
|
+
postgres: PostgresConfigJson(
|
|
141
|
+
host: _p('Postgres host', env['POSTGRES_HOST']),
|
|
142
|
+
port: _p('Postgres port', env['POSTGRES_PORT'] ?? '5432'),
|
|
143
|
+
database: _p('Postgres database', env['POSTGRES_DB'] ?? 'postgres'),
|
|
144
|
+
username: _p('Postgres username', env['POSTGRES_USER']),
|
|
145
|
+
password: _p('Postgres password', env['POSTGRES_PASSWORD']),
|
|
146
|
+
sslMode: _p('Postgres sslmode', env['POSTGRES_SSLMODE']),
|
|
147
|
+
basePath: _p('Storage base path', env['POSTGRES_BASE_PATH']),
|
|
148
|
+
servingBaseUrl: _p(
|
|
149
|
+
'Serving base url',
|
|
150
|
+
env['POSTGRES_SERVING_BASE_URL'],
|
|
151
|
+
),
|
|
152
|
+
),
|
|
153
|
+
);
|
|
154
|
+
case 'cloudflare':
|
|
155
|
+
cfg = FlutterPatcherConfig(
|
|
156
|
+
provider: provider,
|
|
157
|
+
channel: channel,
|
|
158
|
+
platform: platform,
|
|
159
|
+
source: source,
|
|
160
|
+
supabase: const SupabaseConfigJson(),
|
|
161
|
+
cloudflare: CloudflareConfigJson(
|
|
162
|
+
accountId: _p(
|
|
163
|
+
'Cloudflare account id',
|
|
164
|
+
env['CLOUDFLARE_ACCOUNT_ID'],
|
|
165
|
+
),
|
|
166
|
+
d1DatabaseId: _p(
|
|
167
|
+
'Cloudflare D1 database id',
|
|
168
|
+
env['CLOUDFLARE_D1_DATABASE_ID'],
|
|
169
|
+
),
|
|
170
|
+
apiToken: _p('Cloudflare API token', env['CLOUDFLARE_API_TOKEN']),
|
|
171
|
+
r2Bucket: _p('R2 bucket', env['R2_BUCKET'] ?? 'bundles'),
|
|
172
|
+
r2AccessKeyId: _p('R2 access key id', env['R2_ACCESS_KEY_ID']),
|
|
173
|
+
r2SecretAccessKey: _p(
|
|
174
|
+
'R2 secret access key',
|
|
175
|
+
env['R2_SECRET_ACCESS_KEY'],
|
|
176
|
+
),
|
|
177
|
+
r2BasePath: _p('R2 base path', env['R2_BASE_PATH']),
|
|
178
|
+
),
|
|
179
|
+
);
|
|
180
|
+
case 'aws':
|
|
181
|
+
cfg = FlutterPatcherConfig(
|
|
182
|
+
provider: provider,
|
|
183
|
+
channel: channel,
|
|
184
|
+
platform: platform,
|
|
185
|
+
source: source,
|
|
186
|
+
supabase: const SupabaseConfigJson(),
|
|
187
|
+
aws: AwsConfigJson(
|
|
188
|
+
bucket: _p('AWS bucket', env['AWS_BUCKET']),
|
|
189
|
+
region: _p('AWS region', env['AWS_REGION']),
|
|
190
|
+
accessKeyId: _p('AWS access key id', env['AWS_ACCESS_KEY_ID']),
|
|
191
|
+
secretAccessKey: _p(
|
|
192
|
+
'AWS secret access key',
|
|
193
|
+
env['AWS_SECRET_ACCESS_KEY'],
|
|
194
|
+
),
|
|
195
|
+
endpoint: _p('AWS endpoint', env['AWS_ENDPOINT']),
|
|
196
|
+
basePath: _p('AWS base path', env['AWS_BASE_PATH']),
|
|
197
|
+
sessionToken: _p('AWS session token', env['AWS_SESSION_TOKEN']),
|
|
198
|
+
),
|
|
199
|
+
);
|
|
200
|
+
case 'pocketbase':
|
|
201
|
+
cfg = FlutterPatcherConfig(
|
|
202
|
+
provider: provider,
|
|
203
|
+
channel: channel,
|
|
204
|
+
platform: platform,
|
|
205
|
+
source: source,
|
|
206
|
+
supabase: const SupabaseConfigJson(),
|
|
207
|
+
pocketbase: PocketBaseConfigJson(
|
|
208
|
+
url: _p('PocketBase URL', env['POCKETBASE_URL']),
|
|
209
|
+
adminEmail: _p(
|
|
210
|
+
'PocketBase admin email',
|
|
211
|
+
env['POCKETBASE_ADMIN_EMAIL'],
|
|
212
|
+
),
|
|
213
|
+
adminPassword: _p(
|
|
214
|
+
'PocketBase admin password',
|
|
215
|
+
env['POCKETBASE_ADMIN_PASSWORD'],
|
|
216
|
+
),
|
|
217
|
+
),
|
|
218
|
+
);
|
|
219
|
+
case 'supabase':
|
|
220
|
+
default:
|
|
221
|
+
cfg = FlutterPatcherConfig(
|
|
222
|
+
provider: 'supabase',
|
|
223
|
+
channel: channel,
|
|
224
|
+
platform: platform,
|
|
225
|
+
source: source,
|
|
226
|
+
supabase: SupabaseConfigJson(
|
|
227
|
+
url: _p('Supabase URL', env['SUPABASE_URL']),
|
|
228
|
+
serviceRoleKey: _p(
|
|
229
|
+
'Supabase service role key',
|
|
230
|
+
env['SUPABASE_SERVICE_ROLE_KEY'],
|
|
231
|
+
),
|
|
232
|
+
anonKey: _p(
|
|
233
|
+
'Supabase publishable key',
|
|
234
|
+
env['SUPABASE_PUBLISHABLE_KEY'],
|
|
235
|
+
),
|
|
236
|
+
bucket: _p('Storage bucket', env['SUPABASE_BUCKET'] ?? 'bundles'),
|
|
237
|
+
basePath: _p('Storage base path', env['SUPABASE_BASE_PATH']),
|
|
238
|
+
managementKey: _p(
|
|
239
|
+
'Supabase Management API key (optional, used by `migrate`)',
|
|
240
|
+
env['SUPABASE_MANAGEMENT_KEY'],
|
|
241
|
+
),
|
|
242
|
+
databaseUrl: _p(
|
|
243
|
+
'Postgres DATABASE_URL (optional, used by `migrate`)',
|
|
244
|
+
env['SUPABASE_DATABASE_URL'],
|
|
245
|
+
),
|
|
246
|
+
),
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
saveConfig(cfg, global: global);
|
|
251
|
+
step('Wrote ${file.path}');
|
|
252
|
+
|
|
253
|
+
if (global) {
|
|
254
|
+
stdout.writeln(
|
|
255
|
+
' ${yellow('⚠')} Secrets are stored in plaintext under ~/.flutter_ota_kit. '
|
|
256
|
+
'Restrict permissions and use a secrets manager in production.',
|
|
257
|
+
);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Scaffold the host project integration files.
|
|
262
|
+
await _scaffoldProject(cfg, provider);
|
|
263
|
+
|
|
264
|
+
stdout.writeln();
|
|
265
|
+
stdout.writeln(cyan('Next steps:'));
|
|
266
|
+
stdout.writeln(' 1. ${dim('flutter pub get')}');
|
|
267
|
+
stdout.writeln(
|
|
268
|
+
' 2. In lib/main.dart call ${green('await setupFlutterOta();')} '
|
|
269
|
+
'right after WidgetsFlutterBinding.ensureInitialized() (before runApp).',
|
|
270
|
+
);
|
|
271
|
+
stdout.writeln(
|
|
272
|
+
' A `.env` scaffold was written — put secrets there, then build with '
|
|
273
|
+
'`--dart-define-from-file=.env` (environment overrides config).',
|
|
274
|
+
);
|
|
275
|
+
if (provider == 'supabase') {
|
|
276
|
+
stdout.writeln(
|
|
277
|
+
' 3. Provision the backend once: ${dim('flutter_ota_kit migrate supabase')}',
|
|
278
|
+
);
|
|
279
|
+
} else if (provider == 'postgres') {
|
|
280
|
+
stdout.writeln(
|
|
281
|
+
' 3. Provision the backend: ${dim('flutter_ota_kit migrate postgres')}',
|
|
282
|
+
);
|
|
283
|
+
} else if (provider == 'pocketbase') {
|
|
284
|
+
stdout.writeln(
|
|
285
|
+
' 3. Install PocketBase + provision the schema: '
|
|
286
|
+
'${dim('flutter_ota_kit pocketbase install')} then '
|
|
287
|
+
'${dim('flutter_ota_kit pocketbase serve')}.',
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
stdout.writeln(
|
|
291
|
+
' 4. Build a patch: ${dim('flutter_ota_kit build --name 1.0.1')} '
|
|
292
|
+
'then ${dim('flutter_ota_kit deploy')}',
|
|
293
|
+
);
|
|
294
|
+
});
|
|
241
295
|
|
|
242
296
|
/// Generate the integration files (pubspec dep, manifest permission, setup dart
|
|
243
297
|
/// file, .gitignore entry) inside the current directory.
|
|
244
|
-
Future<void> _scaffoldProject(
|
|
298
|
+
Future<void> _scaffoldProject(
|
|
299
|
+
FlutterPatcherConfig cfg,
|
|
300
|
+
String provider,
|
|
301
|
+
) async {
|
|
245
302
|
_addPubDependency();
|
|
246
303
|
_addInternetPermission();
|
|
247
304
|
await _writeSetupFile(cfg, provider);
|
|
@@ -252,7 +309,9 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
252
309
|
void _addPubDependency() {
|
|
253
310
|
final pubspec = File('pubspec.yaml');
|
|
254
311
|
if (!pubspec.existsSync()) {
|
|
255
|
-
stdout.writeln(
|
|
312
|
+
stdout.writeln(
|
|
313
|
+
yellow(' ⚠ pubspec.yaml not found; skipping dependency injection.'),
|
|
314
|
+
);
|
|
256
315
|
return;
|
|
257
316
|
}
|
|
258
317
|
var content = pubspec.readAsStringSync();
|
|
@@ -264,7 +323,11 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
264
323
|
final marker = '\ndependencies:';
|
|
265
324
|
final idx = content.indexOf(marker);
|
|
266
325
|
if (idx == -1) {
|
|
267
|
-
stdout.writeln(
|
|
326
|
+
stdout.writeln(
|
|
327
|
+
yellow(
|
|
328
|
+
' ⚠ could not find a `dependencies:` block; add `flutter_ota_kit` manually.',
|
|
329
|
+
),
|
|
330
|
+
);
|
|
268
331
|
return;
|
|
269
332
|
}
|
|
270
333
|
final insertAt = idx + marker.length;
|
|
@@ -276,7 +339,11 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
276
339
|
void _addInternetPermission() {
|
|
277
340
|
final mf = File('android/app/src/main/AndroidManifest.xml');
|
|
278
341
|
if (!mf.existsSync()) {
|
|
279
|
-
stdout.writeln(
|
|
342
|
+
stdout.writeln(
|
|
343
|
+
yellow(
|
|
344
|
+
' ⚠ AndroidManifest.xml not found; skipping INTERNET permission.',
|
|
345
|
+
),
|
|
346
|
+
);
|
|
280
347
|
return;
|
|
281
348
|
}
|
|
282
349
|
var content = mf.readAsStringSync();
|
|
@@ -286,7 +353,11 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
286
353
|
}
|
|
287
354
|
final match = RegExp(r'<manifest[^>]*>').firstMatch(content);
|
|
288
355
|
if (match == null) {
|
|
289
|
-
stdout.writeln(
|
|
356
|
+
stdout.writeln(
|
|
357
|
+
yellow(
|
|
358
|
+
' ⚠ could not locate <manifest> tag; add INTERNET permission manually.',
|
|
359
|
+
),
|
|
360
|
+
);
|
|
290
361
|
return;
|
|
291
362
|
}
|
|
292
363
|
final insertAt = match.end;
|
|
@@ -296,10 +367,15 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
296
367
|
'\n <uses-permission android:name="android.permission.INTERNET" />',
|
|
297
368
|
);
|
|
298
369
|
mf.writeAsStringSync(content);
|
|
299
|
-
step(
|
|
370
|
+
step(
|
|
371
|
+
'Added INTERNET permission to android/app/src/main/AndroidManifest.xml',
|
|
372
|
+
);
|
|
300
373
|
}
|
|
301
374
|
|
|
302
|
-
Future<void> _writeSetupFile(
|
|
375
|
+
Future<void> _writeSetupFile(
|
|
376
|
+
FlutterPatcherConfig cfg,
|
|
377
|
+
String provider,
|
|
378
|
+
) async {
|
|
303
379
|
final dir = Directory('lib');
|
|
304
380
|
if (!dir.existsSync()) dir.createSync(recursive: true);
|
|
305
381
|
final file = File('lib/flutter_ota_kit_setup.dart');
|
|
@@ -314,7 +390,9 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
314
390
|
step('.gitignore already ignores .flutter_ota_kit/');
|
|
315
391
|
return;
|
|
316
392
|
}
|
|
317
|
-
final next = content.isEmpty
|
|
393
|
+
final next = content.isEmpty
|
|
394
|
+
? '.flutter_ota_kit/\n'
|
|
395
|
+
: '$content\n.flutter_ota_kit/\n';
|
|
318
396
|
gi.writeAsStringSync(next);
|
|
319
397
|
step('Added .flutter_ota_kit/ to .gitignore');
|
|
320
398
|
}
|
|
@@ -360,6 +438,15 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
360
438
|
entries['AWS_SESSION_TOKEN'] = c.sessionToken ?? '';
|
|
361
439
|
entries['CHANNEL'] = cfg.channel;
|
|
362
440
|
entries['APP_VERSION'] = '';
|
|
441
|
+
case 'pocketbase':
|
|
442
|
+
final c = cfg.pocketbase;
|
|
443
|
+
entries['POCKETBASE_URL'] = c.url ?? '';
|
|
444
|
+
entries['POCKETBASE_ADMIN_EMAIL'] = c.adminEmail ?? '';
|
|
445
|
+
entries['POCKETBASE_ADMIN_PASSWORD'] = c.adminPassword ?? '';
|
|
446
|
+
entries['POCKETBASE_BUNDLES_COLLECTION'] = c.bundlesCollection;
|
|
447
|
+
entries['POCKETBASE_BUCKET'] = c.bundlesBucket;
|
|
448
|
+
entries['CHANNEL'] = cfg.channel;
|
|
449
|
+
entries['APP_VERSION'] = '';
|
|
363
450
|
case 'supabase':
|
|
364
451
|
default:
|
|
365
452
|
final c = cfg.supabase;
|
|
@@ -375,7 +462,9 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
375
462
|
buffer.writeln('# Generated by `flutter-ota init $provider`.');
|
|
376
463
|
buffer.writeln('# Fill in (or override) values, then build with:');
|
|
377
464
|
buffer.writeln('# flutter run --dart-define-from-file=.env');
|
|
378
|
-
buffer.writeln(
|
|
465
|
+
buffer.writeln(
|
|
466
|
+
'# Environment overrides the project config and any defaults.',
|
|
467
|
+
);
|
|
379
468
|
for (final e in entries.entries) {
|
|
380
469
|
buffer.writeln('${e.key}=${e.value}');
|
|
381
470
|
}
|
|
@@ -392,8 +481,9 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
392
481
|
|
|
393
482
|
void _ensureEnvGitignored() {
|
|
394
483
|
final gi = File('.gitignore');
|
|
395
|
-
final lines =
|
|
396
|
-
|
|
484
|
+
final lines = gi.existsSync()
|
|
485
|
+
? gi.readAsStringSync().split('\n')
|
|
486
|
+
: const <String>[];
|
|
397
487
|
if (lines.contains('.env')) {
|
|
398
488
|
step('.gitignore already ignores .env');
|
|
399
489
|
return;
|
|
@@ -420,7 +510,8 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
420
510
|
switch (provider) {
|
|
421
511
|
case 'postgres':
|
|
422
512
|
final c = cfg.postgres;
|
|
423
|
-
configure =
|
|
513
|
+
configure =
|
|
514
|
+
'''
|
|
424
515
|
FlutterPatcher.configurePostgres(PostgresUpdateConfig(
|
|
425
516
|
host: const String.fromEnvironment('POSTGRES_HOST', defaultValue: ${_q(c.host ?? '')}),
|
|
426
517
|
port: int.tryParse(const String.fromEnvironment('POSTGRES_PORT', defaultValue: ${_q(c.port ?? '5432')})) ?? 5432,
|
|
@@ -435,7 +526,8 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
435
526
|
));''';
|
|
436
527
|
case 'cloudflare':
|
|
437
528
|
final c = cfg.cloudflare;
|
|
438
|
-
configure =
|
|
529
|
+
configure =
|
|
530
|
+
'''
|
|
439
531
|
FlutterPatcher.configureCloudflare(CloudflareUpdateConfig(
|
|
440
532
|
accountId: const String.fromEnvironment('CLOUDFLARE_ACCOUNT_ID', defaultValue: ${_q(c.accountId ?? '')}),
|
|
441
533
|
databaseId: const String.fromEnvironment('CLOUDFLARE_D1_DATABASE_ID', defaultValue: ${_q(c.d1DatabaseId ?? '')}),
|
|
@@ -451,7 +543,8 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
451
543
|
));''';
|
|
452
544
|
case 'aws':
|
|
453
545
|
final c = cfg.aws;
|
|
454
|
-
configure =
|
|
546
|
+
configure =
|
|
547
|
+
'''
|
|
455
548
|
FlutterPatcher.configureAws(AwsUpdateConfig(
|
|
456
549
|
bucketName: const String.fromEnvironment('AWS_BUCKET', defaultValue: ${_q(c.bucket ?? '')}),
|
|
457
550
|
region: const String.fromEnvironment('AWS_REGION', defaultValue: ${_q(c.region ?? '')}),
|
|
@@ -463,11 +556,27 @@ class InitCommand extends FlutterPatcherCommand {
|
|
|
463
556
|
platform: Platform.android,
|
|
464
557
|
updateStrategy: UpdateStrategy.appVersion,
|
|
465
558
|
appVersion: $appVersionExpr,
|
|
559
|
+
));''';
|
|
560
|
+
case 'pocketbase':
|
|
561
|
+
final c = cfg.pocketbase;
|
|
562
|
+
configure =
|
|
563
|
+
'''
|
|
564
|
+
FlutterPatcher.configurePocketBase(PocketBaseUpdateConfig(
|
|
565
|
+
url: const String.fromEnvironment('POCKETBASE_URL', defaultValue: ${_q(c.url ?? '')}),
|
|
566
|
+
adminEmail: const String.fromEnvironment('POCKETBASE_ADMIN_EMAIL', defaultValue: ''),
|
|
567
|
+
adminPassword: const String.fromEnvironment('POCKETBASE_ADMIN_PASSWORD', defaultValue: ''),
|
|
568
|
+
bundlesCollection: const String.fromEnvironment('POCKETBASE_BUNDLES_COLLECTION', defaultValue: ${_q(c.bundlesCollection)}),
|
|
569
|
+
bundlesBucket: const String.fromEnvironment('POCKETBASE_BUCKET', defaultValue: ${_q(c.bundlesBucket)}),
|
|
570
|
+
channel: const String.fromEnvironment('CHANNEL', defaultValue: $channel),
|
|
571
|
+
platform: Platform.android,
|
|
572
|
+
updateStrategy: UpdateStrategy.appVersion,
|
|
573
|
+
appVersion: $appVersionExpr,
|
|
466
574
|
));''';
|
|
467
575
|
case 'supabase':
|
|
468
576
|
default:
|
|
469
577
|
final c = cfg.supabase;
|
|
470
|
-
configure =
|
|
578
|
+
configure =
|
|
579
|
+
'''
|
|
471
580
|
FlutterPatcher.configureSupabase(SupabaseUpdateConfig(
|
|
472
581
|
supabaseUrl: const String.fromEnvironment('SUPABASE_URL', defaultValue: ${_q(c.url ?? '')}),
|
|
473
582
|
anonKey: const String.fromEnvironment('SUPABASE_PUBLISHABLE_KEY', defaultValue: ''),
|