@_nazmiforreal/flutter-ota 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/flutter-patcher.js +37 -39
- package/dart-src/packages/cli-tools/.dart_tool/package_config.json +496 -0
- package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +743 -0
- package/dart-src/packages/cli-tools/.gitkeep +0 -0
- package/dart-src/packages/cli-tools/bin/flutter_patcher.dart +9 -0
- package/dart-src/packages/cli-tools/lib/flutter_patcher_cli.dart +73 -0
- package/dart-src/packages/cli-tools/lib/src/backend.dart +162 -0
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +56 -0
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +99 -0
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +151 -0
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +140 -0
- package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +113 -0
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +55 -0
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +95 -0
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +48 -0
- package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +29 -0
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +82 -0
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +55 -0
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +132 -0
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +41 -0
- package/dart-src/packages/cli-tools/lib/src/config.dart +715 -0
- package/dart-src/packages/cli-tools/lib/src/operations.dart +206 -0
- package/dart-src/packages/cli-tools/lib/src/pack.dart +417 -0
- package/dart-src/packages/cli-tools/lib/src/sign.dart +60 -0
- package/dart-src/packages/cli-tools/lib/src/util.dart +86 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0001_hot-updater_init.sql +16 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0002_hot-updater_0.13.0.sql +8 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0003_hot-updater_0.18.0.sql +38 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0004_hot-updater_0.29.0.sql +11 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0005_hot-updater_0.31.0.sql +24 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/bundles.sql +42 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250103114225_init.sql +228 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250314000000_hot-updater_0.13.0.sql +134 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250516000000_hot-updater_0.18.0.sql +194 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20251014000000_hot-updater_0.21.0.sql +184 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260401000000_hot-updater_0.29.0.sql +503 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260414000000_hot-updater_0.30.0.sql +50 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260422000000_hot-updater_0.31.0.sql +20 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260520014100_hot-updater_rls.sql +48 -0
- package/dart-src/packages/cli-tools/pubspec.lock +638 -0
- package/dart-src/packages/cli-tools/pubspec.yaml +37 -0
- package/dart-src/packages/core/lib/flutter_patcher_core.dart +29 -0
- package/dart-src/packages/core/lib/src/app_update_info.dart +111 -0
- package/dart-src/packages/core/lib/src/bundle.dart +191 -0
- package/dart-src/packages/core/lib/src/bundle_artifacts.dart +39 -0
- package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +47 -0
- package/dart-src/packages/core/lib/src/changed_asset.dart +77 -0
- package/dart-src/packages/core/lib/src/get_bundles_args.dart +78 -0
- package/dart-src/packages/core/lib/src/metadata.dart +31 -0
- package/dart-src/packages/core/lib/src/patch_info.dart +284 -0
- package/dart-src/packages/core/lib/src/platform.dart +15 -0
- package/dart-src/packages/core/lib/src/rollout.dart +209 -0
- package/dart-src/packages/core/lib/src/semver.dart +70 -0
- package/dart-src/packages/core/lib/src/semver_range.dart +358 -0
- package/dart-src/packages/core/lib/src/semver_version.dart +94 -0
- package/dart-src/packages/core/lib/src/status.dart +24 -0
- package/dart-src/packages/core/lib/src/strategy.dart +10 -0
- package/dart-src/packages/core/lib/src/update_bundle_params.dart +30 -0
- package/dart-src/packages/core/lib/src/uuid.dart +45 -0
- package/dart-src/packages/core/pubspec.lock +381 -0
- package/dart-src/packages/core/pubspec.yaml +9 -0
- package/dart-src/packages/core/tool/dbg.dart +6 -0
- package/dart-src/plugins/aws/.gitkeep +0 -0
- package/dart-src/plugins/aws/lib/flutter_patcher_aws.dart +12 -0
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +207 -0
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_signer.dart +130 -0
- package/dart-src/plugins/aws/lib/src/aws_config.dart +59 -0
- package/dart-src/plugins/aws/lib/src/aws_database.dart +198 -0
- package/dart-src/plugins/aws/lib/src/aws_lambda_edge_storage.dart +72 -0
- package/dart-src/plugins/aws/lib/src/aws_s3_client.dart +312 -0
- package/dart-src/plugins/aws/lib/src/aws_ssm_client.dart +125 -0
- package/dart-src/plugins/aws/lib/src/aws_storage.dart +20 -0
- package/dart-src/plugins/aws/lib/src/aws_storage_profile.dart +123 -0
- package/dart-src/plugins/aws/lib/src/with_cloudfront_signed_url.dart +136 -0
- package/dart-src/plugins/aws/pubspec.lock +435 -0
- package/dart-src/plugins/aws/pubspec.yaml +22 -0
- package/dart-src/plugins/cloudflare/.gitkeep +0 -0
- package/dart-src/plugins/cloudflare/lib/flutter_patcher_cloudflare.dart +18 -0
- package/dart-src/plugins/cloudflare/lib/src/cloudflare_worker_database.dart +29 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_build_where.dart +93 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_bundle_mapper.dart +131 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_client.dart +66 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_config.dart +30 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_database.dart +16 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_database_plugin.dart +314 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_config.dart +50 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_s3_client.dart +287 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_storage.dart +23 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_storage_profile.dart +110 -0
- package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +16 -0
- package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +8 -0
- package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +38 -0
- package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +11 -0
- package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +24 -0
- package/dart-src/plugins/cloudflare/pubspec.lock +411 -0
- package/dart-src/plugins/cloudflare/pubspec.yaml +20 -0
- package/dart-src/plugins/cloudflare/sql/bundles.sql +42 -0
- package/dart-src/plugins/plugin-core/lib/flutter_patcher_plugin_core.dart +42 -0
- package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +94 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_storage_layout.dart +44 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work.dart +259 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work_store.dart +20 -0
- package/dart-src/plugins/plugin-core/lib/src/calculate_pagination.dart +29 -0
- package/dart-src/plugins/plugin-core/lib/src/compression_format.dart +63 -0
- package/dart-src/plugins/plugin-core/lib/src/content_addressed_assets.dart +22 -0
- package/dart-src/plugins/plugin-core/lib/src/create_blob_database_plugin.dart +853 -0
- package/dart-src/plugins/plugin-core/lib/src/create_database_plugin.dart +525 -0
- package/dart-src/plugins/plugin-core/lib/src/create_storage_key_builder.dart +15 -0
- package/dart-src/plugins/plugin-core/lib/src/create_storage_plugin.dart +130 -0
- package/dart-src/plugins/plugin-core/lib/src/filter_compatible_app_versions.dart +18 -0
- package/dart-src/plugins/plugin-core/lib/src/generate_min_bundle_id.dart +19 -0
- package/dart-src/plugins/plugin-core/lib/src/get_update_info.dart +241 -0
- package/dart-src/plugins/plugin-core/lib/src/is_object.dart +5 -0
- package/dart-src/plugins/plugin-core/lib/src/legacy_asset_storage_layout.dart +9 -0
- package/dart-src/plugins/plugin-core/lib/src/paginate_bundles.dart +112 -0
- package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +44 -0
- package/dart-src/plugins/plugin-core/lib/src/query_bundles.dart +70 -0
- package/dart-src/plugins/plugin-core/lib/src/request_update_bundle_state.dart +62 -0
- package/dart-src/plugins/plugin-core/lib/src/resolve_update_info_from_bundles.dart +43 -0
- package/dart-src/plugins/plugin-core/lib/src/storage_profile.dart +27 -0
- package/dart-src/plugins/plugin-core/lib/src/types.dart +454 -0
- package/dart-src/plugins/plugin-core/lib/src/uuidv7.dart +41 -0
- package/dart-src/plugins/plugin-core/pubspec.lock +396 -0
- package/dart-src/plugins/plugin-core/pubspec.yaml +15 -0
- package/dart-src/plugins/postgres/.gitkeep +0 -0
- package/dart-src/plugins/postgres/lib/flutter_patcher_postgres.dart +17 -0
- package/dart-src/plugins/postgres/lib/src/postgres_bundle_mapper.dart +130 -0
- package/dart-src/plugins/postgres/lib/src/postgres_client.dart +90 -0
- package/dart-src/plugins/postgres/lib/src/postgres_config.dart +35 -0
- package/dart-src/plugins/postgres/lib/src/postgres_database.dart +302 -0
- package/dart-src/plugins/postgres/lib/src/postgres_get_update_info.dart +120 -0
- package/dart-src/plugins/postgres/lib/src/postgres_storage.dart +205 -0
- package/dart-src/plugins/postgres/lib/src/postgres_types.dart +132 -0
- package/dart-src/plugins/postgres/pubspec.lock +427 -0
- package/dart-src/plugins/postgres/pubspec.yaml +18 -0
- package/dart-src/plugins/postgres/sql/bundles.sql +45 -0
- package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +21 -0
- package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +171 -0
- package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +126 -0
- package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +125 -0
- package/dart-src/plugins/postgres/sql/hash_user_id.sql +30 -0
- package/dart-src/plugins/postgres/sql/is_device_eligible.sql +237 -0
- package/dart-src/plugins/postgres/sql/prepareSql.ts +11 -0
- package/dart-src/plugins/standalone/.gitkeep +0 -0
- package/dart-src/plugins/standalone/lib/flutter_patcher_standalone.dart +12 -0
- package/dart-src/plugins/standalone/lib/src/standalone_config.dart +107 -0
- package/dart-src/plugins/standalone/lib/src/standalone_database.dart +233 -0
- package/dart-src/plugins/standalone/lib/src/standalone_storage.dart +175 -0
- package/dart-src/plugins/standalone/pubspec.lock +411 -0
- package/dart-src/plugins/standalone/pubspec.yaml +18 -0
- package/dart-src/plugins/supabase/lib/edge.dart +10 -0
- package/dart-src/plugins/supabase/lib/flutter_patcher_supabase.dart +21 -0
- package/dart-src/plugins/supabase/lib/src/supabase_bundle_mapper.dart +129 -0
- package/dart-src/plugins/supabase/lib/src/supabase_client_adapter.dart +154 -0
- package/dart-src/plugins/supabase/lib/src/supabase_client_http.dart +516 -0
- package/dart-src/plugins/supabase/lib/src/supabase_config.dart +34 -0
- package/dart-src/plugins/supabase/lib/src/supabase_database.dart +422 -0
- package/dart-src/plugins/supabase/lib/src/supabase_edge_function_database.dart +32 -0
- package/dart-src/plugins/supabase/lib/src/supabase_edge_function_storage.dart +99 -0
- package/dart-src/plugins/supabase/lib/src/supabase_signed_url_batcher.dart +145 -0
- package/dart-src/plugins/supabase/lib/src/supabase_storage.dart +298 -0
- package/dart-src/plugins/supabase/lib/src/types.dart +130 -0
- package/dart-src/plugins/supabase/pubspec.lock +515 -0
- package/dart-src/plugins/supabase/pubspec.yaml +19 -0
- package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +44 -0
- package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +968 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +228 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +134 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +194 -0
- package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +184 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +503 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +50 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +20 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +48 -0
- package/package.json +2 -1
- package/scripts/postinstall.js +32 -23
- /package/bin/{flutter-patcher-linux → flutter-patcher-linux-x64} +0 -0
|
File without changes
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/// flutter_patcher_cli — command-line interface for flutter_patcher.
|
|
2
|
+
///
|
|
3
|
+
/// Faithful Dart port of hot-updater's `@hot-updater/cli`: deploy bundles,
|
|
4
|
+
/// manage channels/rollbacks, run migrations, and inspect the backend.
|
|
5
|
+
library;
|
|
6
|
+
|
|
7
|
+
import 'dart:io';
|
|
8
|
+
|
|
9
|
+
import 'package:args/command_runner.dart';
|
|
10
|
+
|
|
11
|
+
export 'src/backend.dart';
|
|
12
|
+
export 'src/cli_base.dart';
|
|
13
|
+
export 'src/config.dart';
|
|
14
|
+
export 'src/operations.dart';
|
|
15
|
+
export 'src/pack.dart';
|
|
16
|
+
export 'src/sign.dart';
|
|
17
|
+
export 'src/util.dart';
|
|
18
|
+
|
|
19
|
+
export 'src/commands/init.dart';
|
|
20
|
+
export 'src/commands/config_command.dart';
|
|
21
|
+
export 'src/commands/keys.dart';
|
|
22
|
+
export 'src/commands/doctor.dart';
|
|
23
|
+
export 'src/commands/fingerprint.dart';
|
|
24
|
+
export 'src/commands/deploy.dart';
|
|
25
|
+
export 'src/commands/bundle.dart';
|
|
26
|
+
export 'src/commands/build.dart';
|
|
27
|
+
export 'src/commands/rollback.dart';
|
|
28
|
+
export 'src/commands/channel.dart';
|
|
29
|
+
export 'src/commands/migrate.dart';
|
|
30
|
+
export 'src/commands/console.dart';
|
|
31
|
+
|
|
32
|
+
import 'src/commands/build.dart';
|
|
33
|
+
import 'src/commands/bundle.dart';
|
|
34
|
+
import 'src/commands/channel.dart';
|
|
35
|
+
import 'src/commands/config_command.dart';
|
|
36
|
+
import 'src/commands/console.dart';
|
|
37
|
+
import 'src/commands/deploy.dart';
|
|
38
|
+
import 'src/commands/doctor.dart';
|
|
39
|
+
import 'src/commands/fingerprint.dart';
|
|
40
|
+
import 'src/commands/init.dart';
|
|
41
|
+
import 'src/commands/keys.dart';
|
|
42
|
+
import 'src/commands/migrate.dart';
|
|
43
|
+
import 'src/commands/rollback.dart';
|
|
44
|
+
|
|
45
|
+
/// Entry point: build the command runner and dispatch [args].
|
|
46
|
+
Future<int> run(List<String> args) async {
|
|
47
|
+
final runner = CommandRunner<int>(
|
|
48
|
+
'flutter_patcher',
|
|
49
|
+
'flutter_patcher CLI — OTA code push for Flutter (hot-updater compatible).',
|
|
50
|
+
)
|
|
51
|
+
..addCommand(InitCommand())
|
|
52
|
+
..addCommand(ConfigCommand())
|
|
53
|
+
..addCommand(KeysCommand())
|
|
54
|
+
..addCommand(DoctorCommand())
|
|
55
|
+
..addCommand(FingerprintCommand())
|
|
56
|
+
..addCommand(DeployCommand())
|
|
57
|
+
..addCommand(BuildCommand())
|
|
58
|
+
..addCommand(BundleCommand())
|
|
59
|
+
..addCommand(RollbackCommand())
|
|
60
|
+
..addCommand(ChannelCommand())
|
|
61
|
+
..addCommand(MigrateCommand())
|
|
62
|
+
..addCommand(ConsoleCommand());
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
final result = await runner.run(args);
|
|
66
|
+
return result ?? 0;
|
|
67
|
+
} on UsageException catch (e) {
|
|
68
|
+
stderr.writeln(e.message);
|
|
69
|
+
stderr.writeln('');
|
|
70
|
+
stderr.writeln(e.usage);
|
|
71
|
+
return 64;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import 'package:flutter_patcher_aws/flutter_patcher_aws.dart';
|
|
2
|
+
import 'package:flutter_patcher_cloudflare/flutter_patcher_cloudflare.dart';
|
|
3
|
+
import 'package:flutter_patcher_plugin_core/flutter_patcher_plugin_core.dart';
|
|
4
|
+
import 'package:flutter_patcher_postgres/flutter_patcher_postgres.dart';
|
|
5
|
+
import 'package:flutter_patcher_standalone/flutter_patcher_standalone.dart';
|
|
6
|
+
import 'package:flutter_patcher_supabase/flutter_patcher_supabase.dart';
|
|
7
|
+
|
|
8
|
+
import 'config.dart';
|
|
9
|
+
|
|
10
|
+
/// Backend handle wiring the database + node storage profiles used by the CLI.
|
|
11
|
+
class Backend {
|
|
12
|
+
const Backend({
|
|
13
|
+
required this.db,
|
|
14
|
+
required this.storage,
|
|
15
|
+
this.basePath,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
final DatabasePlugin db;
|
|
19
|
+
final NodeStorageProfile storage;
|
|
20
|
+
|
|
21
|
+
/// Optional storage key prefix (e.g. "bundles") honored when building keys.
|
|
22
|
+
final String? basePath;
|
|
23
|
+
|
|
24
|
+
/// Build the storage object key for a bundle artifact.
|
|
25
|
+
String storageKeyFor(String bundleId, String file) {
|
|
26
|
+
final parts = [
|
|
27
|
+
if (basePath != null && basePath!.isNotEmpty) basePath!,
|
|
28
|
+
bundleId,
|
|
29
|
+
file,
|
|
30
|
+
];
|
|
31
|
+
return parts.join('/');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// Construct a [Backend] for the configured provider.
|
|
36
|
+
///
|
|
37
|
+
/// Supports `supabase`, `postgres`, `cloudflare`, and `aws`. Credential
|
|
38
|
+
/// precedence is: explicit factory/flag > environment variable > JSON config
|
|
39
|
+
/// (see the `resolve*Config` helpers in `config.dart`). Test seams let callers
|
|
40
|
+
/// inject mock clients (e.g. `postgresClientFactory`).
|
|
41
|
+
Backend resolveBackend(
|
|
42
|
+
FlutterPatcherConfig config, {
|
|
43
|
+
SupabaseClientFactory? supabaseClientFactory,
|
|
44
|
+
D1ClientFactory? d1ClientFactory,
|
|
45
|
+
R2S3ClientFactory? r2ClientFactory,
|
|
46
|
+
PostgresClientFactory? postgresClientFactory,
|
|
47
|
+
AwsS3ClientLike Function(S3DatabaseConfig config)? awsS3ClientFactory,
|
|
48
|
+
AwsCloudFrontClientLike Function(S3DatabaseConfig config)?
|
|
49
|
+
awsCloudFrontClientFactory,
|
|
50
|
+
AwsS3ClientLike Function(AwsS3StorageConfig config)? awsStorageClientFactory,
|
|
51
|
+
StandaloneClientFactory? standaloneClientFactory,
|
|
52
|
+
}) {
|
|
53
|
+
switch (config.provider) {
|
|
54
|
+
case 'supabase':
|
|
55
|
+
final dbConfig = resolveSupabaseConfig(
|
|
56
|
+
config,
|
|
57
|
+
clientFactory: supabaseClientFactory,
|
|
58
|
+
);
|
|
59
|
+
final storageConfig = resolveSupabaseStorageConfig(
|
|
60
|
+
config,
|
|
61
|
+
clientFactory: supabaseClientFactory,
|
|
62
|
+
);
|
|
63
|
+
final db = supabaseDatabase(dbConfig)();
|
|
64
|
+
final storage = supabaseStorage(storageConfig);
|
|
65
|
+
final node = storage.profiles.node;
|
|
66
|
+
if (node == null) {
|
|
67
|
+
throw StateError('Supabase storage plugin exposes no node profile.');
|
|
68
|
+
}
|
|
69
|
+
return Backend(
|
|
70
|
+
db: db,
|
|
71
|
+
storage: node,
|
|
72
|
+
basePath: storageConfig.basePath,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
case 'postgres':
|
|
76
|
+
final dbConfig = resolvePostgresDatabaseConfig(
|
|
77
|
+
config,
|
|
78
|
+
clientFactory: postgresClientFactory,
|
|
79
|
+
);
|
|
80
|
+
final storageConfig = resolvePostgresStorageConfig(
|
|
81
|
+
config,
|
|
82
|
+
clientFactory: postgresClientFactory,
|
|
83
|
+
);
|
|
84
|
+
final db = postgresDatabase(dbConfig)();
|
|
85
|
+
final storage = postgresStorage(storageConfig);
|
|
86
|
+
final node = storage.profiles.node;
|
|
87
|
+
if (node == null) {
|
|
88
|
+
throw StateError('Postgres storage plugin exposes no node profile.');
|
|
89
|
+
}
|
|
90
|
+
return Backend(
|
|
91
|
+
db: db,
|
|
92
|
+
storage: node,
|
|
93
|
+
basePath: storageConfig.basePath,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
case 'cloudflare':
|
|
97
|
+
final dbConfig = resolveCloudflareDatabaseConfig(
|
|
98
|
+
config,
|
|
99
|
+
clientFactory: d1ClientFactory,
|
|
100
|
+
);
|
|
101
|
+
final storageConfig = resolveCloudflareStorageConfig(
|
|
102
|
+
config,
|
|
103
|
+
clientFactory: r2ClientFactory,
|
|
104
|
+
);
|
|
105
|
+
final db = d1Database(dbConfig)();
|
|
106
|
+
final storage = r2Storage(storageConfig);
|
|
107
|
+
final node = storage.profiles.node;
|
|
108
|
+
if (node == null) {
|
|
109
|
+
throw StateError('R2 storage plugin exposes no node profile.');
|
|
110
|
+
}
|
|
111
|
+
return Backend(
|
|
112
|
+
db: db,
|
|
113
|
+
storage: node,
|
|
114
|
+
basePath: storageConfig.basePath,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
case 'aws':
|
|
118
|
+
final dbConfig = resolveAwsDatabaseConfig(
|
|
119
|
+
config,
|
|
120
|
+
clientFactory: awsS3ClientFactory,
|
|
121
|
+
cloudfrontClientFactory: awsCloudFrontClientFactory,
|
|
122
|
+
);
|
|
123
|
+
final storageConfig = resolveAwsStorageConfig(
|
|
124
|
+
config,
|
|
125
|
+
clientFactory: awsStorageClientFactory,
|
|
126
|
+
);
|
|
127
|
+
final db = s3Database(dbConfig)();
|
|
128
|
+
final storage = s3Storage(storageConfig);
|
|
129
|
+
final node = storage.profiles.node;
|
|
130
|
+
if (node == null) {
|
|
131
|
+
throw StateError('S3 storage plugin exposes no node profile.');
|
|
132
|
+
}
|
|
133
|
+
return Backend(
|
|
134
|
+
db: db,
|
|
135
|
+
storage: node,
|
|
136
|
+
basePath: storageConfig.basePath,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
case 'standalone':
|
|
140
|
+
final dbConfig = resolveStandaloneDatabaseConfig(
|
|
141
|
+
config,
|
|
142
|
+
clientFactory: standaloneClientFactory,
|
|
143
|
+
);
|
|
144
|
+
final storageConfig = resolveStandaloneStorageConfig(
|
|
145
|
+
config,
|
|
146
|
+
clientFactory: standaloneClientFactory,
|
|
147
|
+
);
|
|
148
|
+
final db = standaloneRepository(dbConfig)();
|
|
149
|
+
final storage = standaloneStorage(storageConfig);
|
|
150
|
+
final node = storage.profiles.node;
|
|
151
|
+
if (node == null) {
|
|
152
|
+
throw StateError('Standalone storage plugin exposes no node profile.');
|
|
153
|
+
}
|
|
154
|
+
return Backend(db: db, storage: node);
|
|
155
|
+
|
|
156
|
+
default:
|
|
157
|
+
throw UnsupportedError(
|
|
158
|
+
'Provider "${config.provider}" is not supported. '
|
|
159
|
+
'Supported: supabase, postgres, cloudflare, aws, standalone.',
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import 'dart:io';
|
|
2
|
+
|
|
3
|
+
import 'package:args/args.dart';
|
|
4
|
+
|
|
5
|
+
import 'backend.dart';
|
|
6
|
+
import 'config.dart';
|
|
7
|
+
|
|
8
|
+
/// Run [body], mapping errors to a non-zero exit code with a clean message.
|
|
9
|
+
|
|
10
|
+
/// Run [body], mapping errors to a non-zero exit code with a clean message.
|
|
11
|
+
Future<int> runGuarded(Future<void> Function() body) async {
|
|
12
|
+
try {
|
|
13
|
+
await body();
|
|
14
|
+
return 0;
|
|
15
|
+
} on StateError catch (e) {
|
|
16
|
+
stderr.writeln('error: ${e.message}');
|
|
17
|
+
return 1;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
stderr.writeln('error: $e');
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// Resolve a [Backend] from config, or return the injected override (tests).
|
|
25
|
+
Backend requireBackend(FlutterPatcherConfig? config, {Backend? override}) {
|
|
26
|
+
if (override != null) return override;
|
|
27
|
+
if (config == null) {
|
|
28
|
+
throw StateError(
|
|
29
|
+
'No configuration found. Run `flutter_patcher init` or set '
|
|
30
|
+
'SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY.',
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return resolveBackend(config);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Apply a `--backend` flag override to the loaded config.
|
|
37
|
+
///
|
|
38
|
+
/// Returns `cfg` unchanged when no `--backend` flag is present. When the flag
|
|
39
|
+
/// is set but no config exists yet, a minimal config with that provider is
|
|
40
|
+
/// created (so env-driven credentials still resolve).
|
|
41
|
+
FlutterPatcherConfig? effectiveConfig(
|
|
42
|
+
FlutterPatcherConfig? cfg,
|
|
43
|
+
ArgResults argResults,
|
|
44
|
+
) {
|
|
45
|
+
final backend = argResults['backend'] as String?;
|
|
46
|
+
if (backend == null) return cfg;
|
|
47
|
+
final base = cfg ??
|
|
48
|
+
FlutterPatcherConfig(
|
|
49
|
+
provider: backend,
|
|
50
|
+
supabase: const SupabaseConfigJson(),
|
|
51
|
+
postgres: const PostgresConfigJson(),
|
|
52
|
+
cloudflare: const CloudflareConfigJson(),
|
|
53
|
+
aws: const AwsConfigJson(),
|
|
54
|
+
);
|
|
55
|
+
return base.copyWith(provider: backend);
|
|
56
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import 'dart:io';
|
|
2
|
+
|
|
3
|
+
import 'package:args/command_runner.dart';
|
|
4
|
+
import 'package:flutter_patcher_cli/flutter_patcher_cli.dart';
|
|
5
|
+
|
|
6
|
+
/// `flutter_patcher build` — package a release APK into a device-compatible
|
|
7
|
+
/// OTA patch (`dist/patch.zip`) that the `deploy` command can publish.
|
|
8
|
+
///
|
|
9
|
+
/// Includes every ABI found in the APK so a single bundle installs on all
|
|
10
|
+
/// device architectures.
|
|
11
|
+
class BuildCommand extends Command<int> {
|
|
12
|
+
BuildCommand() {
|
|
13
|
+
argParser
|
|
14
|
+
..addOption(
|
|
15
|
+
'apk',
|
|
16
|
+
abbr: 'a',
|
|
17
|
+
help: 'Path to the release APK to extract libapp.so and assets from.',
|
|
18
|
+
)
|
|
19
|
+
..addMultiOption(
|
|
20
|
+
'assets',
|
|
21
|
+
help: 'Asset keys to include as overlay updates (repeatable).',
|
|
22
|
+
)
|
|
23
|
+
..addOption(
|
|
24
|
+
'version',
|
|
25
|
+
help: 'Patch version string (stored in manifest.version).',
|
|
26
|
+
)
|
|
27
|
+
..addOption(
|
|
28
|
+
'target-version-code',
|
|
29
|
+
help: 'Host APK versionCode this patch targets (integer).',
|
|
30
|
+
)
|
|
31
|
+
..addOption(
|
|
32
|
+
'abi',
|
|
33
|
+
help: 'Preferred ABI to report (default: all ABIs found in APK).',
|
|
34
|
+
)
|
|
35
|
+
..addOption(
|
|
36
|
+
'out',
|
|
37
|
+
abbr: 'o',
|
|
38
|
+
defaultsTo: 'dist',
|
|
39
|
+
help: 'Output directory for patch.zip and manifest.json.',
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@override
|
|
44
|
+
String get name => 'build';
|
|
45
|
+
|
|
46
|
+
@override
|
|
47
|
+
String get description =>
|
|
48
|
+
'Build a device-compatible OTA patch (patch.zip) from a release APK.';
|
|
49
|
+
|
|
50
|
+
@override
|
|
51
|
+
Future<int> run() async {
|
|
52
|
+
final results = argResults!;
|
|
53
|
+
final apk = results['apk'] as String?;
|
|
54
|
+
if (apk == null || apk.isEmpty) {
|
|
55
|
+
stderr.writeln('error: missing required --apk <path>');
|
|
56
|
+
return 64;
|
|
57
|
+
}
|
|
58
|
+
final version = results['version'] as String?;
|
|
59
|
+
if (version == null || version.isEmpty) {
|
|
60
|
+
stderr.writeln('error: missing required --version <string>');
|
|
61
|
+
return 64;
|
|
62
|
+
}
|
|
63
|
+
final targetVersionCodeRaw = results['target-version-code'] as String?;
|
|
64
|
+
if (targetVersionCodeRaw == null || targetVersionCodeRaw.isEmpty) {
|
|
65
|
+
stderr.writeln('error: missing required --target-version-code <int>');
|
|
66
|
+
return 64;
|
|
67
|
+
}
|
|
68
|
+
final targetVersionCode = int.tryParse(targetVersionCodeRaw);
|
|
69
|
+
if (targetVersionCode == null) {
|
|
70
|
+
stderr.writeln('error: --target-version-code must be an integer');
|
|
71
|
+
return 64;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
final result = await packPatch(
|
|
76
|
+
apkPath: apk,
|
|
77
|
+
version: version,
|
|
78
|
+
targetVersionCode: targetVersionCode,
|
|
79
|
+
abi: results['abi'] as String?,
|
|
80
|
+
requestedAssets: results['assets'] as List<String>,
|
|
81
|
+
out: results['out'] as String,
|
|
82
|
+
);
|
|
83
|
+
stdout.writeln('[build] version: ${result.version}');
|
|
84
|
+
stdout.writeln('[build] targetVersionCode: ${result.targetVersionCode}');
|
|
85
|
+
stdout.writeln('[build] abis: ${result.abis.join(', ')}');
|
|
86
|
+
stdout.writeln('[build] bundle md5: ${result.md5}');
|
|
87
|
+
stdout.writeln('[build] overlay assets: ${result.assetCount}');
|
|
88
|
+
stdout.writeln('[build] payload: ${result.payloadPath}');
|
|
89
|
+
stdout.writeln('[build] manifest: ${result.manifestPath}');
|
|
90
|
+
return 0;
|
|
91
|
+
} on PackException catch (e) {
|
|
92
|
+
stderr.writeln('error: ${e.message}');
|
|
93
|
+
return e.exitCode;
|
|
94
|
+
} on Object catch (e) {
|
|
95
|
+
stderr.writeln('error: $e');
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import 'dart:io';
|
|
2
|
+
|
|
3
|
+
import 'package:args/args.dart';
|
|
4
|
+
import 'package:args/command_runner.dart';
|
|
5
|
+
|
|
6
|
+
import '../backend.dart';
|
|
7
|
+
import '../cli_base.dart';
|
|
8
|
+
import '../config.dart';
|
|
9
|
+
import '../operations.dart';
|
|
10
|
+
|
|
11
|
+
/// `flutter_patcher bundle` — manage bundles.
|
|
12
|
+
class BundleCommand extends Command<int> {
|
|
13
|
+
BundleCommand({this.config, this.backendOverride});
|
|
14
|
+
|
|
15
|
+
final FlutterPatcherConfig? config;
|
|
16
|
+
final Backend? backendOverride;
|
|
17
|
+
|
|
18
|
+
@override
|
|
19
|
+
String get name => 'bundle';
|
|
20
|
+
|
|
21
|
+
@override
|
|
22
|
+
String get description => 'Manage bundles (list / delete / promote).';
|
|
23
|
+
|
|
24
|
+
@override
|
|
25
|
+
Future<int> run() => runGuarded(() async {
|
|
26
|
+
print(description);
|
|
27
|
+
print('');
|
|
28
|
+
print('Subcommands:');
|
|
29
|
+
print(' list List bundles');
|
|
30
|
+
print(' delete <id> Delete a bundle');
|
|
31
|
+
print(' promote <id> -c <c> Promote a bundle to a channel');
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class BundleListCommand extends Command<int> {
|
|
36
|
+
BundleListCommand({this.config, this.backendOverride});
|
|
37
|
+
|
|
38
|
+
final FlutterPatcherConfig? config;
|
|
39
|
+
final Backend? backendOverride;
|
|
40
|
+
|
|
41
|
+
@override
|
|
42
|
+
String get name => 'list';
|
|
43
|
+
|
|
44
|
+
@override
|
|
45
|
+
String get description => 'List bundles.';
|
|
46
|
+
|
|
47
|
+
@override
|
|
48
|
+
ArgParser get argParser => ArgParser()
|
|
49
|
+
..addOption('backend', abbr: 'b', help: 'Backend provider.')
|
|
50
|
+
..addOption('channel', abbr: 'c', help: 'Filter by channel.')
|
|
51
|
+
..addOption('platform', abbr: 'p', help: 'Filter by platform.')
|
|
52
|
+
..addOption('enabled', help: 'Filter by enabled (true/false).')
|
|
53
|
+
..addOption('limit', abbr: 'l', defaultsTo: '20', help: 'Page size.');
|
|
54
|
+
|
|
55
|
+
@override
|
|
56
|
+
Future<int> run() => runGuarded(() async {
|
|
57
|
+
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
58
|
+
final backend = requireBackend(cfg, override: backendOverride);
|
|
59
|
+
final enabledRaw = argResults!['enabled'] as String?;
|
|
60
|
+
final enabled = enabledRaw == null
|
|
61
|
+
? null
|
|
62
|
+
: (enabledRaw == 'true' || enabledRaw == '1');
|
|
63
|
+
final res = await listBundles(
|
|
64
|
+
backend,
|
|
65
|
+
ListOptions(
|
|
66
|
+
channel: argResults!['channel'] as String?,
|
|
67
|
+
platform: argResults!['platform'] as String?,
|
|
68
|
+
enabled: enabled,
|
|
69
|
+
limit: int.parse(argResults!['limit'] as String),
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
if (res.data.isEmpty) {
|
|
73
|
+
stdout.writeln('(no bundles)');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
stdout.writeln('id channel '
|
|
77
|
+
'enabled platform target');
|
|
78
|
+
for (final b in res.data) {
|
|
79
|
+
final target = b.targetAppVersion ?? b.fingerprintHash ?? '-';
|
|
80
|
+
stdout.writeln('${b.id} ${b.channel.padRight(12)} '
|
|
81
|
+
'${(b.enabled ? 'yes' : 'no').padRight(7)} '
|
|
82
|
+
'${b.platform.value.padRight(7)} $target');
|
|
83
|
+
}
|
|
84
|
+
stdout.writeln('');
|
|
85
|
+
stdout.writeln('total: ${res.pagination.total}');
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
class BundleDeleteCommand extends Command<int> {
|
|
90
|
+
BundleDeleteCommand({this.config, this.backendOverride});
|
|
91
|
+
|
|
92
|
+
final FlutterPatcherConfig? config;
|
|
93
|
+
final Backend? backendOverride;
|
|
94
|
+
|
|
95
|
+
@override
|
|
96
|
+
String get name => 'delete';
|
|
97
|
+
|
|
98
|
+
@override
|
|
99
|
+
String get description => 'Delete a bundle by id.';
|
|
100
|
+
|
|
101
|
+
@override
|
|
102
|
+
ArgParser get argParser => ArgParser()
|
|
103
|
+
..addOption('backend', abbr: 'b', help: 'Backend provider.')
|
|
104
|
+
..addOption('id', help: 'Bundle id.');
|
|
105
|
+
|
|
106
|
+
@override
|
|
107
|
+
Future<int> run() => runGuarded(() async {
|
|
108
|
+
final id = argResults!['id'] as String?;
|
|
109
|
+
if (id == null || id.isEmpty) {
|
|
110
|
+
throw StateError('Usage: flutter_patcher bundle delete <id>');
|
|
111
|
+
}
|
|
112
|
+
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
113
|
+
final backend = requireBackend(cfg, override: backendOverride);
|
|
114
|
+
await deleteBundle(backend, id);
|
|
115
|
+
stdout.writeln('Deleted bundle $id');
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
class BundlePromoteCommand extends Command<int> {
|
|
120
|
+
BundlePromoteCommand({this.config, this.backendOverride});
|
|
121
|
+
|
|
122
|
+
final FlutterPatcherConfig? config;
|
|
123
|
+
final Backend? backendOverride;
|
|
124
|
+
|
|
125
|
+
@override
|
|
126
|
+
String get name => 'promote';
|
|
127
|
+
|
|
128
|
+
@override
|
|
129
|
+
String get description => 'Promote a bundle to a channel.';
|
|
130
|
+
|
|
131
|
+
@override
|
|
132
|
+
ArgParser get argParser => ArgParser()
|
|
133
|
+
..addOption('backend', abbr: 'b', help: 'Backend provider.')
|
|
134
|
+
..addOption('id', help: 'Bundle id.')
|
|
135
|
+
..addOption('channel', abbr: 'c', help: 'Target channel.');
|
|
136
|
+
|
|
137
|
+
@override
|
|
138
|
+
Future<int> run() => runGuarded(() async {
|
|
139
|
+
final id = argResults!['id'] as String?;
|
|
140
|
+
final channel = argResults!['channel'] as String?;
|
|
141
|
+
if (id == null || id.isEmpty || channel == null || channel.isEmpty) {
|
|
142
|
+
throw StateError(
|
|
143
|
+
'Usage: flutter_patcher bundle promote <id> --channel <channel>',
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
147
|
+
final backend = requireBackend(cfg, override: backendOverride);
|
|
148
|
+
await promoteBundle(backend, id, channel);
|
|
149
|
+
stdout.writeln('Promoted bundle $id to channel $channel');
|
|
150
|
+
});
|
|
151
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import 'dart:io';
|
|
2
|
+
|
|
3
|
+
import 'package:args/args.dart';
|
|
4
|
+
import 'package:args/command_runner.dart';
|
|
5
|
+
|
|
6
|
+
import '../backend.dart';
|
|
7
|
+
import '../cli_base.dart';
|
|
8
|
+
import '../config.dart';
|
|
9
|
+
import '../operations.dart';
|
|
10
|
+
|
|
11
|
+
/// `flutter_patcher channel` — manage channels.
|
|
12
|
+
class ChannelCommand extends Command<int> {
|
|
13
|
+
final FlutterPatcherConfig? config;
|
|
14
|
+
final Backend? backendOverride;
|
|
15
|
+
|
|
16
|
+
ChannelCommand({this.config, this.backendOverride}) {
|
|
17
|
+
addSubcommand(ChannelListCommand(config: config, backendOverride: backendOverride));
|
|
18
|
+
addSubcommand(ChannelGetCommand(config: config, backendOverride: backendOverride));
|
|
19
|
+
addSubcommand(ChannelSetCommand(config: config, backendOverride: backendOverride));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@override
|
|
23
|
+
String get name => 'channel';
|
|
24
|
+
|
|
25
|
+
@override
|
|
26
|
+
String get description => 'Manage channels (list / get / set).';
|
|
27
|
+
|
|
28
|
+
@override
|
|
29
|
+
Future<int> run() => runGuarded(() async {
|
|
30
|
+
print(description);
|
|
31
|
+
print('');
|
|
32
|
+
print('Subcommands:');
|
|
33
|
+
print(' list List channels');
|
|
34
|
+
print(' get <channel> Show the live bundle for a channel');
|
|
35
|
+
print(' set <c> <id> Promote a bundle to a channel');
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class ChannelListCommand extends Command<int> {
|
|
40
|
+
ChannelListCommand({this.config, this.backendOverride});
|
|
41
|
+
|
|
42
|
+
final FlutterPatcherConfig? config;
|
|
43
|
+
final Backend? backendOverride;
|
|
44
|
+
|
|
45
|
+
@override
|
|
46
|
+
String get name => 'list';
|
|
47
|
+
|
|
48
|
+
@override
|
|
49
|
+
String get description => 'List channels.';
|
|
50
|
+
|
|
51
|
+
@override
|
|
52
|
+
ArgParser get argParser => ArgParser()
|
|
53
|
+
..addOption('backend', abbr: 'b', help: 'Backend provider.');
|
|
54
|
+
|
|
55
|
+
@override
|
|
56
|
+
Future<int> run() => runGuarded(() async {
|
|
57
|
+
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
58
|
+
final backend = requireBackend(cfg, override: backendOverride);
|
|
59
|
+
final channels = await listChannels(backend);
|
|
60
|
+
if (channels.isEmpty) {
|
|
61
|
+
stdout.writeln('(no channels)');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
for (final c in channels) {
|
|
65
|
+
stdout.writeln(' $c');
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class ChannelGetCommand extends Command<int> {
|
|
71
|
+
ChannelGetCommand({this.config, this.backendOverride});
|
|
72
|
+
|
|
73
|
+
final FlutterPatcherConfig? config;
|
|
74
|
+
final Backend? backendOverride;
|
|
75
|
+
|
|
76
|
+
@override
|
|
77
|
+
String get name => 'get';
|
|
78
|
+
|
|
79
|
+
@override
|
|
80
|
+
String get description => 'Show the live (enabled) bundle for a channel.';
|
|
81
|
+
|
|
82
|
+
@override
|
|
83
|
+
ArgParser get argParser => ArgParser()
|
|
84
|
+
..addOption('backend', abbr: 'b', help: 'Backend provider.')
|
|
85
|
+
..addOption('channel', help: 'Channel.');
|
|
86
|
+
|
|
87
|
+
@override
|
|
88
|
+
Future<int> run() => runGuarded(() async {
|
|
89
|
+
final channel = argResults!['channel'] as String?;
|
|
90
|
+
if (channel == null || channel.isEmpty) {
|
|
91
|
+
throw StateError('Usage: flutter_patcher channel get <channel>');
|
|
92
|
+
}
|
|
93
|
+
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
94
|
+
final backend = requireBackend(cfg, override: backendOverride);
|
|
95
|
+
final bundle = await getChannel(backend, channel);
|
|
96
|
+
if (bundle == null) {
|
|
97
|
+
stdout.writeln('(no live bundle on channel "$channel")');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
stdout.writeln('channel "$channel" -> bundle ${bundle.id} '
|
|
101
|
+
'(${bundle.platform.value})');
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
class ChannelSetCommand extends Command<int> {
|
|
106
|
+
ChannelSetCommand({this.config, this.backendOverride});
|
|
107
|
+
|
|
108
|
+
final FlutterPatcherConfig? config;
|
|
109
|
+
final Backend? backendOverride;
|
|
110
|
+
|
|
111
|
+
@override
|
|
112
|
+
String get name => 'set';
|
|
113
|
+
|
|
114
|
+
@override
|
|
115
|
+
String get description => 'Promote a bundle to a channel.';
|
|
116
|
+
|
|
117
|
+
@override
|
|
118
|
+
ArgParser get argParser => ArgParser()
|
|
119
|
+
..addOption('backend', abbr: 'b', help: 'Backend provider.')
|
|
120
|
+
..addOption('channel', help: 'Channel.')
|
|
121
|
+
..addOption('bundle-id', help: 'Bundle id.');
|
|
122
|
+
|
|
123
|
+
@override
|
|
124
|
+
Future<int> run() => runGuarded(() async {
|
|
125
|
+
final channel = argResults!['channel'] as String?;
|
|
126
|
+
final bundleId = argResults!['bundle-id'] as String?;
|
|
127
|
+
if (channel == null ||
|
|
128
|
+
channel.isEmpty ||
|
|
129
|
+
bundleId == null ||
|
|
130
|
+
bundleId.isEmpty) {
|
|
131
|
+
throw StateError(
|
|
132
|
+
'Usage: flutter_patcher channel set --channel <c> --bundle-id <id>',
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
final cfg = effectiveConfig(config ?? loadConfig(), argResults!);
|
|
136
|
+
final backend = requireBackend(cfg, override: backendOverride);
|
|
137
|
+
await promoteBundle(backend, bundleId, channel);
|
|
138
|
+
stdout.writeln('Channel "$channel" -> bundle $bundleId');
|
|
139
|
+
});
|
|
140
|
+
}
|