From 990a1e43add22e0e1cc723ddaa76af254d862432 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 21 Jun 2026 23:35:33 +0800 Subject: [PATCH 01/72] feat!: rewrite getter as package-centric CLI core Replace the old hub-app centered implementation with the reusable rewrite spine for the UpgradeAll architecture. Add split core/storage/CLI crates, Lua repository evaluation, SQLite main/cache storage, repository overlay behavior, and legacy JSON bridge-bundle import coverage. --- Cargo.lock | 2555 +++++------------ Cargo.toml | 65 +- README.md | 26 +- crates/getter-cli/Cargo.toml | 21 + crates/getter-cli/src/lib.rs | 817 ++++++ crates/getter-cli/src/main.rs | 10 + crates/getter-cli/tests/bdd_cli.rs | 486 ++++ .../tests/features/cli/app_list.feature | 8 + .../tests/features/cli/hub_list.feature | 8 + .../tests/features/cli/init.feature | 8 + .../legacy_import_room_bundle_failure.feature | 18 + .../tests/features/cli/repo_list.feature | 8 + .../features/cli/repository_overlay.feature | 14 + .../cli/repository_package_eval.feature | 17 + .../features/cli/storage_validate.feature | 8 + crates/getter-core/Cargo.toml | 14 + crates/getter-core/src/lib.rs | 408 +++ crates/getter-core/src/lua.rs | 460 +++ crates/getter-core/src/repository.rs | 288 ++ crates/getter-downloader/Cargo.toml | 7 + crates/getter-downloader/src/lib.rs | 3 + crates/getter-ffi/Cargo.toml | 7 + crates/getter-ffi/src/lib.rs | 3 + crates/getter-plugin-api/Cargo.toml | 7 + crates/getter-plugin-api/src/lib.rs | 3 + crates/getter-providers/Cargo.toml | 7 + crates/getter-providers/src/lib.rs | 3 + crates/getter-rpc/Cargo.toml | 7 + crates/getter-rpc/src/lib.rs | 3 + crates/getter-storage/Cargo.toml | 12 + crates/getter-storage/src/legacy_room.rs | 215 ++ crates/getter-storage/src/lib.rs | 491 ++++ src/cache.rs | 34 - src/cache/local.rs | 77 - src/cache/manager.rs | 265 -- src/cache/moka.rs | 0 src/core.rs | 1 - src/core/config.rs | 3 - src/core/config/data.rs | 1 - src/core/config/data/rule_list.rs | 89 - src/core/config/utils.rs | 41 - src/core/config/world.rs | 35 - src/core/config/world/local_repo.rs | 31 - src/core/config/world/world_config_wrapper.rs | 71 - src/core/config/world/world_list.rs | 100 - src/database/mod.rs | 229 -- src/database/models/app.rs | 101 - src/database/models/extra_app.rs | 48 - src/database/models/extra_hub.rs | 56 - src/database/models/hub.rs | 80 - src/database/models/mod.rs | 4 - src/database/store.rs | 266 -- src/downloader/config.rs | 199 -- src/downloader/error.rs | 95 - src/downloader/external_rpc_impl.rs | 290 -- src/downloader/hub_dispatch.rs | 208 -- src/downloader/mod.rs | 58 - src/downloader/state.rs | 532 ---- src/downloader/task_manager.rs | 1009 ------- src/downloader/traits.rs | 262 -- src/downloader/trauma_impl.rs | 751 ----- src/error.rs | 82 - src/lib.rs | 76 +- src/locale.rs | 64 - src/main.rs | 16 +- src/manager/android_api.rs | 104 - src/manager/app_manager.rs | 462 --- src/manager/app_status.rs | 18 - src/manager/auto_template.rs | 206 -- src/manager/cloud_config_getter.rs | 403 --- src/manager/data_getter.rs | 99 - src/manager/hub_manager.rs | 173 -- src/manager/mod.rs | 12 - src/manager/notification.rs | 152 - src/manager/updater.rs | 151 - src/manager/url_replace.rs | 170 -- src/manager/version.rs | 136 - src/manager/version_map.rs | 192 -- src/rpc.rs | 3 - src/rpc/client.rs | 77 - src/rpc/data.rs | 411 --- src/rpc/server.rs | 1255 -------- src/utils.rs | 6 - src/utils/convert.rs | 21 - src/utils/http.rs | 327 --- src/utils/instance.rs | 18 - src/utils/json.rs | 31 - src/utils/time.rs | 8 - src/utils/versioning.rs | 170 -- src/websdk.rs | 2 - src/websdk/cloud_rules.rs | 3 - src/websdk/cloud_rules/cloud_rules_manager.rs | 112 - src/websdk/cloud_rules/cloud_rules_wrapper.rs | 109 - src/websdk/cloud_rules/data.rs | 3 - src/websdk/cloud_rules/data/app_item.rs | 94 - src/websdk/cloud_rules/data/config_list.rs | 80 - src/websdk/cloud_rules/data/hub_item.rs | 123 - src/websdk/repo.rs | 3 - src/websdk/repo/api.rs | 166 -- src/websdk/repo/controller.rs | 121 - src/websdk/repo/data.rs | 1 - src/websdk/repo/data/release.rs | 17 - src/websdk/repo/provider.rs | 99 - src/websdk/repo/provider/base_provider.rs | 427 --- src/websdk/repo/provider/fdroid.rs | 356 --- src/websdk/repo/provider/github.rs | 262 -- src/websdk/repo/provider/gitlab.rs | 327 --- src/websdk/repo/provider/lsposed_repo.rs | 224 -- src/websdk/repo/provider/outside_rpc.rs | 115 - 109 files changed, 4237 insertions(+), 14193 deletions(-) create mode 100644 crates/getter-cli/Cargo.toml create mode 100644 crates/getter-cli/src/lib.rs create mode 100644 crates/getter-cli/src/main.rs create mode 100644 crates/getter-cli/tests/bdd_cli.rs create mode 100644 crates/getter-cli/tests/features/cli/app_list.feature create mode 100644 crates/getter-cli/tests/features/cli/hub_list.feature create mode 100644 crates/getter-cli/tests/features/cli/init.feature create mode 100644 crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature create mode 100644 crates/getter-cli/tests/features/cli/repo_list.feature create mode 100644 crates/getter-cli/tests/features/cli/repository_overlay.feature create mode 100644 crates/getter-cli/tests/features/cli/repository_package_eval.feature create mode 100644 crates/getter-cli/tests/features/cli/storage_validate.feature create mode 100644 crates/getter-core/Cargo.toml create mode 100644 crates/getter-core/src/lib.rs create mode 100644 crates/getter-core/src/lua.rs create mode 100644 crates/getter-core/src/repository.rs create mode 100644 crates/getter-downloader/Cargo.toml create mode 100644 crates/getter-downloader/src/lib.rs create mode 100644 crates/getter-ffi/Cargo.toml create mode 100644 crates/getter-ffi/src/lib.rs create mode 100644 crates/getter-plugin-api/Cargo.toml create mode 100644 crates/getter-plugin-api/src/lib.rs create mode 100644 crates/getter-providers/Cargo.toml create mode 100644 crates/getter-providers/src/lib.rs create mode 100644 crates/getter-rpc/Cargo.toml create mode 100644 crates/getter-rpc/src/lib.rs create mode 100644 crates/getter-storage/Cargo.toml create mode 100644 crates/getter-storage/src/legacy_room.rs create mode 100644 crates/getter-storage/src/lib.rs delete mode 100644 src/cache.rs delete mode 100644 src/cache/local.rs delete mode 100644 src/cache/manager.rs delete mode 100644 src/cache/moka.rs delete mode 100644 src/core.rs delete mode 100644 src/core/config.rs delete mode 100644 src/core/config/data.rs delete mode 100644 src/core/config/data/rule_list.rs delete mode 100644 src/core/config/utils.rs delete mode 100644 src/core/config/world.rs delete mode 100644 src/core/config/world/local_repo.rs delete mode 100644 src/core/config/world/world_config_wrapper.rs delete mode 100644 src/core/config/world/world_list.rs delete mode 100644 src/database/mod.rs delete mode 100644 src/database/models/app.rs delete mode 100644 src/database/models/extra_app.rs delete mode 100644 src/database/models/extra_hub.rs delete mode 100644 src/database/models/hub.rs delete mode 100644 src/database/models/mod.rs delete mode 100644 src/database/store.rs delete mode 100644 src/downloader/config.rs delete mode 100644 src/downloader/error.rs delete mode 100644 src/downloader/external_rpc_impl.rs delete mode 100644 src/downloader/hub_dispatch.rs delete mode 100644 src/downloader/mod.rs delete mode 100644 src/downloader/state.rs delete mode 100644 src/downloader/task_manager.rs delete mode 100644 src/downloader/traits.rs delete mode 100644 src/downloader/trauma_impl.rs delete mode 100644 src/error.rs delete mode 100644 src/locale.rs delete mode 100644 src/manager/android_api.rs delete mode 100644 src/manager/app_manager.rs delete mode 100644 src/manager/app_status.rs delete mode 100644 src/manager/auto_template.rs delete mode 100644 src/manager/cloud_config_getter.rs delete mode 100644 src/manager/data_getter.rs delete mode 100644 src/manager/hub_manager.rs delete mode 100644 src/manager/mod.rs delete mode 100644 src/manager/notification.rs delete mode 100644 src/manager/updater.rs delete mode 100644 src/manager/url_replace.rs delete mode 100644 src/manager/version.rs delete mode 100644 src/manager/version_map.rs delete mode 100644 src/rpc.rs delete mode 100644 src/rpc/client.rs delete mode 100644 src/rpc/data.rs delete mode 100644 src/rpc/server.rs delete mode 100644 src/utils.rs delete mode 100644 src/utils/convert.rs delete mode 100644 src/utils/http.rs delete mode 100644 src/utils/instance.rs delete mode 100644 src/utils/json.rs delete mode 100644 src/utils/time.rs delete mode 100644 src/utils/versioning.rs delete mode 100644 src/websdk.rs delete mode 100644 src/websdk/cloud_rules.rs delete mode 100644 src/websdk/cloud_rules/cloud_rules_manager.rs delete mode 100644 src/websdk/cloud_rules/cloud_rules_wrapper.rs delete mode 100644 src/websdk/cloud_rules/data.rs delete mode 100644 src/websdk/cloud_rules/data/app_item.rs delete mode 100644 src/websdk/cloud_rules/data/config_list.rs delete mode 100644 src/websdk/cloud_rules/data/hub_item.rs delete mode 100644 src/websdk/repo.rs delete mode 100644 src/websdk/repo/api.rs delete mode 100644 src/websdk/repo/controller.rs delete mode 100644 src/websdk/repo/data.rs delete mode 100644 src/websdk/repo/data/release.rs delete mode 100644 src/websdk/repo/provider.rs delete mode 100644 src/websdk/repo/provider/base_provider.rs delete mode 100644 src/websdk/repo/provider/fdroid.rs delete mode 100644 src/websdk/repo/provider/github.rs delete mode 100644 src/websdk/repo/provider/gitlab.rs delete mode 100644 src/websdk/repo/provider/lsposed_repo.rs delete mode 100644 src/websdk/repo/provider/outside_rpc.rs diff --git a/Cargo.lock b/Cargo.lock index 0b56aaf..5d73f4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -12,131 +24,102 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" - -[[package]] -name = "assert-json-diff" -version = "2.0.2" +name = "anstream" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ - "serde", - "serde_json", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", ] [[package]] -name = "async-trait" -version = "0.1.89" +name = "anstyle" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] -name = "async_fn_traits" -version = "0.1.1" +name = "anstyle-parse" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc58d489c5f2d2c5be31b9004cec7af25a70d23df4d8111715eee736234cf217" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ - "paste", + "utf8parse", ] [[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "aws-lc-rs" -version = "1.15.2" +name = "anstyle-query" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "aws-lc-sys", - "zeroize", + "windows-sys 0.61.2", ] [[package]] -name = "aws-lc-sys" -version = "0.35.0" +name = "anstyle-wincon" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", ] [[package]] -name = "base64" -version = "0.22.1" +name = "anyhow" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] -name = "bindgen" -version = "0.72.1" +name = "autocfg" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "itertools", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", -] +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] -name = "block-buffer" -version = "0.10.4" +name = "bstr" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ - "generic-array", + "memchr", + "serde", ] [[package]] -name = "bumpalo" -version = "3.19.1" +name = "bytecount" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "cc" -version = "1.2.52" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", - "jobserver", - "libc", "shlex", ] @@ -146,15 +129,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -162,50 +136,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "cfg_aliases" -version = "0.2.1" +name = "clap" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] [[package]] -name = "chacha20" -version = "0.10.0" +name = "clap_builder" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "rand_core 0.10.0", + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", ] [[package]] -name = "clang-sys" -version = "1.8.1" +name = "clap_derive" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ - "glob", - "libc", - "libloading", + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "cmake" -version = "0.1.57" +name = "clap_lex" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" -dependencies = [ - "cc", -] +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] -name = "colored" -version = "3.0.0" +name = "colorchoice" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" -dependencies = [ - "windows-sys 0.52.0", -] +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "combine" @@ -217,6 +192,27 @@ dependencies = [ "memchr", ] +[[package]] +name = "console" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +dependencies = [ + "encode_unicode", + "libc", + "unicode-width", + "windows-sys 0.61.2", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -234,138 +230,167 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "cpufeatures" -version = "0.2.17" +name = "crossbeam-deque" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "libc", + "crossbeam-epoch", + "crossbeam-utils", ] [[package]] -name = "cpufeatures" -version = "0.3.0" +name = "crossbeam-epoch" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "libc", + "crossbeam-utils", ] [[package]] -name = "crypto-common" -version = "0.1.7" +name = "crossbeam-utils" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] -name = "digest" -version = "0.10.7" +name = "cucumber" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "96a87e18d925b19ebe0fd47ea45316abd216d81ec0879c2448c3f9a0e9da62be" dependencies = [ - "block-buffer", - "crypto-common", + "anyhow", + "clap", + "console", + "cucumber-codegen", + "cucumber-expressions", + "derive_more", + "either", + "futures", + "gherkin", + "globwalk", + "humantime", + "inventory", + "itertools", + "linked-hash-map", + "pin-project", + "ref-cast", + "regex", + "sealed", + "smart-default", ] [[package]] -name = "displaydoc" -version = "0.2.5" +name = "cucumber-codegen" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "ed2fc8a8bbb73af3230db699e8690c5c786655f75eb89e5f18d76055fa1a9a4d" dependencies = [ + "cucumber-expressions", + "inflections", + "itertools", "proc-macro2", "quote", + "regex", "syn", + "synthez", ] [[package]] -name = "dunce" -version = "1.0.5" +name = "cucumber-expressions" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +checksum = "6401038de3af44fe74e6fccdb8a5b7db7ba418f480c8e9ad584c6f65c05a27a6" +dependencies = [ + "derive_more", + "either", + "nom", + "nom_locate", + "regex", + "regex-syntax", +] [[package]] -name = "either" -version = "1.15.0" +name = "derive_more" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] [[package]] -name = "encoding_rs" -version = "0.8.35" +name = "derive_more-impl" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "cfg-if", + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", + "unicode-xid", ] [[package]] -name = "equivalent" -version = "1.0.2" +name = "either" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] -name = "errno" -version = "0.3.14" +name = "encode_unicode" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] -name = "fastrand" -version = "2.3.0" +name = "env_home" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" [[package]] -name = "file-locker" -version = "1.1.4" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ae8b5984a4863d8a32109a848d038bd6d914f20f010cc141375f7a183c41cf" -dependencies = [ - "nix", -] +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "find-msvc-tools" -version = "0.1.7" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] [[package]] -name = "fnv" -version = "1.0.7" +name = "fallible-iterator" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] -name = "foldhash" -version = "0.1.5" +name = "fallible-streaming-iterator" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] -name = "form_urlencoded" -version = "1.2.2" +name = "fastrand" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] -name = "fs_extra" -version = "1.3.0" +name = "find-msvc-tools" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "futures" @@ -438,16 +463,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" -[[package]] -name = "futures-timer" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" -dependencies = [ - "gloo-timers", - "send_wrapper", -] - [[package]] name = "futures-util" version = "0.3.32" @@ -465,446 +480,249 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi", - "wasip2", - "wasm-bindgen", ] [[package]] -name = "getrandom" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +name = "getter" +version = "0.1.0" dependencies = [ - "cfg-if", - "libc", - "r-efi", - "rand_core 0.10.0", - "wasip2", - "wasip3", + "getter-cli", + "getter-core", + "getter-storage", + "rustls-platform-verifier", + "thiserror 1.0.69", + "tokio", ] [[package]] -name = "getter" +name = "getter-cli" version = "0.1.0" dependencies = [ - "async-trait", - "async_fn_traits", - "bytes", - "file-locker", - "futures", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "jsonl", - "jsonrpsee", - "libversion-sys", - "markdown", - "mockito", - "once_cell", - "parking_lot", - "quick-xml", - "rand 0.10.0", - "regex", - "reqwest", - "rustls", - "rustls-platform-verifier 0.6.2", + "cucumber", + "getter-core", + "getter-storage", "serde", "serde_json", - "serial_test", "tempfile", + "thiserror 1.0.69", "tokio", - "tokio-util", - "url", - "uuid", ] [[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "gloo-net" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +name = "getter-core" +version = "0.1.0" dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils", - "http", - "js-sys", - "pin-project", + "mlua", "serde", "serde_json", + "tempfile", "thiserror 1.0.69", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", + "toml", ] [[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +name = "getter-downloader" +version = "0.1.0" dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", + "getter-core", ] [[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +name = "getter-ffi" +version = "0.1.0" dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", + "getter-core", ] [[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +name = "getter-plugin-api" +version = "0.1.0" dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", + "getter-core", ] [[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +name = "getter-providers" +version = "0.1.0" dependencies = [ - "foldhash", + "getter-core", ] [[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +name = "getter-rpc" +version = "0.1.0" +dependencies = [ + "getter-core", +] [[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +name = "getter-storage" +version = "0.1.0" +dependencies = [ + "getter-core", + "rusqlite", + "tempfile", + "thiserror 1.0.69", +] [[package]] -name = "http" -version = "1.4.0" +name = "gherkin" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "9e2c0d8c632f8a251ce9a8198079b1022adc586ff4e3d33e18debd40eb463b31" dependencies = [ - "bytes", - "itoa", + "heck", + "peg", + "quote", + "serde", + "serde_json", + "syn", + "textwrap", + "thiserror 2.0.18", + "typed-builder", ] [[package]] -name = "http-body" -version = "1.0.1" +name = "globset" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" dependencies = [ - "bytes", - "http", + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", ] [[package]] -name = "http-body-util" -version = "0.1.3" +name = "globwalk" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", + "bitflags", + "ignore", + "walkdir", ] [[package]] -name = "httparse" -version = "1.10.1" +name = "hashbrown" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] [[package]] -name = "httpdate" -version = "1.0.3" +name = "hashbrown" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] -name = "hyper" -version = "1.8.1" +name = "hashlink" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", + "hashbrown 0.14.5", ] [[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "log", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "rustls-platform-verifier 0.6.2", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "2.1.1" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "id-arena" +name = "humantime" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] -name = "idna_adapter" -version = "1.2.1" +name = "ignore" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "b915661dd01db3f05050265b2477bcc6527b3792388e2749b41623cc592be67d" dependencies = [ - "icu_normalizer", - "icu_properties", + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", ] [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", + "hashbrown 0.17.1", ] [[package]] -name = "ipnet" -version = "2.11.0" +name = "inflections" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" [[package]] -name = "iri-string" -version = "0.7.10" +name = "inventory" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" dependencies = [ - "memchr", - "serde", + "rustversion", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jni" @@ -915,7 +733,7 @@ dependencies = [ "cesu8", "cfg-if", "combine", - "jni-sys", + "jni-sys 0.3.1", "log", "thiserror 1.0.69", "walkdir", @@ -924,229 +742,54 @@ dependencies = [ [[package]] name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "jsonl" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abae98f45234fc1980c198798166a80ad6b35eb5b7db4caa7bc72ff919e6b80" -dependencies = [ - "serde", - "serde_json", - "thiserror 1.0.69", -] - -[[package]] -name = "jsonrpsee" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a" -dependencies = [ - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-http-client", - "jsonrpsee-server", - "jsonrpsee-types", - "jsonrpsee-wasm-client", - "jsonrpsee-ws-client", - "tokio", -] - -[[package]] -name = "jsonrpsee-client-transport" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf36eb27f8e13fa93dcb50ccb44c417e25b818cfa1a481b5470cd07b19c60b98" -dependencies = [ - "base64", - "futures-channel", - "futures-util", - "gloo-net", - "http", - "jsonrpsee-core", - "pin-project", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier 0.5.3", - "soketto", - "thiserror 2.0.17", - "tokio", - "tokio-rustls", - "tokio-util", - "tracing", - "url", -] - -[[package]] -name = "jsonrpsee-core" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "316c96719901f05d1137f19ba598b5fe9c9bc39f4335f67f6be8613921946480" -dependencies = [ - "async-trait", - "bytes", - "futures-timer", - "futures-util", - "http", - "http-body", - "http-body-util", - "jsonrpsee-types", - "parking_lot", - "pin-project", - "rand 0.9.2", - "rustc-hash", - "serde", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tokio-stream", - "tower", - "tracing", - "wasm-bindgen-futures", -] - -[[package]] -name = "jsonrpsee-http-client" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790bedefcec85321e007ff3af84b4e417540d5c87b3c9779b9e247d1bcc3dab8" -dependencies = [ - "base64", - "http-body", - "hyper", - "hyper-rustls", - "hyper-util", - "jsonrpsee-core", - "jsonrpsee-types", - "rustls", - "rustls-platform-verifier 0.5.3", - "serde", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tower", - "url", -] - -[[package]] -name = "jsonrpsee-server" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c51b7c290bb68ce3af2d029648148403863b982f138484a73f02a9dd52dbd7f" -dependencies = [ - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "jsonrpsee-core", - "jsonrpsee-types", - "pin-project", - "route-recognizer", - "serde", - "serde_json", - "soketto", - "thiserror 2.0.17", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tracing", -] - -[[package]] -name = "jsonrpsee-types" -version = "0.26.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc88ff4688e43cc3fa9883a8a95c6fa27aa2e76c96e610b737b6554d650d7fd5" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" dependencies = [ - "http", - "serde", - "serde_json", - "thiserror 2.0.17", + "jni-sys 0.4.1", ] [[package]] -name = "jsonrpsee-wasm-client" -version = "0.26.0" +name = "jni-sys" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7902885de4779f711a95d82c8da2d7e5f9f3a7c7cfa44d51c067fd1c29d72a3c" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" dependencies = [ - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", - "tower", + "jni-sys-macros", ] [[package]] -name = "jsonrpsee-ws-client" -version = "0.26.0" +name = "jni-sys-macros" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ - "http", - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", - "tower", - "url", + "quote", + "syn", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" -version = "0.2.182" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] -name = "libloading" -version = "0.8.9" +name = "libsqlite3-sys" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ - "cfg-if", - "windows-link", + "cc", + "pkg-config", + "vcpkg", ] [[package]] -name = "libversion-sys" -version = "0.1.0" -source = "git+https://github.com/DUpdateSystem/libversion-sys?rev=68391515ac8f555ca86bc5dfcd980f66352e414d#68391515ac8f555ca86bc5dfcd980f66352e414d" -dependencies = [ - "bindgen", - "cc", - "cmake", -] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" @@ -1154,12 +797,6 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - [[package]] name = "lock_api" version = "0.4.14" @@ -1171,42 +808,40 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] -name = "lru-slab" -version = "0.1.2" +name = "lua-src" +version = "547.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +checksum = "1edaf29e3517b49b8b746701e5648ccb5785cde1c119062cbabbc5d5cd115e42" +dependencies = [ + "cc", +] [[package]] -name = "markdown" -version = "1.0.0" +name = "luajit-src" +version = "210.5.12+a4f56a4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5cab8f2cadc416a82d2e783a1946388b31654d391d1c7d92cc1f03e295b1deb" +checksum = "b3a8e7962a5368d5f264d045a5a255e90f9aa3fc1941ae15a8d2940d42cac671" dependencies = [ - "unicode-id", + "cc", + "which", ] [[package]] name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "mio" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -1214,63 +849,79 @@ dependencies = [ ] [[package]] -name = "mockito" -version = "1.7.2" +name = "mlua" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90820618712cab19cfc46b274c6c22546a82affcb3c3bdf0f29e3db8e1bb92c0" +checksum = "c1f5f8fbebc7db5f671671134b9321c4b9aa9adeafccfd9a8c020ae45c6a35d0" dependencies = [ - "assert-json-diff", - "bytes", - "colored", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "log", - "pin-project-lite", - "rand 0.9.2", - "regex", - "serde_json", - "serde_urlencoded", - "similar", - "tokio", + "bstr", + "either", + "mlua-sys", + "num-traits", + "parking_lot", + "rustc-hash", + "rustversion", ] [[package]] -name = "nix" -version = "0.29.0" +name = "mlua-sys" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "380c1f7e2099cafcf40e51d3a9f20a346977587aa4d012eae1f043149a728a93" dependencies = [ - "bitflags", + "cc", "cfg-if", - "cfg_aliases", - "libc", + "lua-src", + "luajit-src", + "pkg-config", ] [[package]] name = "nom" -version = "7.1.3" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nom_locate" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68438d" dependencies = [ + "bytecount", "memchr", - "minimal-lexical", + "nom", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", ] [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl-probe" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "parking_lot" @@ -1296,31 +947,46 @@ dependencies = [ ] [[package]] -name = "paste" -version = "1.0.15" +name = "peg" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] [[package]] -name = "percent-encoding" -version = "2.3.2" +name = "peg-runtime" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" [[package]] name = "pin-project" -version = "1.1.10" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.10" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", @@ -1329,225 +995,74 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] -name = "pin-utils" -version = "0.1.0" +name = "pkg-config" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] -name = "potential_utf" -version = "0.1.4" +name = "proc-macro2" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ - "zerovec", + "unicode-ident", ] [[package]] -name = "ppv-lite86" -version = "0.2.21" +name = "quote" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ - "zerocopy", + "proc-macro2", ] [[package]] -name = "prettyplease" -version = "0.2.37" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] -name = "proc-macro2" -version = "1.0.105" +name = "redox_syscall" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "unicode-ident", + "bitflags", ] [[package]] -name = "quick-xml" -version = "0.39.2" +name = "ref-cast" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ - "encoding_rs", - "memchr", - "tokio", + "ref-cast-impl", ] [[package]] -name = "quinn" -version = "0.11.9" +name = "ref-cast-impl" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.17", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.17", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" -dependencies = [ - "chacha20", - "getrandom 0.4.1", - "rand_core 0.10.0", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_core" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", + "quote", + "syn", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -1557,9 +1072,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1568,49 +1083,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - -[[package]] -name = "reqwest" -version = "0.13.2" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" -dependencies = [ - "base64", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier 0.6.2", - "serde", - "serde_json", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", -] +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "ring" @@ -1620,23 +1095,40 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", ] [[package]] -name = "route-recognizer" -version = "0.3.1" +name = "rusqlite" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] [[package]] name = "rustix" @@ -1648,19 +1140,16 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ - "aws-lc-rs", - "log", "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -1669,9 +1158,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -1681,35 +1170,13 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ - "web-time", "zeroize", ] -[[package]] -name = "rustls-platform-verifier" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" -dependencies = [ - "core-foundation", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs 0.26.11", - "windows-sys 0.52.0", -] - [[package]] name = "rustls-platform-verifier" version = "0.6.2" @@ -1727,8 +1194,8 @@ dependencies = [ "rustls-webpki", "security-framework", "security-framework-sys", - "webpki-root-certs 1.0.5", - "windows-sys 0.60.2", + "webpki-root-certs", + "windows-sys 0.61.2", ] [[package]] @@ -1739,11 +1206,10 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ - "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -1755,12 +1221,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" - [[package]] name = "same-file" version = "1.0.6" @@ -1770,20 +1230,11 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scc" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" -dependencies = [ - "sdd", -] - [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] @@ -1795,16 +1246,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "sdd" -version = "3.0.10" +name = "sealed" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" +checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "security-framework" -version = "3.5.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", "core-foundation", @@ -1815,9 +1271,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -1825,15 +1281,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "send_wrapper" -version = "0.4.0" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -1867,9 +1317,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -1879,119 +1329,64 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serde_spanned" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", "serde", ] [[package]] -name = "serial_test" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f" -dependencies = [ - "futures-executor", - "futures-util", - "log", - "once_cell", - "parking_lot", - "scc", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "3.4.0" +name = "shlex" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] -name = "sha1" -version = "0.10.6" +name = "slab" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest", -] +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] -name = "shlex" -version = "1.3.0" +name = "smallvec" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] -name = "signal-hook-registry" -version = "1.4.8" +name = "smart-default" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" dependencies = [ - "errno", - "libc", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "similar" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "smallvec" -version = "1.15.1" +name = "smawk" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "e8e2fb0f499abb4d162f2bedad68f5ef91a1682b5a03596ddb67efd37768d100" [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "soketto" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" -dependencies = [ - "base64", - "bytes", - "futures", - "http", - "httparse", - "log", - "rand 0.8.5", - "sha1", + "windows-sys 0.61.2", ] [[package]] -name = "stable_deref_trait" -version = "1.2.1" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" @@ -2001,9 +1396,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.114" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -2011,509 +1406,287 @@ dependencies = [ ] [[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tempfile" -version = "3.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" -dependencies = [ - "fastrand", - "getrandom 0.3.4", - "once_cell", - "rustix", - "windows-sys 0.60.2", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" +name = "synthez" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "6d8a928f38f1bc873f28e0d2ba8298ad65374a6ac2241dabd297271531a736cd" dependencies = [ - "proc-macro2", - "quote", "syn", + "synthez-codegen", + "synthez-core", ] [[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.49.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.0" +name = "synthez-codegen" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "8fb83b8df4238e11746984dfb3819b155cd270de0e25847f45abad56b3671047" dependencies = [ - "proc-macro2", - "quote", "syn", + "synthez-core", ] [[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" +name = "synthez-core" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +checksum = "906fba967105d822e7c7ed60477b5e76116724d33de68a585681fb253fc30d5c" dependencies = [ "proc-macro2", "quote", + "sealed", "syn", ] [[package]] -name = "tracing-core" -version = "0.1.36" +name = "tempfile" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ + "fastrand", + "getrandom 0.4.3", "once_cell", + "rustix", + "windows-sys 0.61.2", ] [[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "unicode-id" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ba288e709927c043cbe476718d37be306be53fb1fafecd0dbe36d072be2580" - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" -dependencies = [ - "getrandom 0.4.1", - "js-sys", - "serde_core", - "wasm-bindgen", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" +name = "terminal_size" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ - "try-lock", + "rustix", + "windows-sys 0.61.2", ] [[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +name = "textwrap" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] [[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" +name = "thiserror" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "wit-bindgen 0.46.0", + "thiserror-impl 1.0.69", ] [[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +name = "thiserror" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "wit-bindgen 0.51.0", + "thiserror-impl 2.0.18", ] [[package]] -name = "wasm-bindgen" -version = "0.2.108" +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "wasm-bindgen-futures" -version = "0.4.58" +name = "thiserror-impl" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ - "cfg-if", - "futures-util", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.108" +name = "tokio" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ - "quote", - "wasm-bindgen-macro-support", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", ] [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.108" +name = "tokio-macros" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ - "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-shared", ] [[package]] -name = "wasm-bindgen-shared" -version = "0.2.108" +name = "toml" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ - "unicode-ident", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", ] [[package]] -name = "wasm-encoder" -version = "0.244.0" +name = "toml_datetime" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ - "leb128fmt", - "wasmparser", + "serde", ] [[package]] -name = "wasm-metadata" -version = "0.244.0" +name = "toml_edit" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "anyhow", "indexmap", - "wasm-encoder", - "wasmparser", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", ] [[package]] -name = "wasm-streams" -version = "0.5.0" +name = "toml_write" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] -name = "wasmparser" -version = "0.244.0" +name = "typed-builder" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +checksum = "31aa81521b70f94402501d848ccc0ecaa8f93c8eb6999eb9747e72287757ffda" dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", + "typed-builder-macro", ] [[package]] -name = "web-sys" -version = "0.3.85" +name = "typed-builder-macro" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "076a02dc54dd46795c2e9c8282ed40bcfb1e22747e955de9389a1de28190fb26" dependencies = [ - "js-sys", - "wasm-bindgen", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "web-time" -version = "1.1.0" +name = "unicode-ident" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] -name = "webpki-root-certs" -version = "0.26.11" +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ - "webpki-root-certs 1.0.5", + "same-file", + "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "webpki-root-certs" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" dependencies = [ "rustls-pki-types", ] [[package]] -name = "webpki-roots" -version = "1.0.5" +name = "which" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ - "rustls-pki-types", + "either", + "env_home", + "rustix", + "winsafe", ] [[package]] @@ -2522,7 +1695,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2549,15 +1722,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -2591,30 +1755,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -2627,12 +1774,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -2645,12 +1786,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -2663,24 +1798,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -2693,12 +1816,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -2711,12 +1828,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -2729,12 +1840,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -2748,216 +1853,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - -[[package]] -name = "yoke" -version = "0.8.1" +name = "winnow" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", + "memchr", ] [[package]] -name = "yoke-derive" -version = "0.8.1" +name = "winsafe" +version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", "syn", - "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zmij" -version = "1.0.12" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index c7a8f49..500b0f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,44 +3,35 @@ name = "getter" version = "0.1.0" edition = "2021" +[workspace] +resolver = "2" +members = [ + "crates/getter-core", + "crates/getter-storage", + "crates/getter-providers", + "crates/getter-downloader", + "crates/getter-plugin-api", + "crates/getter-rpc", + "crates/getter-cli", + "crates/getter-ffi", +] + +[workspace.package] +version = "0.1.0" +edition = "2021" + [features] -default = ["rustls-platform-verifier"] -rustls-platform-verifier = ["hyper-rustls/rustls-platform-verifier", "dep:rustls-platform-verifier"] +default = ["cli"] +cli = ["dep:getter-cli"] +rustls-platform-verifier = ["dep:rustls-platform-verifier"] rustls-platform-verifier-android = ["rustls-platform-verifier", "rustls-platform-verifier/jni"] -webpki-roots = ["hyper-rustls/webpki-roots"] -native-tokio = ["hyper-rustls/native-tokio"] +webpki-roots = [] +native-tokio = [] [dependencies] -once_cell = "1.21.3" -async-trait = "0.1.89" -hyper = { version = "1.8", features = ["full"] } -tokio = { version = "1", features = ["full", "macros"] } -bytes = "1.11.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.149" -quick-xml = { version = "0.39.0", features = ["encoding", "async-tokio"] } -async_fn_traits = "0.1.1" -libversion-sys = { git = "https://github.com/DUpdateSystem/libversion-sys", rev = "68391515ac8f555ca86bc5dfcd980f66352e414d" } -regex = "1.12.2" -hyper-util = { version = "0.1.19", features = ["client", "tokio", "http1"] } -http-body-util = "0.1.3" -jsonrpsee = { version = "0.26.0", features = ["server", "client"] } -hyper-rustls = { version = "0.27.7", features = ["http1", "http2", "native-tokio", "ring", "tls12"], default-features = false } -rustls-platform-verifier = { version = "0.6.2", optional = true } -rustls = { version = "0.23.36", default-features = false } -markdown = "1.0.0" -# Downloader dependencies -reqwest = { version = "0.13", default-features = false, features = ["rustls", "stream", "json"] } -uuid = { version = "1.19", features = ["v4", "serde"] } -parking_lot = "0.12" -futures = "0.3" -tokio-util = { version = "0.7", features = ["io"] } -jsonl = "4.0" -file-locker = "1.1" -url = "2" - -[dev-dependencies] -mockito = "1.7.1" -rand = "0.10.0" -serial_test = "3.3.1" -tempfile = "3.24.0" +getter-core = { path = "crates/getter-core" } +getter-storage = { path = "crates/getter-storage" } +getter-cli = { path = "crates/getter-cli", optional = true } +thiserror = "1" +tokio = { version = "1", features = ["net"] } +rustls-platform-verifier = { version = "0.6", optional = true } diff --git a/README.md b/README.md index 24fef6c..34543a0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ -# getter -Get update for everywhere +# UpgradeAll getter + +Reusable Rust getter core for the UpgradeAll rewrite. + +This repository is intentionally usable outside the UpgradeAll UI. The UpgradeAll app is a UI/platform adapter; package/repository evaluation, storage, migration mapping, update policy, and CLI behavior belong here. + +## Current rewrite spine + +- Rust workspace split into `getter-core`, `getter-storage`, `getter-cli`, and placeholder provider/downloader/RPC/FFI crates. +- Package IDs are readable, for example `android/org.fdroid.fdroid`. +- Lua package repositories use `repo.toml` plus `packages/`, `lib/`, and `templates/` directories. +- SQLite storage uses a main DB and a separate cache DB. +- `getter-cli` exposes JSON command contracts for init, app list, repository registration/evaluation, package evaluation, storage validation, and legacy bridge-bundle import. + +## Verify + +```bash +cargo fmt --all --check +cargo test --workspace --lib --bins +cargo test -p getter-cli --test bdd_cli +cargo check --workspace --all-targets +``` + +The CI helper at `tests/script/cargo_test.sh` runs the same core checks plus feature-compatibility checks. diff --git a/crates/getter-cli/Cargo.toml b/crates/getter-cli/Cargo.toml new file mode 100644 index 0000000..ca03088 --- /dev/null +++ b/crates/getter-cli/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "getter-cli" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } +getter-storage = { path = "../getter-storage" } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "1" + +[dev-dependencies] +cucumber = "0.23" +serde_json = "1" +tempfile = "3" +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } + +[[test]] +name = "bdd_cli" +harness = false diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs new file mode 100644 index 0000000..8e0155e --- /dev/null +++ b/crates/getter-cli/src/lib.rs @@ -0,0 +1,817 @@ +//! User-facing getter CLI implementation. +//! +//! The CLI is intentionally thin: it owns command parsing and JSON envelopes, +//! while durable state is initialized through `getter-storage` so the command +//! surface exercises the same Rust-owned SQLite direction used by embedders. + +use getter_core::lua::evaluate_package_file; +use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; +use getter_core::{PackageId, RepositoryId, RepositoryPriority}; +use getter_storage::legacy_room::{ + map_legacy_app, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, +}; +use getter_storage::{ + CacheDb, MainDb, StorageError, StoredPackageResolution, StoredRepository, StoredTrackedPackage, + TrackedPackageUpsert, +}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use std::fs; +use std::path::{Path, PathBuf}; + +const MAIN_DB_FILE: &str = "main.db"; +const CACHE_DB_FILE: &str = "cache.db"; +const MIGRATION_REPORTS_DIR: &str = "migration-reports"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CliInvocation { + pub data_dir: PathBuf, + pub command: CliCommand, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CliCommand { + Init, + AppList, + HubList, + RepoList, + RepoAdd { + id: RepositoryId, + path: PathBuf, + priority: Option, + }, + RepoEval { + id: RepositoryId, + }, + PackageEval { + package_id: PackageId, + repo_id: Option, + }, + StorageValidate, + LegacyImportRoomBundle { + bundle: PathBuf, + }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ExitCode { + Success = 0, + GenericFailure = 1, + Usage = 2, + Storage = 10, + Migration = 20, +} + +impl ExitCode { + pub const fn code(self) -> i32 { + self as i32 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum CliError { + #[error("{0}")] + Usage(String), + #[error("storage error: {0}")] + Storage(String), + #[error("repository error: {0}")] + Repository(String), + #[error("package evaluation error: {0}")] + PackageEval(String), + #[error("Legacy Room export bundle is invalid")] + InvalidLegacyBundle { report_path: PathBuf }, + #[error("Legacy Room bundle import is not implemented yet")] + UnsupportedLegacyBundle { report_path: PathBuf }, +} + +impl CliError { + pub fn exit_code(&self) -> ExitCode { + match self { + Self::Usage(_) => ExitCode::Usage, + Self::Storage(_) => ExitCode::Storage, + Self::Repository(_) | Self::PackageEval(_) => ExitCode::GenericFailure, + Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } => { + ExitCode::Migration + } + } + } + + fn code(&self) -> &'static str { + match self { + Self::Usage(_) => "cli.usage", + Self::Storage(_) => "storage.error", + Self::Repository(_) => "repository.error", + Self::PackageEval(_) => "package.eval_error", + Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", + Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", + } + } + + fn message(&self) -> &'static str { + match self { + Self::Usage(_) => "Invalid getter CLI usage", + Self::Storage(_) => "Getter storage operation failed", + Self::Repository(_) => "Getter repository operation failed", + Self::PackageEval(_) => "Getter package evaluation failed", + Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", + Self::UnsupportedLegacyBundle { .. } => { + "Legacy Room bundle import is not implemented yet" + } + } + } + + fn detail(&self) -> Option<&str> { + match self { + Self::Usage(detail) + | Self::Storage(detail) + | Self::Repository(detail) + | Self::PackageEval(detail) => Some(detail.as_str()), + Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } => None, + } + } + + fn report_path(&self) -> Option<&Path> { + match self { + Self::InvalidLegacyBundle { report_path } + | Self::UnsupportedLegacyBundle { report_path } => Some(report_path.as_path()), + Self::Usage(_) | Self::Storage(_) | Self::Repository(_) | Self::PackageEval(_) => None, + } + } +} + +impl From for CliError { + fn from(value: StorageError) -> Self { + Self::Storage(value.to_string()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CliOutput { + pub exit_code: ExitCode, + pub stdout: String, + pub stderr: String, +} + +/// Parse and execute a getter CLI invocation, returning stdout/stderr and the +/// supported process exit code. This is used by the binary entrypoint and by +/// tests so command behavior stays consistent. +pub fn run(args: I) -> CliOutput +where + I: IntoIterator, + S: Into, +{ + match run_inner(args) { + Ok((command_name, data)) => CliOutput { + exit_code: ExitCode::Success, + stdout: success_envelope(&command_name, data), + stderr: String::new(), + }, + Err((command_name, error)) => { + let exit_code = error.exit_code(); + let stderr = match error { + CliError::Usage(_) => usage_text(), + _ => String::new(), + }; + CliOutput { + exit_code, + stdout: error_envelope(&command_name, &error), + stderr, + } + } + } +} + +fn run_inner(args: I) -> Result<(String, Value), (String, CliError)> +where + I: IntoIterator, + S: Into, +{ + let invocation = parse_args(args).map_err(|error| ("unknown".to_owned(), error))?; + let command_name = invocation.command.name().to_owned(); + execute(invocation) + .map(|data| (command_name.clone(), data)) + .map_err(|error| (command_name, error)) +} + +pub fn parse_args(args: I) -> Result +where + I: IntoIterator, + S: Into, +{ + let mut args: Vec = args.into_iter().map(Into::into).collect(); + if args.first().is_some_and(|arg| arg != "--data-dir") { + args.remove(0); + } + + if args.first().map(String::as_str) != Some("--data-dir") { + return Err(CliError::Usage( + "missing required --data-dir global option".to_owned(), + )); + } + if args.len() < 3 { + return Err(CliError::Usage( + "missing command after --data-dir ".to_owned(), + )); + } + + let data_dir = PathBuf::from(args[1].clone()); + let command_args = &args[2..]; + let command = match command_args { + [command] if command == "init" => CliCommand::Init, + [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, + [domain, command] if domain == "hub" && command == "list" => CliCommand::HubList, + [domain, command] if domain == "repo" && command == "list" => CliCommand::RepoList, + [domain, command, id, path] if domain == "repo" && command == "add" => { + CliCommand::RepoAdd { + id: parse_repository_id(id)?, + path: PathBuf::from(path), + priority: None, + } + } + [domain, command, id, path, flag, priority] + if domain == "repo" && command == "add" && flag == "--priority" => + { + CliCommand::RepoAdd { + id: parse_repository_id(id)?, + path: PathBuf::from(path), + priority: Some(parse_priority(priority)?), + } + } + [domain, command, id] if domain == "repo" && command == "eval" => CliCommand::RepoEval { + id: parse_repository_id(id)?, + }, + [domain, command, package_id] if domain == "package" && command == "eval" => { + CliCommand::PackageEval { + package_id: parse_package_id(package_id)?, + repo_id: None, + } + } + [domain, command, package_id, flag, repo_id] + if domain == "package" && command == "eval" && flag == "--repo" => + { + CliCommand::PackageEval { + package_id: parse_package_id(package_id)?, + repo_id: Some(parse_repository_id(repo_id)?), + } + } + [domain, command] if domain == "storage" && command == "validate" => { + CliCommand::StorageValidate + } + [domain, command, bundle] if domain == "legacy" && command == "import-room-bundle" => { + CliCommand::LegacyImportRoomBundle { + bundle: PathBuf::from(bundle), + } + } + _ => { + return Err(CliError::Usage(format!( + "unsupported command: {}", + command_args.join(" ") + ))) + } + }; + + Ok(CliInvocation { data_dir, command }) +} + +fn execute(invocation: CliInvocation) -> Result { + match invocation.command { + CliCommand::Init => { + initialize_storage(&invocation.data_dir)?; + Ok(json!({ + "data_dir": invocation.data_dir, + "main_db": main_db_path(&invocation.data_dir), + "cache_db": cache_db_path(&invocation.data_dir), + })) + } + CliCommand::AppList => { + let db = open_main_db(&invocation.data_dir)?; + Ok(json!({ "apps": tracked_packages_json(db.tracked_packages()?) })) + } + CliCommand::HubList => { + open_initialized_storage(&invocation.data_dir)?; + Ok(json!({ "hubs": [] })) + } + CliCommand::RepoList => { + let db = open_main_db(&invocation.data_dir)?; + Ok(json!({ "repositories": list_repositories(&db)? })) + } + CliCommand::RepoAdd { id, path, priority } => { + let db = open_main_db(&invocation.data_dir)?; + let layout = load_repository_layout(&path)?; + if layout.metadata.id != id { + return Err(CliError::Repository(format!( + "repo.toml id '{}' does not match requested id '{}'", + layout.metadata.id, id + ))); + } + let metadata = RepositoryMetadata { + priority: priority.unwrap_or(layout.metadata.priority), + ..layout.metadata.clone() + }; + db.upsert_repository(&metadata, Some(&path), None)?; + Ok(json!({ "repository": repository_metadata_json(&metadata, Some(&path), None) })) + } + CliCommand::RepoEval { id } => { + let db = open_main_db(&invocation.data_dir)?; + let repo = find_repository(&db, &id)?; + let path = repo_path(&repo)?; + let layout = load_repository_layout(&path)?; + let mut packages = Vec::new(); + for package_file in &layout.packages { + let package = evaluate_package_file(&layout, &package_file.path) + .map_err(|error| CliError::PackageEval(error.to_string()))?; + packages.push(package_json(package)?); + } + Ok(json!({ + "repository": repository_json(repo), + "packages": packages, + })) + } + CliCommand::PackageEval { + package_id, + repo_id, + } => { + let db = open_main_db(&invocation.data_dir)?; + let package = match repo_id { + Some(repo_id) => evaluate_package_from_repo(&db, &repo_id, &package_id)?, + None => evaluate_highest_priority_package(&db, &package_id)?, + }; + Ok(json!({ "package": package_json(package)? })) + } + CliCommand::StorageValidate => { + initialize_storage(&invocation.data_dir)?; + Ok(json!({ + "valid": true, + "main_db": main_db_path(&invocation.data_dir), + "cache_db": cache_db_path(&invocation.data_dir), + })) + } + CliCommand::LegacyImportRoomBundle { bundle } => { + let db = open_main_db(&invocation.data_dir)?; + let bytes = fs::read(&bundle).map_err(|source| { + match create_migration_report( + &invocation.data_dir, + &bundle, + "migration.invalid_bundle", + &format!("failed to read bundle: {source}"), + 0, + 0, + ) { + Ok(report_path) => CliError::InvalidLegacyBundle { report_path }, + Err(error) => error, + } + })?; + let parsed: LegacyRoomBundle = serde_json::from_slice(&bytes).map_err(|source| { + match create_migration_report( + &invocation.data_dir, + &bundle, + "migration.invalid_bundle", + &format!("failed to parse JSON bundle: {source}"), + 0, + 0, + ) { + Ok(report_path) => CliError::InvalidLegacyBundle { report_path }, + Err(error) => error, + } + })?; + if parsed.format != "upgradeall-legacy-room-bundle" || parsed.version != 17 { + let report_path = create_migration_report( + &invocation.data_dir, + &bundle, + "migration.unsupported_bundle", + &format!( + "unsupported legacy bundle format '{}' version {}", + parsed.format, parsed.version + ), + 0, + 0, + )?; + return Err(CliError::UnsupportedLegacyBundle { report_path }); + } + import_legacy_room_bundle(&db, &parsed)?; + let report_path = create_migration_report( + &invocation.data_dir, + &bundle, + "migration.imported", + "Legacy Room bundle imported", + parsed.apps.len() as u64, + parsed.apps.len() as u64, + )?; + let records = db.tracked_packages()?; + Ok(json!({ + "report_path": report_path, + "imported_records": parsed.apps.len(), + "apps": tracked_packages_json(records), + })) + } + } +} + +fn initialize_storage(data_dir: &Path) -> Result<(), CliError> { + fs::create_dir_all(data_dir).map_err(|source| { + CliError::Storage(format!("failed to create data directory: {source}")) + })?; + MainDb::open(main_db_path(data_dir))?; + CacheDb::open(cache_db_path(data_dir))?; + Ok(()) +} + +fn open_initialized_storage(data_dir: &Path) -> Result<(), CliError> { + initialize_storage(data_dir) +} + +fn open_main_db(data_dir: &Path) -> Result { + initialize_storage(data_dir)?; + Ok(MainDb::open(main_db_path(data_dir))?) +} + +fn parse_repository_id(value: &str) -> Result { + RepositoryId::new(value).map_err(|source| CliError::Usage(source.to_string())) +} + +fn parse_package_id(value: &str) -> Result { + value + .parse() + .map_err(|source: getter_core::PackageIdError| CliError::Usage(source.to_string())) +} + +fn parse_priority(value: &str) -> Result { + value + .parse::() + .map(RepositoryPriority::new) + .map_err(|source| CliError::Usage(format!("invalid repository priority: {source}"))) +} + +fn load_repository_layout(path: &Path) -> Result { + RepositoryLayout::load(path).map_err(|source| CliError::Repository(source.to_string())) +} + +fn list_repositories(db: &MainDb) -> Result, CliError> { + Ok(db + .repositories()? + .into_iter() + .map(repository_json) + .collect()) +} + +fn find_repository(db: &MainDb, id: &RepositoryId) -> Result { + db.repositories()? + .into_iter() + .find(|repo| &repo.id == id) + .ok_or_else(|| CliError::Repository(format!("repository '{id}' is not registered"))) +} + +fn evaluate_package_from_repo( + db: &MainDb, + repo_id: &RepositoryId, + package_id: &PackageId, +) -> Result { + let repo = find_repository(db, repo_id)?; + let path = repo_path(&repo)?; + let layout = load_repository_layout(&path)?; + let package_file = layout.package_file(package_id).ok_or_else(|| { + CliError::PackageEval(format!( + "package '{}' was not found in repository '{}'", + package_id, repo_id + )) + })?; + evaluate_package_file(&layout, &package_file.path) + .map_err(|error| CliError::PackageEval(error.to_string())) +} + +fn evaluate_highest_priority_package( + db: &MainDb, + package_id: &PackageId, +) -> Result { + for repo in db.repositories()? { + let path = repo_path(&repo)?; + let layout = load_repository_layout(&path)?; + if let Some(package_file) = layout.package_file(package_id) { + return evaluate_package_file(&layout, &package_file.path) + .map_err(|error| CliError::PackageEval(error.to_string())); + } + } + Err(CliError::PackageEval(format!( + "package '{package_id}' was not found in any registered repository" + ))) +} + +fn repo_path(repo: &StoredRepository) -> Result { + repo.path + .as_ref() + .map(PathBuf::from) + .ok_or_else(|| CliError::Repository(format!("repository '{}' has no path", repo.id))) +} + +fn repository_json(repo: StoredRepository) -> Value { + json!({ + "id": repo.id.as_str(), + "name": repo.name, + "priority": repo.priority.value(), + "api_version": repo.api_version, + "path": repo.path, + "revision": repo.revision, + }) +} + +fn repository_metadata_json( + metadata: &RepositoryMetadata, + path: Option<&Path>, + revision: Option<&str>, +) -> Value { + json!({ + "id": metadata.id.as_str(), + "name": metadata.name, + "priority": metadata.priority.value(), + "api_version": metadata.api_version, + "path": path.map(|path| path.to_string_lossy().to_string()), + "revision": revision, + }) +} + +fn package_json(package: getter_core::ResolvedPackage) -> Result { + serde_json::to_value(package) + .map_err(|source| CliError::PackageEval(format!("failed to serialize package: {source}"))) +} + +fn tracked_packages_json(packages: Vec) -> Vec { + packages + .into_iter() + .map(|package| { + json!({ + "id": package.package_id.to_string(), + "enabled": package.enabled, + "favorite": package.favorite, + "ignored_version": package.ignored_version, + "repository_id": package.repository_id.map(|id| id.to_string()), + "package_resolution": package.package_resolution.as_str(), + }) + }) + .collect() +} + +fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<(), CliError> { + for app in &bundle.apps { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: app.kind.to_legacy_kind()?, + installed_id: app.installed_id.clone(), + official_package_available: app.official_package_available, + common_conversion_available: app.common_conversion_available, + }, + Some(&LegacyExtraAppRecord { + ignored_version: app.ignored_version.clone(), + favorite: app.favorite, + }), + ) + .map_err(|source| CliError::Storage(source.to_string()))?; + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: mapping.package_id, + enabled: true, + favorite: mapping.user_state.favorite, + ignored_version: mapping.user_state.ignored_version, + repository_id: None, + package_resolution: stored_resolution(mapping.package_resolution), + })?; + } + + let report_json = json!({ + "ok": true, + "source": "legacy-room-bundle", + "imported_records": bundle.apps.len(), + }) + .to_string(); + db.insert_migration_record("legacy-room-v17", "legacy-room-bundle", &report_json)?; + Ok(()) +} + +fn stored_resolution(resolution: LegacyPackageResolution) -> StoredPackageResolution { + match resolution { + LegacyPackageResolution::OfficialRepositoryPackage => { + StoredPackageResolution::OfficialRepositoryPackage + } + LegacyPackageResolution::GenerateLocalPackage => { + StoredPackageResolution::GenerateLocalPackage + } + LegacyPackageResolution::MissingPackageDefinition => { + StoredPackageResolution::MissingPackageDefinition + } + } +} + +fn main_db_path(data_dir: &Path) -> PathBuf { + data_dir.join(MAIN_DB_FILE) +} + +fn cache_db_path(data_dir: &Path) -> PathBuf { + data_dir.join(CACHE_DB_FILE) +} + +fn create_migration_report( + data_dir: &Path, + bundle: &Path, + code: &str, + detail: &str, + imported_records: u64, + tracked_records: u64, +) -> Result { + let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); + fs::create_dir_all(&reports_dir).map_err(|source| { + CliError::Storage(format!( + "failed to create migration report directory: {source}" + )) + })?; + let report_path = reports_dir.join(report_file_name(code)); + let report = MigrationReport { + ok: code == "migration.imported", + code, + message: match code { + "migration.invalid_bundle" => "Legacy Room export bundle is invalid", + "migration.unsupported_bundle" => "Legacy Room bundle import is not implemented yet", + "migration.imported" => "Legacy Room bundle imported", + _ => "Legacy migration failed", + }, + bundle_file_name: bundle.file_name().and_then(|name| name.to_str()), + detail, + imported_records, + tracked_records, + }; + let bytes = serde_json::to_vec_pretty(&report) + .map_err(|source| CliError::Storage(format!("failed to serialize report: {source}")))?; + fs::write(&report_path, bytes) + .map_err(|source| CliError::Storage(format!("failed to write report: {source}")))?; + Ok(report_path) +} + +fn report_file_name(code: &str) -> String { + format!("{}.json", code.replace('.', "-")) +} + +#[derive(Debug, Serialize)] +struct MigrationReport<'a> { + ok: bool, + code: &'a str, + message: &'a str, + bundle_file_name: Option<&'a str>, + detail: &'a str, + imported_records: u64, + tracked_records: u64, +} + +fn success_envelope(command: &str, data: Value) -> String { + envelope_to_string(json!({ + "ok": true, + "command": command, + "data": data, + "warnings": [] + })) +} + +fn error_envelope(command: &str, error: &CliError) -> String { + let mut error_value = json!({ + "code": error.code(), + "message": error.message(), + }); + if let Some(detail) = error.detail() { + error_value["detail"] = json!(detail); + } + if let Some(report_path) = error.report_path() { + error_value["report_path"] = json!(report_path); + } + + envelope_to_string(json!({ + "ok": false, + "command": command, + "error": error_value, + })) +} + +fn envelope_to_string(value: Value) -> String { + let mut rendered = serde_json::to_string_pretty(&value).expect("JSON envelope is serializable"); + rendered.push('\n'); + rendered +} + +fn usage_text() -> String { + "Usage: getter --data-dir [--priority ]|repo eval |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle >\n".to_owned() +} + +#[derive(Debug, Deserialize)] +struct LegacyRoomBundle { + format: String, + version: u32, + #[serde(default)] + apps: Vec, +} + +#[derive(Debug, Deserialize)] +struct LegacyBundleApp { + kind: LegacyBundleAppKind, + installed_id: String, + #[serde(default)] + official_package_available: bool, + #[serde(default)] + common_conversion_available: bool, + #[serde(default)] + ignored_version: Option, + #[serde(default)] + favorite: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "snake_case")] +enum LegacyBundleAppKind { + Android, + Magisk, +} + +impl LegacyBundleAppKind { + fn to_legacy_kind(&self) -> Result { + Ok(match self { + Self::Android => LegacyAppKind::Android, + Self::Magisk => LegacyAppKind::Magisk, + }) + } +} + +impl CliCommand { + fn name(&self) -> &'static str { + match self { + Self::Init => "init", + Self::AppList => "app list", + Self::HubList => "hub list", + Self::RepoList => "repo list", + Self::RepoAdd { .. } => "repo add", + Self::RepoEval { .. } => "repo eval", + Self::PackageEval { .. } => "package eval", + Self::StorageValidate => "storage validate", + Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_global_data_dir_and_app_list_command() { + let invocation = + parse_args(["getter", "--data-dir", "/tmp/ua-getter", "app", "list"]).unwrap(); + + assert_eq!(invocation.data_dir, PathBuf::from("/tmp/ua-getter")); + assert_eq!(invocation.command, CliCommand::AppList); + } + + #[test] + fn run_init_creates_sqlite_database_files_and_json_envelope() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + + let output = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "init".to_owned(), + ]); + + assert_eq!(output.exit_code, ExitCode::Success); + assert!(data_dir.join(MAIN_DB_FILE).is_file()); + assert!(data_dir.join(CACHE_DB_FILE).is_file()); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "init"); + } + + #[test] + fn malformed_bundle_report_does_not_create_imported_state() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + let bundle = temp.path().join("bad.json"); + fs::write(&bundle, "not-json").unwrap(); + + let init = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "init".to_owned(), + ]); + assert_eq!(init.exit_code, ExitCode::Success); + + let output = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "legacy".to_owned(), + "import-room-bundle".to_owned(), + bundle.to_string_lossy().to_string(), + ]); + + assert_eq!(output.exit_code, ExitCode::Migration); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["ok"], false); + assert_eq!(json["error"]["code"], "migration.invalid_bundle"); + let report_path = json["error"]["report_path"].as_str().unwrap(); + assert!(Path::new(report_path).is_file()); + } +} diff --git a/crates/getter-cli/src/main.rs b/crates/getter-cli/src/main.rs new file mode 100644 index 0000000..dc49483 --- /dev/null +++ b/crates/getter-cli/src/main.rs @@ -0,0 +1,10 @@ +fn main() { + let output = getter_cli::run(std::env::args()); + if !output.stdout.is_empty() { + print!("{}", output.stdout); + } + if !output.stderr.is_empty() { + eprint!("{}", output.stderr); + } + std::process::exit(output.exit_code.code()); +} diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs new file mode 100644 index 0000000..807aaed --- /dev/null +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -0,0 +1,486 @@ +use cucumber::{given, then, when, World as _}; +use serde_json::Value; +use std::fs; +use std::path::PathBuf; +use std::process::Command; +use tempfile::TempDir; + +#[derive(Debug, Default, cucumber::World)] +struct CliWorld { + temp: Option, + data_dir: Option, + bundle: Option, + fixture_repo_id: Option, + fixture_repo_path: Option, + fixture_package_id: Option, + output: Option, + json: Option, +} + +#[given("an empty getter data directory")] +fn empty_getter_data_dir(world: &mut CliWorld) { + let temp = tempfile::tempdir().expect("create tempdir"); + let data_dir = temp.path().join("getter-data"); + world.temp = Some(temp); + world.data_dir = Some(data_dir); + world.json = None; +} + +#[given("an initialized getter data directory")] +fn initialized_getter_data_dir(world: &mut CliWorld) { + empty_getter_data_dir(world); + let output = run_getter(world, ["init".to_owned()]); + assert_success(&output); +} + +#[given("a corrupted legacy export bundle")] +fn corrupted_legacy_export_bundle(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let bundle = temp.path().join("corrupted-room-bundle.json"); + fs::write(&bundle, "not-json").expect("write corrupted bundle"); + world.bundle = Some(bundle); +} + +#[given("a syntactically valid legacy export bundle with an Android app")] +fn valid_legacy_export_bundle_with_android_app(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let bundle = temp.path().join("valid-room-bundle.json"); + fs::write( + &bundle, + r#"{ + "format": "upgradeall-legacy-room-bundle", + "version": 17, + "apps": [ + { + "kind": "android", + "installed_id": "org.fdroid.fdroid", + "official_package_available": true, + "ignored_version": "1.20.0", + "favorite": true + } + ] +}"#, + ) + .expect("write valid bundle"); + world.bundle = Some(bundle); +} + +#[given(expr = "a fixture Lua repository {string} with package {string}")] +fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { + create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); +} + +#[given(expr = "a fixture Lua repository {string} with package {string} named {string}")] +fn fixture_lua_repository_named( + world: &mut CliWorld, + repo_id: String, + package_id: String, + package_name: String, +) { + create_fixture_lua_repository(world, repo_id, package_id, package_name); +} + +#[when("I run getter init for that directory")] +fn run_getter_init(world: &mut CliWorld) { + let output = run_getter(world, ["init".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter app list for that directory")] +fn run_getter_app_list(world: &mut CliWorld) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter hub list for that directory")] +fn run_getter_hub_list(world: &mut CliWorld) { + let output = run_getter(world, ["hub".to_owned(), "list".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter repo list for that directory")] +fn run_getter_repo_list(world: &mut CliWorld) { + let output = run_getter(world, ["repo".to_owned(), "list".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter storage validate for that directory")] +fn run_getter_storage_validate(world: &mut CliWorld) { + let output = run_getter(world, ["storage".to_owned(), "validate".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter repo add for that repository with priority 0")] +fn run_getter_repo_add(world: &mut CliWorld) { + let repo_id = world + .fixture_repo_id + .as_ref() + .expect("fixture repo id exists") + .clone(); + run_getter_repo_add_with_priority(world, &repo_id, 0); +} + +#[when(expr = "I run getter repo add for repository {string} with priority {int}")] +fn run_getter_repo_add_named(world: &mut CliWorld, repo_id: String, priority: i32) { + run_getter_repo_add_with_priority(world, &repo_id, priority); +} + +#[when("I run getter repo eval for that repository")] +fn run_getter_repo_eval(world: &mut CliWorld) { + let repo_id = world + .fixture_repo_id + .as_ref() + .expect("fixture repo id exists"); + let output = run_getter( + world, + ["repo".to_owned(), "eval".to_owned(), repo_id.to_owned()], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter package eval for that fixture package")] +fn run_getter_package_eval(world: &mut CliWorld) { + let repo_id = world + .fixture_repo_id + .as_ref() + .expect("fixture repo id exists"); + let package_id = world + .fixture_package_id + .as_ref() + .expect("fixture package id exists"); + let output = run_getter( + world, + [ + "package".to_owned(), + "eval".to_owned(), + package_id.to_owned(), + "--repo".to_owned(), + repo_id.to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when(expr = "I run getter package eval for package {string}")] +fn run_getter_package_eval_without_repo(world: &mut CliWorld, package_id: String) { + let output = run_getter(world, ["package".to_owned(), "eval".to_owned(), package_id]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter legacy import-room-bundle for that bundle")] +fn run_getter_legacy_import(world: &mut CliWorld) { + let bundle = world.bundle.as_ref().expect("bundle exists"); + let output = run_getter( + world, + [ + "legacy".to_owned(), + "import-room-bundle".to_owned(), + bundle.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[then("the command succeeds")] +fn command_succeeds(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_success(output); +} + +#[then("the command fails with a documented migration error")] +fn command_fails_with_migration_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(20)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["command"], "legacy import-room-bundle"); + assert_eq!(json["error"]["code"], "migration.invalid_bundle"); + world.json = Some(json); +} + +#[then("the output is valid JSON")] +fn output_is_valid_json(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + let json = parse_stdout(output); + assert!( + json.is_object(), + "stdout JSON should be an object: {json:?}" + ); + world.json = Some(json); +} + +#[then("the getter data directory is usable")] +fn getter_data_directory_is_usable(world: &mut CliWorld) { + let data_dir = world.data_dir.as_ref().expect("data dir exists"); + assert!(data_dir.join("main.db").is_file(), "main.db should exist"); + assert!(data_dir.join("cache.db").is_file(), "cache.db should exist"); + + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); +} + +#[then("the output contains an empty app list")] +fn output_contains_empty_app_list(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "app list"); + assert_eq!(json["data"]["apps"], Value::Array(Vec::new())); +} + +#[then("the output contains an empty hub list")] +fn output_contains_empty_hub_list(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "hub list"); + assert_eq!(json["data"]["hubs"], Value::Array(Vec::new())); +} + +#[then("the output contains an empty repository list")] +fn output_contains_empty_repository_list(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "repo list"); + assert_eq!(json["data"]["repositories"], Value::Array(Vec::new())); +} + +#[then("the output reports valid storage")] +fn output_reports_valid_storage(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "storage validate"); + assert_eq!(json["data"]["valid"], true); + assert!(json["data"]["main_db"].as_str().is_some()); + assert!(json["data"]["cache_db"].as_str().is_some()); +} + +#[then("the output contains the added repository")] +fn output_contains_added_repository(world: &mut CliWorld) { + let repo_id = world + .fixture_repo_id + .as_ref() + .expect("fixture repo id exists") + .clone(); + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "repo add"); + assert_eq!(json["data"]["repository"]["id"], repo_id); +} + +#[then("the output contains the evaluated fixture package")] +fn output_contains_evaluated_fixture_package(world: &mut CliWorld) { + let package_id = world + .fixture_package_id + .as_ref() + .expect("fixture package id exists") + .clone(); + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "repo eval"); + let packages = json["data"]["packages"] + .as_array() + .expect("packages should be an array"); + assert!( + packages + .iter() + .any(|package| package["id"].as_str() == Some(package_id.as_str())), + "repo eval packages should include {package_id}: {packages:?}" + ); +} + +#[then("the output contains the fixture package")] +fn output_contains_fixture_package(world: &mut CliWorld) { + let package_id = world + .fixture_package_id + .as_ref() + .expect("fixture package id exists") + .clone(); + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "package eval"); + assert_eq!(json["data"]["package"]["id"], package_id); + assert_eq!(json["data"]["package"]["name"], "F-Droid"); +} + +#[then(expr = "the output contains package {string} named {string}")] +fn output_contains_named_package(world: &mut CliWorld, package_id: String, package_name: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "package eval"); + assert_eq!(json["data"]["package"]["id"], package_id); + assert_eq!(json["data"]["package"]["name"], package_name); +} + +#[then("no partially usable imported state is created")] +fn no_partially_usable_imported_state(world: &mut CliWorld) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); + let json = parse_stdout(&output); + assert_eq!(json["data"]["apps"], Value::Array(Vec::new())); +} + +#[then("a sanitized migration report is available")] +fn sanitized_migration_report_available(world: &mut CliWorld) { + let json = current_json(world); + let report_path = json["error"]["report_path"] + .as_str() + .expect("report_path should be a string"); + let report = fs::read_to_string(report_path).expect("report should be readable"); + let report_json: Value = serde_json::from_str(&report).expect("report should be JSON"); + assert_eq!(report_json["ok"], false); + assert_eq!(report_json["imported_records"], 0); + assert!(report_json.get("bundle_file_name").is_some()); + assert!( + report_json.get("raw_bundle").is_none(), + "report must not include raw bundle content" + ); +} + +#[then("the import reports one tracked app")] +fn import_reports_one_tracked_app(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "legacy import-room-bundle"); + assert_eq!(json["data"]["imported_records"], 1); + assert_eq!( + json["data"]["apps"].as_array().expect("apps array").len(), + 1 + ); +} + +#[then(expr = "the app list contains imported package {string}")] +fn app_list_contains_imported_package(world: &mut CliWorld, package_id: String) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); + let json = parse_stdout(&output); + let apps = json["data"]["apps"].as_array().expect("apps array"); + let app = apps + .iter() + .find(|app| app["id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); + assert_eq!(app["favorite"], true); + assert_eq!(app["ignored_version"], "1.20.0"); + assert_eq!(app["package_resolution"], "official_repository_package"); + world.output = Some(output); + world.json = Some(json); +} + +fn current_json(world: &mut CliWorld) -> &Value { + world + .json + .get_or_insert_with(|| parse_stdout(world.output.as_ref().expect("command output exists"))) +} + +fn create_fixture_lua_repository( + world: &mut CliWorld, + repo_id: String, + package_id: String, + package_name: String, +) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let repo_path = temp.path().join(format!("repo-{repo_id}")); + let package_name_path = package_id + .strip_prefix("android/") + .expect("fixture package id should be android package id"); + fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); + fs::create_dir(repo_path.join("lib")).expect("create lib dir"); + fs::create_dir(repo_path.join("templates")).expect("create templates dir"); + fs::write( + repo_path.join("repo.toml"), + format!( + "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" + ), + ) + .expect("write repo.toml"); + fs::write( + repo_path.join(format!("packages/android/{package_name_path}.lua")), + format!( + r#" +return package_def {{ + id = "{package_id}", + name = "{package_name}", + installed = {{ + {{ kind = "android_package", package_name = "{package_name_path}" }}, + }}, +}} +"# + ), + ) + .expect("write package Lua"); + + world.fixture_repo_id = Some(repo_id); + world.fixture_repo_path = Some(repo_path); + world.fixture_package_id = Some(package_id); +} + +fn run_getter_repo_add_with_priority(world: &mut CliWorld, repo_id: &str, priority: i32) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let repo_path = temp.path().join(format!("repo-{repo_id}")); + let output = run_getter( + world, + [ + "repo".to_owned(), + "add".to_owned(), + repo_id.to_owned(), + repo_path.to_string_lossy().to_string(), + "--priority".to_owned(), + priority.to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +fn run_getter(world: &CliWorld, command_args: I) -> std::process::Output +where + I: IntoIterator, +{ + let exe = env!("CARGO_BIN_EXE_getter-cli"); + let data_dir = world.data_dir.as_ref().expect("data dir exists"); + let mut command = Command::new(exe); + command.arg("--data-dir").arg(data_dir); + for arg in command_args { + command.arg(arg); + } + command.output().expect("run getter binary") +} + +fn assert_success(output: &std::process::Output) { + assert_eq!( + output.status.code(), + Some(0), + "stdout:\n{}\nstderr:\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + let json = parse_stdout(output); + assert_eq!(json["ok"], true); +} + +fn parse_stdout(output: &std::process::Output) -> Value { + serde_json::from_slice(&output.stdout).unwrap_or_else(|error| { + panic!( + "stdout should be JSON: {error}\nstdout:\n{}\nstderr:\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) + }) +} + +#[tokio::main] +async fn main() { + let features = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/features/cli"); + CliWorld::cucumber() + .fail_on_skipped() + .max_concurrent_scenarios(Some(1)) + .run_and_exit(features) + .await; +} diff --git a/crates/getter-cli/tests/features/cli/app_list.feature b/crates/getter-cli/tests/features/cli/app_list.feature new file mode 100644 index 0000000..3baa858 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/app_list.feature @@ -0,0 +1,8 @@ +@getter-cli @smoke +Feature: Getter CLI app listing + Scenario: User lists apps before adding any app records + Given an initialized getter data directory + When I run getter app list for that directory + Then the command succeeds + And the output is valid JSON + And the output contains an empty app list diff --git a/crates/getter-cli/tests/features/cli/hub_list.feature b/crates/getter-cli/tests/features/cli/hub_list.feature new file mode 100644 index 0000000..3e467a2 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/hub_list.feature @@ -0,0 +1,8 @@ +@getter-cli @smoke +Feature: Getter CLI hub listing + Scenario: User lists hubs before adding any hub records + Given an initialized getter data directory + When I run getter hub list for that directory + Then the command succeeds + And the output is valid JSON + And the output contains an empty hub list diff --git a/crates/getter-cli/tests/features/cli/init.feature b/crates/getter-cli/tests/features/cli/init.feature new file mode 100644 index 0000000..51cb38a --- /dev/null +++ b/crates/getter-cli/tests/features/cli/init.feature @@ -0,0 +1,8 @@ +@getter-cli @smoke +Feature: Getter CLI initialization + Scenario: User initializes a new getter data directory + Given an empty getter data directory + When I run getter init for that directory + Then the command succeeds + And the output is valid JSON + And the getter data directory is usable diff --git a/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature b/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature new file mode 100644 index 0000000..864417f --- /dev/null +++ b/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature @@ -0,0 +1,18 @@ +@getter-cli @migration +Feature: Legacy import failure recovery + Scenario: User receives a non-destructive report when legacy import fails + Given an initialized getter data directory + And a corrupted legacy export bundle + When I run getter legacy import-room-bundle for that bundle + Then the command fails with a documented migration error + And no partially usable imported state is created + And a sanitized migration report is available + + Scenario: User imports a valid legacy bundle into tracked app state + Given an initialized getter data directory + And a syntactically valid legacy export bundle with an Android app + When I run getter legacy import-room-bundle for that bundle + Then the command succeeds + And the output is valid JSON + And the import reports one tracked app + And the app list contains imported package "android/org.fdroid.fdroid" diff --git a/crates/getter-cli/tests/features/cli/repo_list.feature b/crates/getter-cli/tests/features/cli/repo_list.feature new file mode 100644 index 0000000..0ffd2d8 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/repo_list.feature @@ -0,0 +1,8 @@ +@getter-cli @smoke +Feature: Getter CLI repository listing + Scenario: User lists repositories before adding any repository records + Given an initialized getter data directory + When I run getter repo list for that directory + Then the command succeeds + And the output is valid JSON + And the output contains an empty repository list diff --git a/crates/getter-cli/tests/features/cli/repository_overlay.feature b/crates/getter-cli/tests/features/cli/repository_overlay.feature new file mode 100644 index 0000000..d5906cb --- /dev/null +++ b/crates/getter-cli/tests/features/cli/repository_overlay.feature @@ -0,0 +1,14 @@ +@getter-cli @repository +Feature: Getter CLI repository overlay resolution + Scenario: Highest-priority repository package wins by package id + Given an initialized getter data directory + And a fixture Lua repository "official" with package "android/org.fdroid.fdroid" named "Official F-Droid" + And a fixture Lua repository "local" with package "android/org.fdroid.fdroid" named "Local F-Droid" + When I run getter repo add for repository "official" with priority 0 + Then the command succeeds + When I run getter repo add for repository "local" with priority 100 + Then the command succeeds + When I run getter package eval for package "android/org.fdroid.fdroid" + Then the command succeeds + And the output is valid JSON + And the output contains package "android/org.fdroid.fdroid" named "Local F-Droid" diff --git a/crates/getter-cli/tests/features/cli/repository_package_eval.feature b/crates/getter-cli/tests/features/cli/repository_package_eval.feature new file mode 100644 index 0000000..4edefc2 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/repository_package_eval.feature @@ -0,0 +1,17 @@ +@getter-cli @repository +Feature: Getter CLI repository and package evaluation + Scenario: User adds and evaluates a fixture Lua repository + Given an initialized getter data directory + And a fixture Lua repository "official" with package "android/org.fdroid.fdroid" + When I run getter repo add for that repository with priority 0 + Then the command succeeds + And the output is valid JSON + And the output contains the added repository + When I run getter repo eval for that repository + Then the command succeeds + And the output is valid JSON + And the output contains the evaluated fixture package + When I run getter package eval for that fixture package + Then the command succeeds + And the output is valid JSON + And the output contains the fixture package diff --git a/crates/getter-cli/tests/features/cli/storage_validate.feature b/crates/getter-cli/tests/features/cli/storage_validate.feature new file mode 100644 index 0000000..815edb3 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/storage_validate.feature @@ -0,0 +1,8 @@ +@getter-cli @smoke +Feature: Getter CLI storage validation + Scenario: User validates initialized getter storage + Given an initialized getter data directory + When I run getter storage validate for that directory + Then the command succeeds + And the output is valid JSON + And the output reports valid storage diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml new file mode 100644 index 0000000..47326c5 --- /dev/null +++ b/crates/getter-core/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "getter-core" +version.workspace = true +edition.workspace = true + +[dependencies] +mlua = { version = "0.10", features = ["lua54", "vendored"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "1" +toml = "0.8" + +[dev-dependencies] +tempfile = "3" diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs new file mode 100644 index 0000000..45a464d --- /dev/null +++ b/crates/getter-core/src/lib.rs @@ -0,0 +1,408 @@ +//! Core domain model for the UpgradeAll getter rewrite. +//! +//! `getter-core` owns product/domain behavior. Flutter and Android code are +//! platform/UI adapters and must not reimplement package, repository, update or +//! storage rules. + +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::fmt; +use std::str::FromStr; + +pub mod lua; +pub mod repository; + +/// Error returned when parsing or constructing a [`PackageId`]. +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum PackageIdError { + #[error("package id is empty")] + Empty, + #[error("package id must be '/'")] + MissingSeparator, + #[error("package id kind is empty")] + EmptyKind, + #[error("package id name is empty")] + EmptyName, + #[error("unsupported package kind '{0}'")] + UnsupportedKind(String), + #[error("package id kind '{0}' contains invalid characters")] + InvalidKind(String), + #[error("package id name '{0}' contains invalid characters")] + InvalidName(String), +} + +/// Known package target kinds in v1. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum PackageKind { + Android, + Magisk, + Generic, +} + +impl PackageKind { + pub fn as_str(self) -> &'static str { + match self { + Self::Android => "android", + Self::Magisk => "magisk", + Self::Generic => "generic", + } + } +} + +impl fmt::Display for PackageKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl FromStr for PackageKind { + type Err = PackageIdError; + + fn from_str(value: &str) -> Result { + match value { + "android" => Ok(Self::Android), + "magisk" => Ok(Self::Magisk), + "generic" => Ok(Self::Generic), + other => Err(PackageIdError::UnsupportedKind(other.to_owned())), + } + } +} + +/// Readable package identifier such as `android/org.fdroid.fdroid`. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(try_from = "String", into = "String")] +pub struct PackageId { + kind: PackageKind, + name: String, +} + +impl PackageId { + pub fn new(kind: PackageKind, name: impl Into) -> Result { + let name = name.into(); + validate_name(&name)?; + Ok(Self { kind, name }) + } + + pub fn kind(&self) -> PackageKind { + self.kind + } + + pub fn name(&self) -> &str { + &self.name + } +} + +impl fmt::Display for PackageId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}/{}", self.kind, self.name) + } +} + +impl FromStr for PackageId { + type Err = PackageIdError; + + fn from_str(value: &str) -> Result { + if value.is_empty() { + return Err(PackageIdError::Empty); + } + let (kind, name) = value + .split_once('/') + .ok_or(PackageIdError::MissingSeparator)?; + if kind.is_empty() { + return Err(PackageIdError::EmptyKind); + } + if name.is_empty() { + return Err(PackageIdError::EmptyName); + } + validate_kind_segment(kind)?; + validate_name(name)?; + Ok(Self { + kind: kind.parse()?, + name: name.to_owned(), + }) + } +} + +impl TryFrom for PackageId { + type Error = PackageIdError; + + fn try_from(value: String) -> Result { + value.parse() + } +} + +impl From for String { + fn from(value: PackageId) -> Self { + value.to_string() + } +} + +/// Repository identifier such as `official`, `community`, `local`, or +/// `local_autogen`. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(try_from = "String", into = "String")] +pub struct RepositoryId(String); + +impl RepositoryId { + pub fn new(value: impl Into) -> Result { + let value = value.into(); + validate_repository_id(&value)?; + Ok(Self(value)) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl fmt::Display for RepositoryId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +impl FromStr for RepositoryId { + type Err = RepositoryIdError; + + fn from_str(value: &str) -> Result { + Self::new(value) + } +} + +impl TryFrom for RepositoryId { + type Error = RepositoryIdError; + + fn try_from(value: String) -> Result { + Self::new(value) + } +} + +impl From for String { + fn from(value: RepositoryId) -> Self { + value.0 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum RepositoryIdError { + #[error("repository id is empty")] + Empty, + #[error("repository id '{0}' contains invalid characters")] + Invalid(String), +} + +/// Higher repository priority wins during overlay resolution. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct RepositoryPriority(i32); + +impl RepositoryPriority { + pub const LOCAL: Self = Self(100); + pub const DEFAULT: Self = Self(0); + pub const LOCAL_AUTOGEN: Self = Self(-1); + + pub const fn new(value: i32) -> Self { + Self(value) + } + + pub const fn value(self) -> i32 { + self.0 + } + + pub fn cmp_winner(self, other: Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +impl Default for RepositoryPriority { + fn default() -> Self { + Self::DEFAULT + } +} + +impl Ord for RepositoryPriority { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +impl PartialOrd for RepositoryPriority { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Package metadata after repository resolution and Lua validation. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ResolvedPackage { + pub id: PackageId, + pub repository: RepositoryId, + pub name: String, + #[serde(default)] + pub installed: Vec, + #[serde(default)] + pub permissions: PackagePermissions, + #[serde(default)] + pub source_priority: Vec, +} + +/// Installed target matched by a package, such as an Android package name. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum InstalledTarget { + AndroidPackage { package_name: String }, + MagiskModule { module_id: String }, + Generic { id: String }, +} + +/// Package permission declaration relevant to UI warnings and Lua host APIs. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct PackagePermissions { + #[serde(default)] + pub free_network: bool, +} + +/// Candidate version/update discovered by provider/package logic. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct UpdateCandidate { + pub version: String, + #[serde(default)] + pub channel: Option, + #[serde(default)] + pub source: Option, + #[serde(default)] + pub artifacts: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct UpdateArtifact { + pub name: String, + pub url: String, + #[serde(default)] + pub file_name: Option, +} + +/// Candidate selected for update after package/user-state policy. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SelectedUpdate { + pub package_id: PackageId, + pub candidate: UpdateCandidate, + #[serde(default)] + pub artifact: Option, +} + +/// Executable update actions generated by the `resolve` lifecycle phase. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum UpdateAction { + Download { url: String, file_name: String }, + Install { installer: String, file: String }, + OpenUrl { url: String }, +} + +fn validate_kind_segment(value: &str) -> Result<(), PackageIdError> { + if value + .chars() + .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-') + { + Ok(()) + } else { + Err(PackageIdError::InvalidKind(value.to_owned())) + } +} + +fn validate_name(value: &str) -> Result<(), PackageIdError> { + if value.is_empty() { + return Err(PackageIdError::EmptyName); + } + if value.starts_with('/') || value.ends_with('/') || value.contains("//") { + return Err(PackageIdError::InvalidName(value.to_owned())); + } + if value + .chars() + .all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-' | '+' | '/')) + { + Ok(()) + } else { + Err(PackageIdError::InvalidName(value.to_owned())) + } +} + +fn validate_repository_id(value: &str) -> Result<(), RepositoryIdError> { + if value.is_empty() { + return Err(RepositoryIdError::Empty); + } + if value + .chars() + .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | '.')) + { + Ok(()) + } else { + Err(RepositoryIdError::Invalid(value.to_owned())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_readable_android_package_id() { + let id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + assert_eq!(id.kind(), PackageKind::Android); + assert_eq!(id.name(), "org.fdroid.fdroid"); + assert_eq!(id.to_string(), "android/org.fdroid.fdroid"); + } + + #[test] + fn parses_readable_magisk_package_id() { + let id: PackageId = "magisk/zygisk-next".parse().unwrap(); + assert_eq!(id.kind(), PackageKind::Magisk); + assert_eq!(id.name(), "zygisk-next"); + assert_eq!(id.to_string(), "magisk/zygisk-next"); + } + + #[test] + fn rejects_invalid_package_ids() { + assert_eq!("".parse::(), Err(PackageIdError::Empty)); + assert_eq!( + "android".parse::(), + Err(PackageIdError::MissingSeparator) + ); + assert_eq!( + "/org.fdroid.fdroid".parse::(), + Err(PackageIdError::EmptyKind) + ); + assert_eq!( + "android/".parse::(), + Err(PackageIdError::EmptyName) + ); + assert_eq!( + "hub/org.fdroid.fdroid".parse::(), + Err(PackageIdError::UnsupportedKind("hub".to_owned())) + ); + assert!("android/org fdroid".parse::().is_err()); + } + + #[test] + fn repository_priority_higher_number_wins() { + assert!(RepositoryPriority::LOCAL > RepositoryPriority::DEFAULT); + assert!(RepositoryPriority::DEFAULT > RepositoryPriority::LOCAL_AUTOGEN); + assert_eq!(RepositoryPriority::LOCAL.value(), 100); + assert_eq!(RepositoryPriority::DEFAULT.value(), 0); + assert_eq!(RepositoryPriority::LOCAL_AUTOGEN.value(), -1); + } + + #[test] + fn repository_id_accepts_named_repositories() { + assert_eq!( + RepositoryId::new("local_autogen").unwrap().as_str(), + "local_autogen" + ); + assert_eq!( + RepositoryId::new("community.1").unwrap().to_string(), + "community.1" + ); + assert!(RepositoryId::new("bad/repo").is_err()); + } +} diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs new file mode 100644 index 0000000..fd6348e --- /dev/null +++ b/crates/getter-core/src/lua.rs @@ -0,0 +1,460 @@ +//! Minimal Lua package-file evaluation and Rust validation boundary. + +use crate::repository::RepositoryLayout; +use crate::{InstalledTarget, PackageId, PackagePermissions, ResolvedPackage}; +use mlua::{Lua, Table, Value}; +use serde_json::{Map, Number, Value as JsonValue}; +use std::fs; +use std::path::{Path, PathBuf}; + +#[derive(Debug, thiserror::Error)] +pub enum LuaPackageError { + #[error("failed to read Lua package file {path}: {source}")] + ReadFile { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("Lua runtime error in {path}: {source}")] + Runtime { + path: PathBuf, + #[source] + source: mlua::Error, + }, + #[error("Lua package {path} did not return a table")] + NotATable { path: PathBuf }, + #[error("Lua package {path} returned unsupported value at {location}: {value_type}")] + UnsupportedValue { + path: PathBuf, + location: String, + value_type: &'static str, + }, + #[error("schema validation failed for {path}: {message}")] + Schema { path: PathBuf, message: String }, + #[error("domain validation failed for {path}: {message}")] + Domain { path: PathBuf, message: String }, +} + +/// Evaluate and validate a Lua package file from a repository layout. +pub fn evaluate_package_file( + repository: &RepositoryLayout, + path: impl AsRef, +) -> Result { + let path = path.as_ref(); + let source = fs::read_to_string(path).map_err(|source| LuaPackageError::ReadFile { + path: path.to_path_buf(), + source, + })?; + evaluate_package_source(repository, path, &source) +} + +/// Evaluate package source text. This is public for focused tests and future CLI +/// plumbing; repository callers should normally use [`evaluate_package_file`]. +pub fn evaluate_package_source( + repository: &RepositoryLayout, + path: impl AsRef, + source: &str, +) -> Result { + let path = path.as_ref().to_path_buf(); + let lua = Lua::new(); + configure_package_path(&lua, repository).map_err(|source| LuaPackageError::Runtime { + path: path.clone(), + source, + })?; + install_helpers(&lua).map_err(|source| LuaPackageError::Runtime { + path: path.clone(), + source, + })?; + + let value = lua + .load(source) + .set_name(path.to_string_lossy().as_ref()) + .eval::() + .map_err(|source| LuaPackageError::Runtime { + path: path.clone(), + source, + })?; + let table = match value { + Value::Table(table) => table, + _ => return Err(LuaPackageError::NotATable { path }), + }; + let json = lua_table_to_json(&path, "$", table)?; + validate_package_json(repository, &path, json) +} + +fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Result<()> { + let package: Table = lua.globals().get("package")?; + let current_path: String = package.get("path")?; + let lib_pattern = repository.lib_dir.join("?.lua"); + let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); + let new_path = format!( + "{};{};{}", + lib_pattern.to_string_lossy(), + nested_lib_pattern.to_string_lossy(), + current_path + ); + package.set("path", new_path) +} + +fn install_helpers(lua: &Lua) -> mlua::Result<()> { + let package_fn = lua.create_function(|_, table: Table| Ok(table))?; + lua.globals().set("package_def", package_fn.clone())?; + lua.globals().set("android_app", package_fn.clone())?; + lua.globals().set("magisk_module", package_fn.clone())?; + lua.globals().set("generic_package", package_fn)?; + Ok(()) +} + +fn lua_table_to_json( + path: &Path, + location: &str, + table: Table, +) -> Result { + if is_array_table(&table).map_err(|source| LuaPackageError::Runtime { + path: path.to_path_buf(), + source, + })? { + let mut array = Vec::new(); + for pair in table.sequence_values::() { + let value = pair.map_err(|source| LuaPackageError::Runtime { + path: path.to_path_buf(), + source, + })?; + array.push(lua_value_to_json(path, &format!("{location}[]"), value)?); + } + Ok(JsonValue::Array(array)) + } else { + let mut object = Map::new(); + for pair in table.pairs::() { + let (key, value) = pair.map_err(|source| LuaPackageError::Runtime { + path: path.to_path_buf(), + source, + })?; + let key = match key { + Value::String(value) => value + .to_str() + .map_err(|source| LuaPackageError::Runtime { + path: path.to_path_buf(), + source, + })? + .to_owned(), + Value::Integer(value) => value.to_string(), + _ => { + return Err(LuaPackageError::UnsupportedValue { + path: path.to_path_buf(), + location: format!("{location}."), + value_type: key.type_name(), + }) + } + }; + let child_location = format!("{location}.{key}"); + object.insert(key, lua_value_to_json(path, &child_location, value)?); + } + Ok(JsonValue::Object(object)) + } +} + +fn lua_value_to_json( + path: &Path, + location: &str, + value: Value, +) -> Result { + match value { + Value::Nil => Ok(JsonValue::Null), + Value::Boolean(value) => Ok(JsonValue::Bool(value)), + Value::Integer(value) => Ok(JsonValue::Number(Number::from(value))), + Value::Number(value) => Number::from_f64(value) + .map(JsonValue::Number) + .ok_or_else(|| LuaPackageError::UnsupportedValue { + path: path.to_path_buf(), + location: location.to_owned(), + value_type: "non-finite number", + }), + Value::String(value) => Ok(JsonValue::String( + value + .to_str() + .map_err(|source| LuaPackageError::Runtime { + path: path.to_path_buf(), + source, + })? + .to_owned(), + )), + Value::Table(table) => lua_table_to_json(path, location, table), + _ => Err(LuaPackageError::UnsupportedValue { + path: path.to_path_buf(), + location: location.to_owned(), + value_type: value.type_name(), + }), + } +} + +fn is_array_table(table: &Table) -> mlua::Result { + let len = table.raw_len(); + if len == 0 { + // Empty tables are accepted as objects by default. Package schemas avoid + // ambiguous empty array/object requirements at this boundary. + return Ok(false); + } + let mut count = 0usize; + for pair in table.clone().pairs::() { + let (key, _) = pair?; + match key { + Value::Integer(index) if index >= 1 && (index as usize) <= len => count += 1, + _ => return Ok(false), + } + } + Ok(count == len) +} + +fn validate_package_json( + repository: &RepositoryLayout, + path: &Path, + value: JsonValue, +) -> Result { + let object = value.as_object().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "package value must be an object".to_owned(), + })?; + + let id = required_string(path, object, "id")?; + let id: PackageId = id.parse().map_err(|source| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("invalid package id: {source}"), + })?; + let path_id = crate::repository::package_id_from_path(&repository.packages_dir, path).map_err( + |source| LuaPackageError::Domain { + path: path.to_path_buf(), + message: format!("failed to derive package id from path: {source}"), + }, + )?; + if id != path_id { + return Err(LuaPackageError::Domain { + path: path.to_path_buf(), + message: format!("package id '{id}' does not match path-derived id '{path_id}'"), + }); + } + + let name = required_string(path, object, "name")?.to_owned(); + let installed = parse_installed_targets(path, object.get("installed"))?; + let permissions = parse_permissions(path, object.get("permissions"))?; + let source_priority = + parse_string_array(path, "source_priority", object.get("source_priority"))?; + + Ok(ResolvedPackage { + id, + repository: repository.metadata.id.clone(), + name, + installed, + permissions, + source_priority, + }) +} + +fn required_string<'a>( + path: &Path, + object: &'a Map, + field: &str, +) -> Result<&'a str, LuaPackageError> { + object + .get(field) + .and_then(JsonValue::as_str) + .ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("required string field '{field}' is missing"), + }) +} + +fn parse_installed_targets( + path: &Path, + value: Option<&JsonValue>, +) -> Result, LuaPackageError> { + let Some(value) = value else { + return Ok(Vec::new()); + }; + let array = value.as_array().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "field 'installed' must be an array".to_owned(), + })?; + array + .iter() + .map(|item| { + let object = item.as_object().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "installed entries must be objects".to_owned(), + })?; + let kind = required_string(path, object, "kind")?; + match kind { + "android_package" => Ok(InstalledTarget::AndroidPackage { + package_name: required_string(path, object, "package_name")?.to_owned(), + }), + "magisk_module" => Ok(InstalledTarget::MagiskModule { + module_id: required_string(path, object, "module_id")?.to_owned(), + }), + "generic" => Ok(InstalledTarget::Generic { + id: required_string(path, object, "id")?.to_owned(), + }), + other => Err(LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("unknown installed target kind '{other}'"), + }), + } + }) + .collect() +} + +fn parse_permissions( + path: &Path, + value: Option<&JsonValue>, +) -> Result { + let Some(value) = value else { + return Ok(PackagePermissions::default()); + }; + let object = value.as_object().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "field 'permissions' must be an object".to_owned(), + })?; + Ok(PackagePermissions { + free_network: object + .get("free_network") + .and_then(JsonValue::as_bool) + .unwrap_or(false), + }) +} + +fn parse_string_array( + path: &Path, + field: &str, + value: Option<&JsonValue>, +) -> Result, LuaPackageError> { + let Some(value) = value else { + return Ok(Vec::new()); + }; + let array = value.as_array().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("field '{field}' must be an array"), + })?; + array + .iter() + .map(|item| { + item.as_str() + .map(str::to_owned) + .ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("field '{field}' must contain only strings"), + }) + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::repository::RepositoryLayout; + use std::fs; + + fn fixture_repo() -> (tempfile::TempDir, RepositoryLayout, PathBuf) { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::write( + root.join("repo.toml"), + r#"id = "official" +name = "UpgradeAll Official" +priority = 0 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::create_dir(root.join("packages")).unwrap(); + fs::create_dir(root.join("packages/android")).unwrap(); + fs::create_dir(root.join("lib")).unwrap(); + fs::create_dir(root.join("templates")).unwrap(); + let package_path = root.join("packages/android/org.fdroid.fdroid.lua"); + fs::write(&package_path, "return {}").unwrap(); + let layout = RepositoryLayout::load(root).unwrap(); + (temp, layout, package_path) + } + + #[test] + fn evaluates_json_like_lua_package_table() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + &package_path, + r#" +return package_def { + id = "android/org.fdroid.fdroid", + name = "F-Droid", + installed = { + { kind = "android_package", package_name = "org.fdroid.fdroid" }, + }, + permissions = { free_network = true }, + source_priority = { "github", "fdroid" }, +} +"#, + ) + .unwrap(); + + let package = evaluate_package_file(&layout, &package_path).unwrap(); + assert_eq!(package.id.to_string(), "android/org.fdroid.fdroid"); + assert_eq!(package.repository.as_str(), "official"); + assert_eq!(package.name, "F-Droid"); + assert_eq!( + package.installed, + vec![InstalledTarget::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned() + }] + ); + assert!(package.permissions.free_network); + assert_eq!(package.source_priority, vec!["github", "fdroid"]); + } + + #[test] + fn rejects_package_id_that_does_not_match_path() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + &package_path, + r#"return { id = "android/com.termux", name = "Termux" }"#, + ) + .unwrap(); + + let err = evaluate_package_file(&layout, &package_path).unwrap_err(); + assert!(matches!(err, LuaPackageError::Domain { .. })); + } + + #[test] + fn require_can_load_repository_lib_modules() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + layout.lib_dir.join("android.lua"), + r#" +return { + local_app = function(input) + return { + id = input.id, + name = input.name, + installed = { + { kind = "android_package", package_name = input.package_name }, + }, + } + end +} +"#, + ) + .unwrap(); + fs::write( + &package_path, + r#" +local android = require("android") +return android.local_app { + id = "android/org.fdroid.fdroid", + name = "F-Droid", + package_name = "org.fdroid.fdroid", +} +"#, + ) + .unwrap(); + + let package = evaluate_package_file(&layout, &package_path).unwrap(); + assert_eq!(package.name, "F-Droid"); + assert_eq!(package.installed.len(), 1); + } +} diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs new file mode 100644 index 0000000..0bacf47 --- /dev/null +++ b/crates/getter-core/src/repository.rs @@ -0,0 +1,288 @@ +//! Repository layout loading for Lua package repositories. + +use crate::{PackageId, PackageIdError, RepositoryId, RepositoryIdError, RepositoryPriority}; +use serde::Deserialize; +use std::fs; +use std::path::{Path, PathBuf}; + +pub const REPO_API_VERSION_V1: &str = "getter.repo.v1"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryLayout { + pub root: PathBuf, + pub metadata: RepositoryMetadata, + pub packages_dir: PathBuf, + pub lib_dir: PathBuf, + pub templates_dir: PathBuf, + pub packages: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryMetadata { + pub id: RepositoryId, + pub name: String, + pub priority: RepositoryPriority, + pub api_version: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PackageFile { + pub id: PackageId, + pub path: PathBuf, +} + +#[derive(Debug, thiserror::Error)] +pub enum RepositoryLoadError { + #[error("failed to read repo.toml at {path}: {source}")] + ReadRepoToml { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("failed to parse repo.toml at {path}: {source}")] + ParseRepoToml { + path: PathBuf, + #[source] + source: toml::de::Error, + }, + #[error("invalid repository id in repo.toml: {0}")] + RepositoryId(#[from] RepositoryIdError), + #[error("unsupported repository api_version '{0}'")] + UnsupportedApiVersion(String), + #[error("repository path {path} is missing required directory '{directory}'")] + MissingDirectory { + path: PathBuf, + directory: &'static str, + }, + #[error("failed to read packages directory {path}: {source}")] + ReadPackagesDir { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("invalid package path {path}: {reason}")] + InvalidPackagePath { path: PathBuf, reason: String }, + #[error("invalid package id derived from {path}: {source}")] + PackageId { + path: PathBuf, + #[source] + source: PackageIdError, + }, +} + +impl RepositoryLayout { + pub fn load(root: impl AsRef) -> Result { + let root = root.as_ref().to_path_buf(); + let repo_toml_path = root.join("repo.toml"); + let raw = fs::read_to_string(&repo_toml_path).map_err(|source| { + RepositoryLoadError::ReadRepoToml { + path: repo_toml_path.clone(), + source, + } + })?; + let raw_metadata: RawRepositoryMetadata = + toml::from_str(&raw).map_err(|source| RepositoryLoadError::ParseRepoToml { + path: repo_toml_path.clone(), + source, + })?; + let api_version = raw_metadata.api_version; + if api_version != REPO_API_VERSION_V1 { + return Err(RepositoryLoadError::UnsupportedApiVersion(api_version)); + } + let metadata = RepositoryMetadata { + id: RepositoryId::new(raw_metadata.id)?, + name: raw_metadata.name, + priority: RepositoryPriority::new(raw_metadata.priority.unwrap_or_default()), + api_version, + }; + + let packages_dir = root.join("packages"); + let lib_dir = root.join("lib"); + let templates_dir = root.join("templates"); + require_dir(&root, &packages_dir, "packages")?; + require_dir(&root, &lib_dir, "lib")?; + require_dir(&root, &templates_dir, "templates")?; + + let mut packages = Vec::new(); + collect_package_files(&packages_dir, &packages_dir, &mut packages)?; + packages.sort_by(|a, b| a.id.to_string().cmp(&b.id.to_string())); + + Ok(Self { + root, + metadata, + packages_dir, + lib_dir, + templates_dir, + packages, + }) + } + + pub fn package_file(&self, id: &PackageId) -> Option<&PackageFile> { + self.packages.iter().find(|package| &package.id == id) + } +} + +#[derive(Debug, Deserialize)] +struct RawRepositoryMetadata { + id: String, + name: String, + #[serde(default)] + priority: Option, + api_version: String, +} + +fn require_dir( + root: &Path, + path: &Path, + directory: &'static str, +) -> Result<(), RepositoryLoadError> { + if path.is_dir() { + Ok(()) + } else { + Err(RepositoryLoadError::MissingDirectory { + path: root.to_path_buf(), + directory, + }) + } +} + +fn collect_package_files( + packages_root: &Path, + current: &Path, + out: &mut Vec, +) -> Result<(), RepositoryLoadError> { + for entry in fs::read_dir(current).map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: current.to_path_buf(), + source, + })? { + let entry = entry.map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: current.to_path_buf(), + source, + })?; + let path = entry.path(); + let file_type = + entry + .file_type() + .map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: current.to_path_buf(), + source, + })?; + if file_type.is_dir() { + collect_package_files(packages_root, &path, out)?; + } else if file_type.is_file() && path.extension().is_some_and(|ext| ext == "lua") { + let id = package_id_from_path(packages_root, &path)?; + out.push(PackageFile { id, path }); + } + } + Ok(()) +} + +pub fn package_id_from_path( + packages_root: impl AsRef, + path: impl AsRef, +) -> Result { + let packages_root = packages_root.as_ref(); + let path = path.as_ref(); + let relative = + path.strip_prefix(packages_root) + .map_err(|_| RepositoryLoadError::InvalidPackagePath { + path: path.to_path_buf(), + reason: format!("path is not under {}", packages_root.display()), + })?; + if relative.extension().is_none_or(|ext| ext != "lua") { + return Err(RepositoryLoadError::InvalidPackagePath { + path: path.to_path_buf(), + reason: "package file must have .lua extension".to_owned(), + }); + } + let without_extension = relative.with_extension(""); + let mut parts = without_extension.components(); + let kind = parts + .next() + .ok_or_else(|| RepositoryLoadError::InvalidPackagePath { + path: path.to_path_buf(), + reason: "missing package kind directory".to_owned(), + })? + .as_os_str() + .to_string_lossy() + .into_owned(); + let name_path: PathBuf = parts.collect(); + let name = name_path + .to_string_lossy() + .replace(std::path::MAIN_SEPARATOR, "/"); + if name.is_empty() { + return Err(RepositoryLoadError::InvalidPackagePath { + path: path.to_path_buf(), + reason: "missing package name".to_owned(), + }); + } + format!("{kind}/{name}") + .parse() + .map_err(|source| RepositoryLoadError::PackageId { + path: path.to_path_buf(), + source, + }) +} + +pub fn highest_priority<'a, T, F>(items: &'a [T], priority: F) -> Option<&'a T> +where + F: Fn(&T) -> RepositoryPriority, +{ + items.iter().max_by_key(|item| priority(item)) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Write; + + #[test] + fn derives_package_id_from_lua_path() { + let root = PathBuf::from("repo/packages"); + let id = + package_id_from_path(&root, "repo/packages/android/org.fdroid.fdroid.lua").unwrap(); + assert_eq!(id.to_string(), "android/org.fdroid.fdroid"); + } + + #[test] + fn loads_repository_layout_with_required_directories() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::write( + root.join("repo.toml"), + r#"id = "official" +name = "UpgradeAll Official" +priority = 0 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::create_dir(root.join("packages")).unwrap(); + fs::create_dir(root.join("packages/android")).unwrap(); + fs::create_dir(root.join("lib")).unwrap(); + fs::create_dir(root.join("templates")).unwrap(); + let mut file = + fs::File::create(root.join("packages/android/org.fdroid.fdroid.lua")).unwrap(); + writeln!(file, "return {{ id = 'android/org.fdroid.fdroid' }}").unwrap(); + + let layout = RepositoryLayout::load(root).unwrap(); + assert_eq!(layout.metadata.id.as_str(), "official"); + assert_eq!(layout.metadata.priority, RepositoryPriority::DEFAULT); + assert_eq!(layout.packages.len(), 1); + assert_eq!( + layout.packages[0].id.to_string(), + "android/org.fdroid.fdroid" + ); + } + + #[test] + fn highest_priority_selects_larger_number() { + let priorities = [ + RepositoryPriority::LOCAL_AUTOGEN, + RepositoryPriority::DEFAULT, + RepositoryPriority::LOCAL, + ]; + let selected = highest_priority(&priorities, |priority| *priority).unwrap(); + assert_eq!(*selected, RepositoryPriority::LOCAL); + } +} diff --git a/crates/getter-downloader/Cargo.toml b/crates/getter-downloader/Cargo.toml new file mode 100644 index 0000000..d8b2562 --- /dev/null +++ b/crates/getter-downloader/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "getter-downloader" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } diff --git a/crates/getter-downloader/src/lib.rs b/crates/getter-downloader/src/lib.rs new file mode 100644 index 0000000..4a11c7d --- /dev/null +++ b/crates/getter-downloader/src/lib.rs @@ -0,0 +1,3 @@ +//! getter-downloader rewrite crate skeleton. + +pub use getter_core as core; diff --git a/crates/getter-ffi/Cargo.toml b/crates/getter-ffi/Cargo.toml new file mode 100644 index 0000000..fa1c2cf --- /dev/null +++ b/crates/getter-ffi/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "getter-ffi" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } diff --git a/crates/getter-ffi/src/lib.rs b/crates/getter-ffi/src/lib.rs new file mode 100644 index 0000000..b7dde13 --- /dev/null +++ b/crates/getter-ffi/src/lib.rs @@ -0,0 +1,3 @@ +//! getter-ffi rewrite crate skeleton. + +pub use getter_core as core; diff --git a/crates/getter-plugin-api/Cargo.toml b/crates/getter-plugin-api/Cargo.toml new file mode 100644 index 0000000..4f41ccf --- /dev/null +++ b/crates/getter-plugin-api/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "getter-plugin-api" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } diff --git a/crates/getter-plugin-api/src/lib.rs b/crates/getter-plugin-api/src/lib.rs new file mode 100644 index 0000000..52cd1d1 --- /dev/null +++ b/crates/getter-plugin-api/src/lib.rs @@ -0,0 +1,3 @@ +//! getter-plugin-api rewrite crate skeleton. + +pub use getter_core as core; diff --git a/crates/getter-providers/Cargo.toml b/crates/getter-providers/Cargo.toml new file mode 100644 index 0000000..5a44be9 --- /dev/null +++ b/crates/getter-providers/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "getter-providers" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs new file mode 100644 index 0000000..35371aa --- /dev/null +++ b/crates/getter-providers/src/lib.rs @@ -0,0 +1,3 @@ +//! getter-providers rewrite crate skeleton. + +pub use getter_core as core; diff --git a/crates/getter-rpc/Cargo.toml b/crates/getter-rpc/Cargo.toml new file mode 100644 index 0000000..6ab6be0 --- /dev/null +++ b/crates/getter-rpc/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "getter-rpc" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } diff --git a/crates/getter-rpc/src/lib.rs b/crates/getter-rpc/src/lib.rs new file mode 100644 index 0000000..ceb75d9 --- /dev/null +++ b/crates/getter-rpc/src/lib.rs @@ -0,0 +1,3 @@ +//! getter-rpc rewrite crate skeleton. + +pub use getter_core as core; diff --git a/crates/getter-storage/Cargo.toml b/crates/getter-storage/Cargo.toml new file mode 100644 index 0000000..6a561a4 --- /dev/null +++ b/crates/getter-storage/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "getter-storage" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } +rusqlite = { version = "0.32", features = ["bundled"] } +thiserror = "1" + +[dev-dependencies] +tempfile = "3" diff --git a/crates/getter-storage/src/legacy_room.rs b/crates/getter-storage/src/legacy_room.rs new file mode 100644 index 0000000..798809e --- /dev/null +++ b/crates/getter-storage/src/legacy_room.rs @@ -0,0 +1,215 @@ +//! Legacy Room migration mapping tests and pure mapping helpers. +//! +//! This module intentionally contains no Android Room/database reader. It is the +//! TDD boundary for source->target mapping rules before the full migration +//! implementation is added. + +use getter_core::PackageId; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LegacyAppKind { + Android, + Magisk, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LegacyAppRecord { + pub kind: LegacyAppKind, + pub installed_id: String, + pub official_package_available: bool, + pub common_conversion_available: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LegacyExtraAppRecord { + pub ignored_version: Option, + pub favorite: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LegacyAppMapping { + pub package_id: PackageId, + pub package_resolution: LegacyPackageResolution, + pub user_state: LegacyUserStateMapping, + pub warnings: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LegacyPackageResolution { + OfficialRepositoryPackage, + GenerateLocalPackage, + MissingPackageDefinition, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct LegacyUserStateMapping { + pub ignored_version: Option, + pub favorite: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum LegacyMigrationWarning { + MissingPackageDefinition { package_id: PackageId }, +} + +#[derive(Debug, thiserror::Error)] +pub enum LegacyRoomMappingError { + #[error("legacy installed id is empty")] + EmptyInstalledId, + #[error("failed to construct target package id: {0}")] + PackageId(#[from] getter_core::PackageIdError), +} + +pub fn map_legacy_app( + app: &LegacyAppRecord, + extra: Option<&LegacyExtraAppRecord>, +) -> Result { + let package_id = map_legacy_package_id(app.kind, &app.installed_id)?; + let package_resolution = if app.official_package_available { + LegacyPackageResolution::OfficialRepositoryPackage + } else if app.common_conversion_available { + LegacyPackageResolution::GenerateLocalPackage + } else { + LegacyPackageResolution::MissingPackageDefinition + }; + let warnings = match package_resolution { + LegacyPackageResolution::MissingPackageDefinition => { + vec![LegacyMigrationWarning::MissingPackageDefinition { + package_id: package_id.clone(), + }] + } + _ => Vec::new(), + }; + let user_state = LegacyUserStateMapping { + ignored_version: extra.and_then(|extra| extra.ignored_version.clone()), + favorite: extra.is_some_and(|extra| extra.favorite), + }; + + Ok(LegacyAppMapping { + package_id, + package_resolution, + user_state, + warnings, + }) +} + +pub fn map_legacy_package_id( + kind: LegacyAppKind, + installed_id: &str, +) -> Result { + if installed_id.is_empty() { + return Err(LegacyRoomMappingError::EmptyInstalledId); + } + let prefix = match kind { + LegacyAppKind::Android => "android", + LegacyAppKind::Magisk => "magisk", + }; + Ok(format!("{prefix}/{installed_id}").parse()?) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn android_legacy_app_maps_to_readable_android_package_id() { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: LegacyAppKind::Android, + installed_id: "org.fdroid.fdroid".to_owned(), + official_package_available: true, + common_conversion_available: false, + }, + None, + ) + .unwrap(); + + assert_eq!(mapping.package_id.to_string(), "android/org.fdroid.fdroid"); + assert_eq!( + mapping.package_resolution, + LegacyPackageResolution::OfficialRepositoryPackage + ); + assert!(mapping.warnings.is_empty()); + } + + #[test] + fn magisk_legacy_app_maps_to_readable_magisk_package_id() { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: LegacyAppKind::Magisk, + installed_id: "zygisk-next".to_owned(), + official_package_available: true, + common_conversion_available: false, + }, + None, + ) + .unwrap(); + + assert_eq!(mapping.package_id.to_string(), "magisk/zygisk-next"); + } + + #[test] + fn common_unofficial_conversion_generates_local_package() { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: LegacyAppKind::Android, + installed_id: "com.example.private".to_owned(), + official_package_available: false, + common_conversion_available: true, + }, + None, + ) + .unwrap(); + + assert_eq!( + mapping.package_resolution, + LegacyPackageResolution::GenerateLocalPackage + ); + assert!(mapping.warnings.is_empty()); + } + + #[test] + fn unmapped_complex_app_preserves_id_and_records_missing_definition_warning() { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: LegacyAppKind::Android, + installed_id: "com.example.unmapped".to_owned(), + official_package_available: false, + common_conversion_available: false, + }, + None, + ) + .unwrap(); + + assert_eq!( + mapping.package_resolution, + LegacyPackageResolution::MissingPackageDefinition + ); + assert_eq!( + mapping.warnings, + vec![LegacyMigrationWarning::MissingPackageDefinition { + package_id: "android/com.example.unmapped".parse().unwrap(), + }] + ); + } + + #[test] + fn extra_app_ignore_and_favorite_state_are_preserved_when_present() { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: LegacyAppKind::Android, + installed_id: "org.fdroid.fdroid".to_owned(), + official_package_available: true, + common_conversion_available: false, + }, + Some(&LegacyExtraAppRecord { + ignored_version: Some("1.2.3".to_owned()), + favorite: true, + }), + ) + .unwrap(); + + assert_eq!(mapping.user_state.ignored_version.as_deref(), Some("1.2.3")); + assert!(mapping.user_state.favorite); + } +} diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs new file mode 100644 index 0000000..6eed591 --- /dev/null +++ b/crates/getter-storage/src/lib.rs @@ -0,0 +1,491 @@ +//! SQLite storage skeleton for getter main/cache databases. + +pub mod legacy_room; + +use getter_core::repository::RepositoryMetadata; +use getter_core::{PackageId, RepositoryId, RepositoryPriority}; +use rusqlite::{params, Connection}; +use std::path::Path; +use std::str::FromStr; + +#[derive(Debug, thiserror::Error)] +pub enum StorageError { + #[error("sqlite error: {0}")] + Sqlite(#[from] rusqlite::Error), + #[error("invalid package id in database: {0}")] + PackageId(#[from] getter_core::PackageIdError), + #[error("invalid repository id in database: {0}")] + RepositoryId(#[from] getter_core::RepositoryIdError), + #[error("invalid package resolution in database: {0}")] + PackageResolution(String), +} + +pub struct MainDb { + conn: Connection, +} + +pub struct CacheDb { + conn: Connection, +} + +impl MainDb { + pub fn open(path: impl AsRef) -> Result { + let conn = Connection::open(path)?; + let db = Self { conn }; + db.migrate()?; + Ok(db) + } + + pub fn open_in_memory() -> Result { + let conn = Connection::open_in_memory()?; + let db = Self { conn }; + db.migrate()?; + Ok(db) + } + + fn migrate(&self) -> Result<(), StorageError> { + self.conn.execute_batch( + r#" +PRAGMA foreign_keys = ON; + +CREATE TABLE IF NOT EXISTS schema_migrations ( + id TEXT PRIMARY KEY, + applied_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + +CREATE TABLE IF NOT EXISTS repositories ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + priority INTEGER NOT NULL, + api_version TEXT NOT NULL, + path TEXT, + revision TEXT +); + +CREATE TABLE IF NOT EXISTS tracked_packages ( + package_id TEXT PRIMARY KEY, + enabled INTEGER NOT NULL DEFAULT 1, + favorite INTEGER NOT NULL DEFAULT 0, + ignored_version TEXT, + repository_id TEXT, + package_resolution TEXT NOT NULL DEFAULT 'missing_package_definition', + FOREIGN KEY(repository_id) REFERENCES repositories(id) +); + +CREATE TABLE IF NOT EXISTS migration_records ( + id TEXT PRIMARY KEY, + source TEXT NOT NULL, + completed_at_unix INTEGER NOT NULL DEFAULT (unixepoch()), + report_json TEXT NOT NULL DEFAULT '{}' +); +"#, + )?; + self.ensure_column("tracked_packages", "ignored_version", "TEXT")?; + self.ensure_column( + "tracked_packages", + "package_resolution", + "TEXT NOT NULL DEFAULT 'missing_package_definition'", + )?; + self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('main-v1')", + [], + )?; + Ok(()) + } + + fn ensure_column( + &self, + table: &'static str, + column: &'static str, + definition: &'static str, + ) -> Result<(), StorageError> { + let columns = self.table_columns(table)?; + if !columns.iter().any(|existing| existing == column) { + self.conn.execute_batch(&format!( + "ALTER TABLE {table} ADD COLUMN {column} {definition};" + ))?; + } + Ok(()) + } + + fn table_columns(&self, table: &'static str) -> Result, StorageError> { + let mut stmt = self.conn.prepare(&format!("PRAGMA table_info({table})"))?; + let rows = stmt.query_map([], |row| row.get::<_, String>(1))?; + let mut columns = Vec::new(); + for row in rows { + columns.push(row?); + } + Ok(columns) + } + + pub fn upsert_repository( + &self, + metadata: &RepositoryMetadata, + path: Option<&Path>, + revision: Option<&str>, + ) -> Result<(), StorageError> { + self.conn.execute( + r#" +INSERT INTO repositories(id, name, priority, api_version, path, revision) +VALUES (?1, ?2, ?3, ?4, ?5, ?6) +ON CONFLICT(id) DO UPDATE SET + name = excluded.name, + priority = excluded.priority, + api_version = excluded.api_version, + path = excluded.path, + revision = excluded.revision +"#, + params![ + metadata.id.as_str(), + metadata.name, + metadata.priority.value(), + metadata.api_version, + path.map(|p| p.to_string_lossy().to_string()), + revision, + ], + )?; + Ok(()) + } + + pub fn repositories(&self) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + "SELECT id, name, priority, api_version, path, revision FROM repositories ORDER BY priority DESC, id ASC", + )?; + let rows = stmt.query_map([], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, i32>(2)?, + row.get::<_, String>(3)?, + row.get::<_, Option>(4)?, + row.get::<_, Option>(5)?, + )) + })?; + let mut repositories = Vec::new(); + for row in rows { + let (id, name, priority, api_version, path, revision) = row?; + repositories.push(StoredRepository { + id: RepositoryId::new(id)?, + name, + priority: RepositoryPriority::new(priority), + api_version, + path, + revision, + }); + } + Ok(repositories) + } + + pub fn upsert_tracked_package( + &self, + package: &TrackedPackageUpsert, + ) -> Result<(), StorageError> { + self.conn.execute( + r#" +INSERT INTO tracked_packages( + package_id, + enabled, + favorite, + ignored_version, + repository_id, + package_resolution +) +VALUES (?1, ?2, ?3, ?4, ?5, ?6) +ON CONFLICT(package_id) DO UPDATE SET + enabled = excluded.enabled, + favorite = excluded.favorite, + ignored_version = excluded.ignored_version, + repository_id = excluded.repository_id, + package_resolution = excluded.package_resolution +"#, + params![ + package.package_id.to_string(), + bool_to_i64(package.enabled), + bool_to_i64(package.favorite), + package.ignored_version.as_deref(), + package.repository_id.as_ref().map(RepositoryId::as_str), + package.package_resolution.as_str(), + ], + )?; + Ok(()) + } + + pub fn tracked_packages(&self) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + r#" +SELECT package_id, enabled, favorite, ignored_version, repository_id, package_resolution +FROM tracked_packages +ORDER BY package_id ASC +"#, + )?; + let rows = stmt.query_map([], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, i64>(1)?, + row.get::<_, i64>(2)?, + row.get::<_, Option>(3)?, + row.get::<_, Option>(4)?, + row.get::<_, String>(5)?, + )) + })?; + let mut packages = Vec::new(); + for row in rows { + let (package_id, enabled, favorite, ignored_version, repository_id, resolution) = row?; + packages.push(StoredTrackedPackage { + package_id: PackageId::from_str(&package_id)?, + enabled: enabled != 0, + favorite: favorite != 0, + ignored_version, + repository_id: repository_id.map(RepositoryId::new).transpose()?, + package_resolution: StoredPackageResolution::from_str(&resolution)?, + }); + } + Ok(packages) + } + + pub fn insert_migration_record( + &self, + id: &str, + source: &str, + report_json: &str, + ) -> Result<(), StorageError> { + self.conn.execute( + r#" +INSERT INTO migration_records(id, source, report_json) +VALUES (?1, ?2, ?3) +ON CONFLICT(id) DO UPDATE SET + source = excluded.source, + completed_at_unix = unixepoch(), + report_json = excluded.report_json +"#, + params![id, source, report_json], + )?; + Ok(()) + } + + pub fn migration_records(&self) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + "SELECT id, source, report_json FROM migration_records ORDER BY completed_at_unix ASC, id ASC", + )?; + let rows = stmt.query_map([], |row| { + Ok(StoredMigrationRecord { + id: row.get(0)?, + source: row.get(1)?, + report_json: row.get(2)?, + }) + })?; + let mut records = Vec::new(); + for row in rows { + records.push(row?); + } + Ok(records) + } +} + +impl CacheDb { + pub fn open(path: impl AsRef) -> Result { + let conn = Connection::open(path)?; + let db = Self { conn }; + db.migrate()?; + Ok(db) + } + + pub fn open_in_memory() -> Result { + let conn = Connection::open_in_memory()?; + let db = Self { conn }; + db.migrate()?; + Ok(db) + } + + fn migrate(&self) -> Result<(), StorageError> { + self.conn.execute_batch( + r#" +PRAGMA foreign_keys = ON; + +CREATE TABLE IF NOT EXISTS schema_migrations ( + id TEXT PRIMARY KEY, + applied_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + +CREATE TABLE IF NOT EXISTS evaluated_packages ( + cache_key TEXT PRIMARY KEY, + repository_id TEXT NOT NULL, + package_id TEXT NOT NULL, + package_file_hash TEXT NOT NULL, + schema_version TEXT NOT NULL, + evaluated_json TEXT NOT NULL, + evaluated_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + +CREATE TABLE IF NOT EXISTS provider_responses ( + cache_key TEXT PRIMARY KEY, + provider TEXT NOT NULL, + response_json TEXT NOT NULL, + fetched_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); +"#, + )?; + self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('cache-v1')", + [], + )?; + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StoredRepository { + pub id: RepositoryId, + pub name: String, + pub priority: RepositoryPriority, + pub api_version: String, + pub path: Option, + pub revision: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TrackedPackageUpsert { + pub package_id: PackageId, + pub enabled: bool, + pub favorite: bool, + pub ignored_version: Option, + pub repository_id: Option, + pub package_resolution: StoredPackageResolution, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StoredTrackedPackage { + pub package_id: PackageId, + pub enabled: bool, + pub favorite: bool, + pub ignored_version: Option, + pub repository_id: Option, + pub package_resolution: StoredPackageResolution, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StoredMigrationRecord { + pub id: String, + pub source: String, + pub report_json: String, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum StoredPackageResolution { + OfficialRepositoryPackage, + GenerateLocalPackage, + MissingPackageDefinition, +} + +impl StoredPackageResolution { + pub const fn as_str(self) -> &'static str { + match self { + Self::OfficialRepositoryPackage => "official_repository_package", + Self::GenerateLocalPackage => "generate_local_package", + Self::MissingPackageDefinition => "missing_package_definition", + } + } +} + +impl FromStr for StoredPackageResolution { + type Err = StorageError; + + fn from_str(value: &str) -> Result { + match value { + "official_repository_package" => Ok(Self::OfficialRepositoryPackage), + "generate_local_package" => Ok(Self::GenerateLocalPackage), + "missing_package_definition" => Ok(Self::MissingPackageDefinition), + other => Err(StorageError::PackageResolution(other.to_owned())), + } + } +} + +fn bool_to_i64(value: bool) -> i64 { + if value { + 1 + } else { + 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::repository::REPO_API_VERSION_V1; + + #[test] + fn main_db_stores_repository_registry_ordered_by_priority() { + let db = MainDb::open_in_memory().unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::DEFAULT, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + None, + Some("rev1"), + ) + .unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("local").unwrap(), + name: "Local".to_owned(), + priority: RepositoryPriority::LOCAL, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + None, + None, + ) + .unwrap(); + + let repositories = db.repositories().unwrap(); + assert_eq!(repositories[0].id.as_str(), "local"); + assert_eq!(repositories[1].id.as_str(), "official"); + } + + #[test] + fn main_db_stores_tracked_package_user_state() { + let db = MainDb::open_in_memory().unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + enabled: true, + favorite: true, + ignored_version: Some("1.2.3".to_owned()), + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + let packages = db.tracked_packages().unwrap(); + assert_eq!(packages.len(), 1); + assert_eq!( + packages[0].package_id.to_string(), + "android/org.fdroid.fdroid" + ); + assert!(packages[0].enabled); + assert!(packages[0].favorite); + assert_eq!(packages[0].ignored_version.as_deref(), Some("1.2.3")); + assert_eq!( + packages[0].package_resolution, + StoredPackageResolution::OfficialRepositoryPackage + ); + } + + #[test] + fn main_db_records_migration_completion() { + let db = MainDb::open_in_memory().unwrap(); + db.insert_migration_record("legacy-room-v17", "legacy-room-bundle", r#"{"ok":true}"#) + .unwrap(); + + let records = db.migration_records().unwrap(); + assert_eq!(records.len(), 1); + assert_eq!(records[0].id, "legacy-room-v17"); + assert_eq!(records[0].source, "legacy-room-bundle"); + } + + #[test] + fn cache_db_migrates_schema() { + let _db = CacheDb::open_in_memory().unwrap(); + } +} diff --git a/src/cache.rs b/src/cache.rs deleted file mode 100644 index e96302b..0000000 --- a/src/cache.rs +++ /dev/null @@ -1,34 +0,0 @@ -mod local; -pub mod manager; - -use once_cell::sync::Lazy; -use std::{path::Path, sync::Arc}; -use tokio::sync::Mutex; - -use crate::utils::instance::InstanceContainer; - -use self::manager::CacheManager; - -static INSTANCE_CONTAINER: Lazy> = - Lazy::new(|| InstanceContainer::new(CacheManager::new())); - -pub async fn init_cache_manager(local_cache_dir: &Path) { - get_cache_manager() - .await - .lock() - .await - .set_local_cache_dir(local_cache_dir); -} - -pub async fn init_cache_manager_with_expire(local_cache_path: &Path, expire_time: u64) { - get_cache_manager() - .await - .lock() - .await - .set_local_cache_dir(local_cache_path) - .set_global_expire_time(expire_time); -} - -pub async fn get_cache_manager() -> Arc> { - INSTANCE_CONTAINER.get().await.clone() -} diff --git a/src/cache/local.rs b/src/cache/local.rs deleted file mode 100644 index 22cdcb4..0000000 --- a/src/cache/local.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::io::Result; -use std::path::{Path, PathBuf}; -use std::time::SystemTime; -use tokio::fs::{metadata, read, remove_file, write}; - -use tokio::fs::create_dir_all; - -pub struct LocalCacheItem { - cache_path: PathBuf, -} - -impl LocalCacheItem { - pub fn new(cache_dir: &Path, key: &str) -> Self { - Self { - cache_path: cache_dir.join(key), - } - } - - pub async fn get(&self, decoder: fn(Vec) -> T) -> Result { - read(&self.cache_path).await.map(decoder) - } - - pub async fn save(&self, data: T, encoder: fn(T) -> Vec) -> Result<()> { - let parent = self.cache_path.parent().ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::NotFound, "cache path not found") - })?; - create_dir_all(parent).await?; - write(&self.cache_path, encoder(data)).await - } - - pub async fn remove(&self) -> Result<()> { - remove_file(&self.cache_path).await - } - - pub async fn get_cache_time(&self) -> Result { - let metadata = metadata(&self.cache_path).await?; - let modified_time = metadata.modified()?; - Ok(modified_time - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap_or_default() - .as_secs()) - } -} - -#[cfg(test)] -mod tests { - use std::fs; - - use super::*; - - #[tokio::test] - async fn test_local_cache_item() { - let key = "test_key"; - let data = "test_data"; - let cache_path = Path::new("./test_cache"); - let cache_item = LocalCacheItem::new(cache_path, key); - cache_item - .save(data, |data| data.as_bytes().to_vec()) - .await - .unwrap(); - let result = cache_item - .get(|data| String::from_utf8(data).unwrap()) - .await - .unwrap(); - assert_eq!(result, data); - let cache_time = cache_item.get_cache_time().await.unwrap(); - assert!( - cache_time - >= SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap_or_default() - .as_secs() - ); - cache_item.remove().await.unwrap(); - fs::remove_dir_all(cache_path).unwrap(); - } -} diff --git a/src/cache/manager.rs b/src/cache/manager.rs deleted file mode 100644 index fbb8bb3..0000000 --- a/src/cache/manager.rs +++ /dev/null @@ -1,265 +0,0 @@ -use std::path::{Path, PathBuf}; - -use bytes::Bytes; - -use super::local::LocalCacheItem; -use crate::utils::time::get_now_unix; - -#[derive(Debug, Eq, Hash, PartialEq)] -pub enum GroupType { - RepoInside, - Api, -} - -pub struct CacheManager { - local_cache_dir: Option, - global_expire_time: Option, -} - -impl CacheManager { - pub fn new() -> Self { - Self { - local_cache_dir: None, - global_expire_time: None, - } - } - - pub fn set_local_cache_dir(&mut self, local_cache_dir: &Path) -> &mut Self { - self.local_cache_dir = Some(local_cache_dir.to_path_buf()); - self - } - - pub fn set_global_expire_time(&mut self, global_expire_time: u64) -> &mut Self { - self.global_expire_time = Some(global_expire_time); - self - } - - fn get_local_cache_key(group: &GroupType, key: &str) -> String { - format!("{:?}_{}", group, key) - } - - async fn get_local( - &self, - group: &GroupType, - key: &str, - expire_time: Option, - ) -> Option { - let local_cache_item = self.get_local_cache_item(group, key).ok()?; - if let Ok(time) = local_cache_item.get_cache_time().await { - if let Some(expire_time) = expire_time.or(self.global_expire_time) { - if time + expire_time < get_now_unix() { - return None; - } - } - if let Ok(data) = local_cache_item.get(|data| data).await { - return Some(Bytes::from(data)); - } - } - None - } - - pub async fn get( - &self, - group: &GroupType, - key: &str, - expire_time: Option, - ) -> Option { - self.get_local(group, key, expire_time).await - } - - pub async fn save( - &mut self, - group: &GroupType, - key: &str, - value: Bytes, - ) -> Result<(), std::io::Error> { - let local_cache_item = self.get_local_cache_item(group, key)?; - local_cache_item.save(value, |data| data.into()).await - } - - #[allow(dead_code)] - pub async fn remove(&mut self, group: &GroupType, key: &str) -> Result<(), std::io::Error> { - let local_cache_item = self.get_local_cache_item(group, key)?; - local_cache_item.remove().await - } - - #[allow(dead_code)] - pub async fn clean(&mut self) -> Result<(), std::io::Error> { - self.clean_local().await - } - - #[allow(dead_code)] - async fn clean_local(&mut self) -> Result<(), std::io::Error> { - let local_cache_dir = self.local_cache_dir.as_ref().ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::NotFound, "local cache dir not found") - })?; - tokio::fs::remove_dir_all(local_cache_dir).await - } - - fn get_local_cache_item( - &self, - group: &GroupType, - key: &str, - ) -> Result { - let local_cache_key = Self::get_local_cache_key(group, key); - let local_cache_dir = self.local_cache_dir.as_ref().ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::NotFound, "local cache dir not found") - })?; - Ok(LocalCacheItem::new(local_cache_dir, &local_cache_key)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_cache_manager() { - let mut cache_manager = CacheManager::new(); - cache_manager.set_local_cache_dir(Path::new("./test_cache_manager")); - let group = GroupType::RepoInside; - let key = "test_key"; - let value = Bytes::from("test_value"); - let _ = cache_manager.remove(&group, key).await; - cache_manager - .save(&group, key, value.clone()) - .await - .expect("save failed"); - let data = cache_manager - .get(&group, key, None) - .await - .expect("get failed"); - assert_eq!(data, value); - cache_manager - .remove(&group, key) - .await - .expect("remove failed"); - cache_manager.clean().await.expect("clean failed"); - } - - #[tokio::test] - async fn test_cache_manager_restart() { - let mut cache_manager = CacheManager::new(); - cache_manager.set_local_cache_dir(Path::new("./test_cache_manager_restart")); - let group = GroupType::RepoInside; - let key = "test_key"; - let value = Bytes::from("test_value"); - let _ = cache_manager.remove(&group, key).await; - cache_manager - .save(&group, key, value.clone()) - .await - .expect("save failed"); - let mut _cache_manager = CacheManager::new(); - _cache_manager.set_local_cache_dir(Path::new("./test_cache_manager_restart")); - let data = _cache_manager - .get(&group, key, None) - .await - .expect("get failed"); - assert_eq!(data, value); - cache_manager - .remove(&group, key) - .await - .expect("remove failed"); - cache_manager.clean().await.expect("clean failed"); - } - - #[tokio::test] - async fn test_cache_manager_no_exist() { - let mut cache_manager = CacheManager::new(); - cache_manager.set_local_cache_dir(Path::new("./test_cache_manager_no_exist")); - let group = GroupType::RepoInside; - let key = "test_key_no_exist"; - let data = cache_manager.get(&group, key, None).await; - assert_eq!(data, None); - let clean_result = cache_manager.clean().await; - assert!(clean_result.is_err()); - } - - #[tokio::test] - async fn test_cache_manager_expire_non_global() { - let mut cache_manager = CacheManager::new(); - cache_manager.set_local_cache_dir(Path::new("./test_cache_manager_expire_non_global")); - let group = GroupType::RepoInside; - let key = "test_key_expire"; - let value = Bytes::from("test_value_expire"); - let _ = cache_manager.remove(&group, key).await; - cache_manager - .save(&group, key, value.clone()) - .await - .expect("save failed"); - // sleep 1 second - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - let data = cache_manager.get(&group, key, Some(0)).await; - assert_eq!(data, None); - let data = cache_manager - .get(&group, key, Some(100)) - .await - .expect("get failed"); - assert_eq!(data, value); - cache_manager - .remove(&group, key) - .await - .expect("remove failed"); - cache_manager.clean().await.expect("clean failed"); - } - - #[tokio::test] - async fn test_cache_manager_expire_non_expire() { - let mut cache_manager = CacheManager::new(); - cache_manager.set_local_cache_dir(Path::new("./test_cache_manager_non_expire")); - let group = GroupType::RepoInside; - let key = "test_key_expire"; - let value = Bytes::from("test_value_expire"); - let _ = cache_manager.remove(&group, key).await; - cache_manager - .save(&group, key, value.clone()) - .await - .expect("save failed"); - // sleep 1 second - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - let data = cache_manager.get(&group, key, Some(0)).await; - assert_eq!(data, None); - let data = cache_manager - .get(&group, key, None) - .await - .expect("get failed"); - assert_eq!(data, value); - cache_manager - .remove(&group, key) - .await - .expect("remove failed"); - cache_manager.clean().await.expect("clean failed"); - } - - #[tokio::test] - async fn test_cache_manager_global_expire() { - let mut cache_manager = CacheManager::new(); - cache_manager - .set_local_cache_dir(Path::new("./test_cache_manager_global_expire")) - .set_global_expire_time(1); - let group = GroupType::RepoInside; - let key = "test_key_expire"; - let value = Bytes::from("test_value_expire"); - let _ = cache_manager.remove(&group, key).await; - cache_manager - .save(&group, key, value.clone()) - .await - .expect("save failed"); - // sleep 1 second - tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; - let data = cache_manager.get(&group, key, Some(0)).await; - assert_eq!(data, None); - let data = cache_manager.get(&group, key, None).await; - assert_eq!(data, None); - let data = cache_manager - .get(&group, key, Some(100)) - .await - .expect("get failed"); - assert_eq!(data, value); - cache_manager - .remove(&group, key) - .await - .expect("remove failed"); - cache_manager.clean().await.expect("clean failed"); - } -} diff --git a/src/cache/moka.rs b/src/cache/moka.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/core.rs b/src/core.rs deleted file mode 100644 index ef68c36..0000000 --- a/src/core.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod config; diff --git a/src/core/config.rs b/src/core/config.rs deleted file mode 100644 index 33d48eb..0000000 --- a/src/core/config.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod data; -mod utils; -pub mod world; diff --git a/src/core/config/data.rs b/src/core/config/data.rs deleted file mode 100644 index 80675b9..0000000 --- a/src/core/config/data.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod rule_list; diff --git a/src/core/config/data/rule_list.rs b/src/core/config/data/rule_list.rs deleted file mode 100644 index b0dd2d9..0000000 --- a/src/core/config/data/rule_list.rs +++ /dev/null @@ -1,89 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// Configuration lists -/// -/// JSON Schema: -/// ```json -/// { -/// "app_list": ["", ], -/// "hub_list": ["", ] -/// } -/// ``` - -#[derive(Serialize, Deserialize, Debug)] -pub struct RuleList { - #[serde(rename = "app_list")] - pub app_list: Vec, - - #[serde(rename = "hub_list")] - pub hub_list: Vec, -} - -impl RuleList { - pub fn new() -> Self { - RuleList { - app_list: Vec::new(), - hub_list: Vec::new(), - } - } - - pub fn push_app(&mut self, app_name: &str) -> bool { - if self.app_list.contains(&app_name.to_string()) { - false - } else { - self.app_list.push(app_name.to_string()); - true - } - } - - pub fn remove_app(&mut self, app_name: &str) -> bool { - if let Some(index) = self.app_list.iter().position(|x| x == app_name) { - self.app_list.remove(index); - true - } else { - false - } - } - - pub fn push_hub(&mut self, hub_name: &str) -> bool { - if self.hub_list.contains(&hub_name.to_string()) { - false - } else { - self.hub_list.push(hub_name.to_string()); - true - } - } - - pub fn remove_hub(&mut self, hub_name: &str) -> bool { - if let Some(index) = self.hub_list.iter().position(|x| x == hub_name) { - self.hub_list.remove(index); - true - } else { - false - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json; - - #[test] - fn test_config_list() { - let json = r#" -{ - "app_list": ["UpgradeAll", ""], - "hub_list": ["GitHub"] -}"#; - - let config_list: RuleList = serde_json::from_str(json).unwrap(); - - // check app_config_list - assert_eq!(config_list.app_list.len(), 2); - assert_eq!(config_list.app_list[0], "UpgradeAll"); - // check hub_config_list - assert_eq!(config_list.hub_list.len(), 1); - assert_eq!(config_list.hub_list[0], "GitHub"); - } -} diff --git a/src/core/config/utils.rs b/src/core/config/utils.rs deleted file mode 100644 index 15a32dd..0000000 --- a/src/core/config/utils.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::{env, path::PathBuf}; - -use crate::locale::all_dir; - -pub fn get_data_path(sub: &str) -> String { - let data_dir = env::var("DATA_DIR").map(PathBuf::from).unwrap_or_else(|_| { - all_dir() - .expect("Non-support OS, you should set DATA_DIR env arg") - .data_dir - }); - data_dir - .join(sub) - .to_str() - .expect("Invalid config path") - .to_string() -} - -#[cfg(test)] -mod tests { - use super::*; - use std::env; - use std::fs; - - #[test] - fn test_get_data_path_env() { - let path = "/tmp/getter_test"; - env::set_var("DATA_DIR", path); - let path = PathBuf::from(get_data_path("test")); - let _ = fs::remove_file(&path); - let path = path.to_str().unwrap(); - assert_eq!(path, "/tmp/getter_test/test"); - } - - #[test] - fn test_get_data_path_default() { - let path = PathBuf::from(get_data_path("test")); - let _ = fs::remove_file(&path); - let path = path.to_str().unwrap(); - assert!(path.ends_with("test")) - } -} diff --git a/src/core/config/world.rs b/src/core/config/world.rs deleted file mode 100644 index 9ab73ac..0000000 --- a/src/core/config/world.rs +++ /dev/null @@ -1,35 +0,0 @@ -pub mod local_repo; -pub mod world_config_wrapper; -pub mod world_list; - -use once_cell::sync::Lazy; -use std::{path::Path, sync::Arc}; -use tokio::sync::Mutex; - -use crate::{error::Result, utils::instance::InstanceContainer}; - -use self::world_list::WorldList; - -static INSTANCE_CONTAINER: Lazy> = - Lazy::new(|| InstanceContainer::new(WorldList::new())); - -pub async fn init_world_list(world_list_path: &Path) -> Result<()> { - get_world_list().await.lock().await.load(world_list_path)?; - Ok(()) -} - -pub async fn get_world_list() -> Arc> { - INSTANCE_CONTAINER.get().await.clone() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_get_world_list_micro() { - let world_list_path = Path::new("./test_get_world_list_micro"); - init_world_list(world_list_path).await.unwrap(); - let _ = get_world_list().await; - } -} diff --git a/src/core/config/world/local_repo.rs b/src/core/config/world/local_repo.rs deleted file mode 100644 index b277da2..0000000 --- a/src/core/config/world/local_repo.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::error::{GetterError, Result}; -use std::fs; -use std::path::PathBuf; - -const LOCAL_REPO_DIR: &str = "local_repo"; - -pub struct LocalRepo { - path: PathBuf, -} - -impl LocalRepo { - pub fn new(root_dir_path: &str) -> Self { - let path = PathBuf::from(root_dir_path).join(LOCAL_REPO_DIR); - Self { path } - } - - pub fn load(&self, rule_path: &str) -> Result { - let path = PathBuf::from(&self.path).join(rule_path); - let content = fs::read_to_string(&path) - .map_err(|e| GetterError::new("LocalRepo", "load", Box::new(e)))?; - Ok(content) - } - - pub fn save(&self, rule_path: &str, content: &str) -> Result<()> { - let path = PathBuf::from(&self.path).join(rule_path); - let _ = fs::create_dir_all(path.parent().unwrap()); - fs::write(&path, content) - .map_err(|e| GetterError::new("LocalRepo", "save", Box::new(e)))?; - Ok(()) - } -} diff --git a/src/core/config/world/world_config_wrapper.rs b/src/core/config/world/world_config_wrapper.rs deleted file mode 100644 index a4422f3..0000000 --- a/src/core/config/world/world_config_wrapper.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::path::Path; - -use crate::error::{GetterError, Result}; -use crate::utils::json::{json_to_string, string_to_json}; -use crate::websdk::cloud_rules::cloud_rules_wrapper::CloudRules; -use crate::websdk::cloud_rules::data::app_item::AppItem; -use crate::websdk::cloud_rules::data::hub_item::HubItem; - -use super::local_repo::LocalRepo; -use super::world_list::WorldList; - -pub struct WorldConfigWrapper { - pub world_list: WorldList, - pub local_repo: LocalRepo, -} - -impl WorldConfigWrapper { - pub fn new(world_config_path: &Path, local_repo_path: &str) -> Result { - let mut world_list = WorldList::new(); - world_list.load(world_config_path)?; - let local_repo = LocalRepo::new(local_repo_path); - Ok(WorldConfigWrapper { - world_list, - local_repo, - }) - } - - pub fn get_app_rule(&self, app_name: &str) -> Option { - if let Ok(content) = self.local_repo.load(app_name) { - if let Ok(app_item) = string_to_json(&content) { - return Some(app_item); - } - } - None - } - - pub fn get_hub_rule(&self, hub_name: &str) -> Option { - if let Ok(content) = self.local_repo.load(hub_name) { - if let Ok(hub_item) = string_to_json(&content) { - return Some(hub_item); - } - } - None - } - - pub fn download_app_rule(&self, app_name: &str, cloud_rules: &mut CloudRules) -> Result<()> { - let app_item = cloud_rules.get_cloud_app_rules(|x| x.info.name == app_name); - let content = json_to_string(&app_item).map_err(|e| { - GetterError::new("WorldConfigWrapper", "download_app_rule", Box::new(e)) - })?; - self.local_repo.save(app_name, &content)?; - Ok(()) - } - - pub fn download_hub_rule(&self, hub_name: &str, cloud_rules: &mut CloudRules) -> Result<()> { - let hub_item = cloud_rules.get_cloud_hub_rules(|x| x.info.hub_name == hub_name); - let content = json_to_string(&hub_item).map_err(|e| { - GetterError::new("WorldConfigWrapper", "download_hub_rule", Box::new(e)) - })?; - self.local_repo.save(hub_name, &content)?; - Ok(()) - } - - pub fn get_app_rule_list(&self) -> &Vec { - &self.world_list.rule_list.app_list - } - - pub fn get_hub_rule_list(&self) -> &Vec { - &self.world_list.rule_list.hub_list - } -} diff --git a/src/core/config/world/world_list.rs b/src/core/config/world/world_list.rs deleted file mode 100644 index 6dd89a0..0000000 --- a/src/core/config/world/world_list.rs +++ /dev/null @@ -1,100 +0,0 @@ -use std::fs::{create_dir_all, File}; -use std::io::BufReader; -use std::path::{Path, PathBuf}; - -use crate::error::{GetterError, Result}; - -use super::super::data::rule_list::RuleList; - -pub const WORLD_CONFIG_LIST_NAME: &str = "world_config_list.json"; - -pub struct WorldList { - config_path: Option, - pub rule_list: RuleList, -} - -impl WorldList { - pub fn new() -> Self { - Self { - config_path: None, - rule_list: RuleList::new(), - } - } - - pub fn load(&mut self, config_path: &Path) -> Result<&mut Self> { - let rule_list = if let Ok(file) = File::open(config_path) { - let reader = BufReader::new(file); - serde_json::from_reader(reader) - .map_err(|e| GetterError::new("WorldList", "load", Box::new(e)))? - } else { - RuleList::new() - }; - self.config_path = Some(config_path.to_path_buf()); - self.rule_list = rule_list; - Ok(self) - } - - pub fn save(&self) -> Result<()> { - let path = self - .config_path - .as_deref() - .ok_or_else(|| GetterError::new_nobase("WorldList", "save: path not set"))?; - let parent = path - .parent() - .ok_or_else(|| GetterError::new_nobase("WorldList", "save: get parent dir failed"))?; - let _ = create_dir_all(parent); - let file = - File::create(path).map_err(|e| GetterError::new("WorldList", "save", Box::new(e)))?; - serde_json::to_writer(file, &self.rule_list) - .map_err(|e| GetterError::new("WorldList", "save", Box::new(e)))?; - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fs; - - #[test] - fn test_world_list() { - let path_base = "/tmp/getter_test_world_list"; - let _ = fs::remove_dir_all(path_base); - let config_path = PathBuf::from(path_base).join(WORLD_CONFIG_LIST_NAME); - - let mut world_list = WorldList::new(); - world_list.load(&config_path).unwrap(); - assert!(world_list.rule_list.app_list.is_empty()); - assert!(world_list.rule_list.hub_list.is_empty()); - let value = world_list.save(); - assert!(value.is_ok()); - let content = fs::read_to_string(&config_path).expect("test_world_list: read file failed"); - assert!(!content.is_empty()); - - let mut world_list = WorldList::new(); - world_list.load(&config_path).unwrap(); - world_list.rule_list.app_list.push("UpgradeAll".to_string()); - world_list.rule_list.hub_list.push("GitHub".to_string()); - let value = world_list.save(); - assert!(value.is_ok()); - - let mut world_list = WorldList::new(); - world_list.load(&config_path).unwrap(); - assert_eq!(world_list.rule_list.app_list.len(), 1); - assert_eq!(world_list.rule_list.app_list[0], "UpgradeAll"); - assert_eq!(world_list.rule_list.hub_list.len(), 1); - assert_eq!(world_list.rule_list.hub_list[0], "GitHub"); - - fs::remove_dir_all(path_base).expect("test_world_list: clean failed"); - } - - #[test] - fn test_world_list_only_load() { - let path_base = "/tmp/getter_test_world_list_only_load"; - let _ = fs::remove_dir(path_base); - let path = PathBuf::from(path_base).join(WORLD_CONFIG_LIST_NAME); - - let _ = WorldList::new().load(&path); - assert!(path.try_exists().is_ok_and(|x| !x)); - } -} diff --git a/src/database/mod.rs b/src/database/mod.rs deleted file mode 100644 index dcb6598..0000000 --- a/src/database/mod.rs +++ /dev/null @@ -1,229 +0,0 @@ -pub mod models; -pub mod store; - -use models::{ - app::AppRecord, extra_app::ExtraAppRecord, extra_hub::ExtraHubRecord, hub::HubRecord, -}; -use once_cell::sync::OnceCell; -use std::path::Path; - -use crate::error::Result; -use store::{HasId, JsonlStore}; - -impl HasId for AppRecord { - fn id(&self) -> &str { - &self.id - } -} - -impl HasId for HubRecord { - fn id(&self) -> &str { - &self.uuid - } -} - -impl HasId for ExtraAppRecord { - fn id(&self) -> &str { - &self.id - } -} - -impl HasId for ExtraHubRecord { - fn id(&self) -> &str { - &self.id - } -} - -pub struct Database { - pub apps: JsonlStore, - pub hubs: JsonlStore, - pub extra_apps: JsonlStore, - pub extra_hubs: JsonlStore, -} - -impl Database { - pub fn open(data_dir: &Path) -> Result { - let db = Self { - apps: JsonlStore::new(data_dir.join("apps.jsonl")), - hubs: JsonlStore::new(data_dir.join("hubs.jsonl")), - extra_apps: JsonlStore::new(data_dir.join("extra_apps.jsonl")), - extra_hubs: JsonlStore::new(data_dir.join("extra_hubs.jsonl")), - }; - db.apps.ensure_file()?; - db.hubs.ensure_file()?; - db.extra_apps.ensure_file()?; - db.extra_hubs.ensure_file()?; - Ok(db) - } - - // --- App CRUD --- - - pub fn load_apps(&self) -> Result> { - self.apps.load_all() - } - - pub fn upsert_app(&self, record: &AppRecord) -> Result<()> { - self.apps.upsert(record) - } - - pub fn delete_app(&self, id: &str) -> Result { - self.apps.delete::(id) - } - - pub fn find_app(&self, id: &str) -> Result> { - self.apps.find_by_id(id) - } - - // --- Hub CRUD --- - - pub fn load_hubs(&self) -> Result> { - self.hubs.load_all() - } - - pub fn upsert_hub(&self, record: &HubRecord) -> Result<()> { - self.hubs.upsert(record) - } - - pub fn delete_hub(&self, uuid: &str) -> Result { - self.hubs.delete::(uuid) - } - - pub fn find_hub(&self, uuid: &str) -> Result> { - self.hubs.find_by_id(uuid) - } - - // --- ExtraApp CRUD --- - - pub fn load_extra_apps(&self) -> Result> { - self.extra_apps.load_all() - } - - pub fn upsert_extra_app(&self, record: &ExtraAppRecord) -> Result<()> { - self.extra_apps.upsert(record) - } - - pub fn delete_extra_app(&self, id: &str) -> Result { - self.extra_apps.delete::(id) - } - - /// Find an ExtraApp record by matching its `app_id` map. - pub fn get_extra_app_by_app_id( - &self, - app_id: &std::collections::HashMap>, - ) -> Result> { - let all = self.extra_apps.load_all::()?; - Ok(all.into_iter().find(|r| &r.app_id == app_id)) - } - - // --- ExtraHub CRUD --- - - pub fn load_extra_hubs(&self) -> Result> { - self.extra_hubs.load_all() - } - - pub fn upsert_extra_hub(&self, record: &ExtraHubRecord) -> Result<()> { - self.extra_hubs.upsert(record) - } - - pub fn delete_extra_hub(&self, id: &str) -> Result { - self.extra_hubs.delete::(id) - } - - pub fn find_extra_hub(&self, id: &str) -> Result> { - self.extra_hubs.find_by_id(id) - } -} - -static DB: OnceCell = OnceCell::new(); - -/// Initialize the global database. Must be called once before `get_db()`. -/// Idempotent: if already initialized, returns `Ok(())`. -pub fn init_db(data_dir: &Path) -> Result<()> { - if DB.get().is_some() { - return Ok(()); - } - let db = Database::open(data_dir)?; - let _ = DB.set(db); // Ignore error if another thread beat us to it - Ok(()) -} - -/// Get the global database instance. Panics if `init_db` was not called. -pub fn get_db() -> &'static Database { - DB.get() - .expect("Database not initialized. Call init_db() first.") -} - -#[cfg(test)] -mod tests { - use super::*; - use tempfile::TempDir; - - fn open_test_db() -> (Database, TempDir) { - let dir = tempfile::tempdir().unwrap(); - let db = Database::open(dir.path()).unwrap(); - (db, dir) - } - - #[test] - fn test_open_creates_files() { - let dir = tempfile::tempdir().unwrap(); - Database::open(dir.path()).unwrap(); - assert!(dir.path().join("apps.jsonl").exists()); - assert!(dir.path().join("hubs.jsonl").exists()); - assert!(dir.path().join("extra_apps.jsonl").exists()); - assert!(dir.path().join("extra_hubs.jsonl").exists()); - } - - #[test] - fn test_app_crud() { - let (db, _dir) = open_test_db(); - let app = AppRecord::new( - "TestApp".to_string(), - std::collections::HashMap::from([("owner".to_string(), Some("alice".to_string()))]), - ); - db.upsert_app(&app).unwrap(); - - let apps = db.load_apps().unwrap(); - assert_eq!(apps.len(), 1); - assert_eq!(apps[0].name, "TestApp"); - - let found = db.find_app(&app.id).unwrap(); - assert!(found.is_some()); - - let deleted = db.delete_app(&app.id).unwrap(); - assert!(deleted); - assert!(db.load_apps().unwrap().is_empty()); - } - - #[test] - fn test_hub_crud() { - use crate::websdk::cloud_rules::data::hub_item::{HubItem, Info}; - let (db, _dir) = open_test_db(); - let hub = HubRecord::new( - "fd9b2602-62c5-4d55-bd1e-0d6537714ca0".to_string(), - HubItem { - base_version: 6, - config_version: 1, - uuid: "fd9b2602-62c5-4d55-bd1e-0d6537714ca0".to_string(), - info: Info { - hub_name: "GitHub".to_string(), - hub_icon_url: None, - }, - api_keywords: vec!["owner".to_string(), "repo".to_string()], - auth_keywords: vec![], - app_url_templates: vec![], - target_check_api: None, - }, - ); - db.upsert_hub(&hub).unwrap(); - let hubs = db.load_hubs().unwrap(); - assert_eq!(hubs.len(), 1); - assert_eq!(hubs[0].uuid, "fd9b2602-62c5-4d55-bd1e-0d6537714ca0"); - - let deleted = db - .delete_hub("fd9b2602-62c5-4d55-bd1e-0d6537714ca0") - .unwrap(); - assert!(deleted); - assert!(db.load_hubs().unwrap().is_empty()); - } -} diff --git a/src/database/models/app.rs b/src/database/models/app.rs deleted file mode 100644 index 104e0ce..0000000 --- a/src/database/models/app.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::websdk::cloud_rules::data::app_item::AppItem; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct AppRecord { - /// UUID v4 identifier (replaces Room's auto-increment Long id) - pub id: String, - pub name: String, - pub app_id: HashMap>, - #[serde(skip_serializing_if = "Option::is_none")] - pub invalid_version_number_field_regex: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub include_version_number_field_regex: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub ignore_version_number: Option, - /// Cloud config (AppItem), optional - #[serde(skip_serializing_if = "Option::is_none")] - pub cloud_config: Option, - /// Space-separated hub UUIDs in priority order - #[serde(skip_serializing_if = "Option::is_none")] - pub enable_hub_list: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub star: Option, -} - -impl AppRecord { - pub fn new(name: String, app_id: HashMap>) -> Self { - Self { - id: uuid::Uuid::new_v4().to_string(), - name, - app_id, - invalid_version_number_field_regex: None, - include_version_number_field_regex: None, - ignore_version_number: None, - cloud_config: None, - enable_hub_list: None, - star: None, - } - } - - pub fn get_sorted_hub_uuids(&self) -> Vec { - match &self.enable_hub_list { - Some(s) if !s.is_empty() => s.split(' ').map(String::from).collect(), - _ => vec![], - } - } - - pub fn set_sorted_hub_uuids(&mut self, uuids: &[String]) { - let s = uuids.join(" "); - self.enable_hub_list = if s.is_empty() { None } else { Some(s) }; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn sample_app() -> AppRecord { - AppRecord { - id: "test-uuid".to_string(), - name: "TestApp".to_string(), - app_id: HashMap::from([("owner".to_string(), Some("alice".to_string()))]), - invalid_version_number_field_regex: None, - include_version_number_field_regex: None, - ignore_version_number: None, - cloud_config: None, - enable_hub_list: Some("hub1 hub2".to_string()), - star: Some(true), - } - } - - #[test] - fn test_serialization_roundtrip() { - let app = sample_app(); - let json = serde_json::to_string(&app).unwrap(); - let decoded: AppRecord = serde_json::from_str(&json).unwrap(); - assert_eq!(app, decoded); - } - - #[test] - fn test_get_sorted_hub_uuids() { - let app = sample_app(); - let uuids = app.get_sorted_hub_uuids(); - assert_eq!(uuids, vec!["hub1", "hub2"]); - } - - #[test] - fn test_set_sorted_hub_uuids() { - let mut app = sample_app(); - app.set_sorted_hub_uuids(&["a".to_string(), "b".to_string(), "c".to_string()]); - assert_eq!(app.enable_hub_list, Some("a b c".to_string())); - } - - #[test] - fn test_empty_hub_list_is_none() { - let mut app = sample_app(); - app.set_sorted_hub_uuids(&[]); - assert_eq!(app.enable_hub_list, None); - } -} diff --git a/src/database/models/extra_app.rs b/src/database/models/extra_app.rs deleted file mode 100644 index 0123376..0000000 --- a/src/database/models/extra_app.rs +++ /dev/null @@ -1,48 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ExtraAppRecord { - /// UUID v4 identifier - pub id: String, - pub app_id: HashMap>, - #[serde(skip_serializing_if = "Option::is_none")] - pub mark_version_number: Option, -} - -impl ExtraAppRecord { - pub fn new(app_id: HashMap>) -> Self { - Self { - id: uuid::Uuid::new_v4().to_string(), - app_id, - mark_version_number: None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_serialization_roundtrip() { - let record = ExtraAppRecord { - id: "test-uuid".to_string(), - app_id: HashMap::from([( - "android_app_package".to_string(), - Some("com.foo".to_string()), - )]), - mark_version_number: Some("1.2.3".to_string()), - }; - let json = serde_json::to_string(&record).unwrap(); - let decoded: ExtraAppRecord = serde_json::from_str(&json).unwrap(); - assert_eq!(record, decoded); - } - - #[test] - fn test_none_fields_skipped() { - let record = ExtraAppRecord::new(HashMap::new()); - let json = serde_json::to_string(&record).unwrap(); - assert!(!json.contains("mark_version_number")); - } -} diff --git a/src/database/models/extra_hub.rs b/src/database/models/extra_hub.rs deleted file mode 100644 index 4366d62..0000000 --- a/src/database/models/extra_hub.rs +++ /dev/null @@ -1,56 +0,0 @@ -use serde::{Deserialize, Serialize}; - -pub const GLOBAL_HUB_ID: &str = "GLOBAL"; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ExtraHubRecord { - /// Hub UUID or GLOBAL_HUB_ID - pub id: String, - #[serde(default)] - pub enable_global: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub url_replace_search: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub url_replace_string: Option, -} - -impl ExtraHubRecord { - pub fn new(id: String) -> Self { - Self { - id, - enable_global: false, - url_replace_search: None, - url_replace_string: None, - } - } - - pub fn global() -> Self { - Self::new(GLOBAL_HUB_ID.to_string()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_serialization_roundtrip() { - let record = ExtraHubRecord { - id: "some-hub-uuid".to_string(), - enable_global: true, - url_replace_search: Some("github.com".to_string()), - url_replace_string: Some("mirror.example.com".to_string()), - }; - let json = serde_json::to_string(&record).unwrap(); - let decoded: ExtraHubRecord = serde_json::from_str(&json).unwrap(); - assert_eq!(record, decoded); - } - - #[test] - fn test_none_fields_skipped() { - let record = ExtraHubRecord::global(); - let json = serde_json::to_string(&record).unwrap(); - assert!(!json.contains("url_replace_search")); - assert!(!json.contains("url_replace_string")); - } -} diff --git a/src/database/models/hub.rs b/src/database/models/hub.rs deleted file mode 100644 index 84e94ce..0000000 --- a/src/database/models/hub.rs +++ /dev/null @@ -1,80 +0,0 @@ -use crate::websdk::cloud_rules::data::hub_item::HubItem; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct HubRecord { - /// Hub UUID — primary key - pub uuid: String, - pub hub_config: HubItem, - pub auth: HashMap, - #[serde(default)] - pub ignore_app_id_list: Vec>>, - /// 0 = disabled, 1 = enabled - #[serde(default)] - pub applications_mode: i32, - #[serde(default)] - pub user_ignore_app_id_list: Vec>>, - /// Lower = higher priority. Default is -(hub list size). - #[serde(default)] - pub sort_point: i32, -} - -impl HubRecord { - pub fn new(uuid: String, hub_config: HubItem) -> Self { - Self { - uuid, - hub_config, - auth: HashMap::new(), - ignore_app_id_list: vec![], - applications_mode: 0, - user_ignore_app_id_list: vec![], - sort_point: 0, - } - } - - pub fn applications_mode_enabled(&self) -> bool { - self.applications_mode == 1 - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::websdk::cloud_rules::data::hub_item::Info; - - fn sample_hub() -> HubRecord { - HubRecord::new( - "fd9b2602-62c5-4d55-bd1e-0d6537714ca0".to_string(), - HubItem { - base_version: 6, - config_version: 3, - uuid: "fd9b2602-62c5-4d55-bd1e-0d6537714ca0".to_string(), - info: Info { - hub_name: "GitHub".to_string(), - hub_icon_url: None, - }, - api_keywords: vec!["owner".to_string(), "repo".to_string()], - auth_keywords: vec![], - app_url_templates: vec!["https://github.com/%owner/%repo/".to_string()], - target_check_api: None, - }, - ) - } - - #[test] - fn test_serialization_roundtrip() { - let hub = sample_hub(); - let json = serde_json::to_string(&hub).unwrap(); - let decoded: HubRecord = serde_json::from_str(&json).unwrap(); - assert_eq!(hub, decoded); - } - - #[test] - fn test_applications_mode() { - let mut hub = sample_hub(); - assert!(!hub.applications_mode_enabled()); - hub.applications_mode = 1; - assert!(hub.applications_mode_enabled()); - } -} diff --git a/src/database/models/mod.rs b/src/database/models/mod.rs deleted file mode 100644 index c4e40bc..0000000 --- a/src/database/models/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod app; -pub mod extra_app; -pub mod extra_hub; -pub mod hub; diff --git a/src/database/store.rs b/src/database/store.rs deleted file mode 100644 index 32d374f..0000000 --- a/src/database/store.rs +++ /dev/null @@ -1,266 +0,0 @@ -use file_locker::FileLock; -use jsonl::ReadError; -use serde::{de::DeserializeOwned, Serialize}; -use std::io::{BufReader, Seek, SeekFrom, Write}; -use std::path::{Path, PathBuf}; - -use crate::error::{Error, Result}; - -pub trait HasId { - fn id(&self) -> &str; -} - -/// A simple JSONL-backed persistent store for a single record type. -/// -/// Each line in the file is a JSON-serialized record. All mutating operations -/// use `file-locker` advisory locking to ensure safe concurrent access. -pub struct JsonlStore { - path: PathBuf, -} - -impl JsonlStore { - pub fn new(path: impl Into) -> Self { - Self { path: path.into() } - } - - pub fn path(&self) -> &Path { - &self.path - } - - /// Ensure the backing file exists (creates parent dirs as needed). - pub fn ensure_file(&self) -> Result<()> { - if !self.path.exists() { - if let Some(parent) = self.path.parent() { - std::fs::create_dir_all(parent)?; - } - std::fs::File::create(&self.path)?; - } - Ok(()) - } - - /// Read all records from the file line by line. - pub fn load_all(&self) -> Result> { - if !self.path.exists() { - return Ok(vec![]); - } - let file = std::fs::File::open(&self.path)?; - let reader = BufReader::new(file); - read_all_from_reader(reader) - } - - fn acquire_write_lock(&self) -> Result { - self.ensure_file()?; - FileLock::new(&self.path) - .blocking(true) - .writeable(true) - .lock() - .map_err(Error::Io) - } - - /// Overwrite the entire file with the given records (must already hold lock). - fn write_all_locked(lock: &mut FileLock, records: &[T]) -> Result<()> { - lock.file.set_len(0)?; - lock.file.seek(SeekFrom::Start(0))?; - for record in records { - jsonl::write(&mut lock.file, record) - .map_err(|e| Error::Other(format!("jsonl write: {e}")))?; - } - lock.file.flush()?; - Ok(()) - } - - /// Insert or replace a record (matched by id). - pub fn upsert(&self, record: &T) -> Result<()> { - let mut lock = self.acquire_write_lock()?; - let mut records: Vec = { - lock.file.seek(SeekFrom::Start(0))?; - read_all_from_reader(BufReader::new(&lock.file))? - }; - - let id = record.id(); - let serialized: T = serde_json::from_str( - &serde_json::to_string(record).map_err(|e| Error::Other(format!("serialize: {e}")))?, - ) - .map_err(|e| Error::Other(format!("deserialize: {e}")))?; - - if let Some(pos) = records.iter().position(|r| r.id() == id) { - records[pos] = serialized; - } else { - records.push(serialized); - } - - Self::write_all_locked(&mut lock, &records) - } - - /// Delete the record with the given id. Returns true if a record was removed. - pub fn delete(&self, id: &str) -> Result { - let mut lock = self.acquire_write_lock()?; - let records: Vec = { - lock.file.seek(SeekFrom::Start(0))?; - read_all_from_reader(BufReader::new(&lock.file))? - }; - - let original_len = records.len(); - let filtered: Vec = records.into_iter().filter(|r| r.id() != id).collect(); - let deleted = filtered.len() < original_len; - Self::write_all_locked(&mut lock, &filtered)?; - Ok(deleted) - } - - /// Find a record by id (read-only, no lock). - pub fn find_by_id(&self, id: &str) -> Result> { - Ok(self.load_all::()?.into_iter().find(|r| r.id() == id)) - } -} - -fn read_all_from_reader(mut reader: R) -> Result> { - let mut records = Vec::new(); - loop { - match jsonl::read::<_, T>(&mut reader) { - Ok(record) => records.push(record), - Err(ReadError::Eof) => break, - Err(ReadError::Deserialize(e)) => { - eprintln!("JsonlStore: skipping malformed line: {e}"); - } - Err(ReadError::Io(e)) => return Err(Error::Io(e)), - } - } - Ok(records) -} - -#[cfg(test)] -mod tests { - use super::*; - use serde::{Deserialize, Serialize}; - use tempfile::NamedTempFile; - - #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] - struct TestRecord { - id: String, - value: String, - } - - impl HasId for TestRecord { - fn id(&self) -> &str { - &self.id - } - } - - fn make_store() -> (JsonlStore, NamedTempFile) { - let tmp = NamedTempFile::new().unwrap(); - let store = JsonlStore::new(tmp.path()); - (store, tmp) - } - - #[test] - fn test_empty_load() { - let (store, _tmp) = make_store(); - let records: Vec = store.load_all().unwrap(); - assert!(records.is_empty()); - } - - #[test] - fn test_upsert_and_load() { - let (store, _tmp) = make_store(); - let r = TestRecord { - id: "1".to_string(), - value: "hello".to_string(), - }; - store.upsert(&r).unwrap(); - let records: Vec = store.load_all().unwrap(); - assert_eq!(records.len(), 1); - assert_eq!(records[0], r); - } - - #[test] - fn test_upsert_updates_existing() { - let (store, _tmp) = make_store(); - store - .upsert(&TestRecord { - id: "1".to_string(), - value: "old".to_string(), - }) - .unwrap(); - store - .upsert(&TestRecord { - id: "1".to_string(), - value: "new".to_string(), - }) - .unwrap(); - let records: Vec = store.load_all().unwrap(); - assert_eq!(records.len(), 1); - assert_eq!(records[0].value, "new"); - } - - #[test] - fn test_multiple_records() { - let (store, _tmp) = make_store(); - for i in 0..5 { - store - .upsert(&TestRecord { - id: i.to_string(), - value: format!("v{i}"), - }) - .unwrap(); - } - let records: Vec = store.load_all().unwrap(); - assert_eq!(records.len(), 5); - } - - #[test] - fn test_delete() { - let (store, _tmp) = make_store(); - store - .upsert(&TestRecord { - id: "1".to_string(), - value: "a".to_string(), - }) - .unwrap(); - store - .upsert(&TestRecord { - id: "2".to_string(), - value: "b".to_string(), - }) - .unwrap(); - let deleted = store.delete::("1").unwrap(); - assert!(deleted); - let records: Vec = store.load_all().unwrap(); - assert_eq!(records.len(), 1); - assert_eq!(records[0].id, "2"); - } - - #[test] - fn test_delete_nonexistent() { - let (store, _tmp) = make_store(); - let deleted = store.delete::("nope").unwrap(); - assert!(!deleted); - } - - #[test] - fn test_find_by_id() { - let (store, _tmp) = make_store(); - store - .upsert(&TestRecord { - id: "42".to_string(), - value: "answer".to_string(), - }) - .unwrap(); - let found: Option = store.find_by_id("42").unwrap(); - assert!(found.is_some()); - assert_eq!(found.unwrap().value, "answer"); - } - - #[test] - fn test_find_by_id_missing() { - let (store, _tmp) = make_store(); - let found: Option = store.find_by_id("999").unwrap(); - assert!(found.is_none()); - } - - #[test] - fn test_file_not_exist_returns_empty() { - let store = JsonlStore::new("/tmp/this_file_does_not_exist_upgradeall.jsonl"); - let records: Vec = store.load_all().unwrap(); - assert!(records.is_empty()); - } -} diff --git a/src/downloader/config.rs b/src/downloader/config.rs deleted file mode 100644 index a4b0e9d..0000000 --- a/src/downloader/config.rs +++ /dev/null @@ -1,199 +0,0 @@ -//! Configuration system for the downloader module - -use serde::{Deserialize, Serialize}; - -/// Downloader backend selection -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] -#[serde(rename_all = "lowercase")] -pub enum DownloaderBackend { - /// Use trauma downloader (default) - #[default] - Trauma, - // Future backends can be added here: - // /// Use reqwest downloader - // Reqwest, - // /// Use custom CLI command - // Custom, -} - -/// Download configuration -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DownloadConfig { - /// Downloader backend to use - pub backend: DownloaderBackend, - - /// Maximum number of concurrent downloads - #[serde(default = "default_max_concurrent")] - pub max_concurrent: usize, - - /// Number of retry attempts for failed downloads - #[serde(default = "default_retries")] - pub retries: usize, - - /// Timeout for each download in seconds - #[serde(default = "default_timeout")] - pub timeout_seconds: u64, - - /// Custom command template for CLI downloader (future use) - /// Example: "wget -O \"${FILE}\" \"${URI}\"" - #[serde(skip_serializing_if = "Option::is_none")] - pub custom_command: Option, -} - -fn default_max_concurrent() -> usize { - 4 -} - -fn default_retries() -> usize { - 3 -} - -fn default_timeout() -> u64 { - 300 // 5 minutes -} - -impl Default for DownloadConfig { - fn default() -> Self { - Self { - backend: DownloaderBackend::default(), - max_concurrent: default_max_concurrent(), - retries: default_retries(), - timeout_seconds: default_timeout(), - custom_command: None, - } - } -} - -impl DownloadConfig { - /// Create a new configuration with default values - pub fn new() -> Self { - Self::default() - } - - /// Set the backend - pub fn with_backend(mut self, backend: DownloaderBackend) -> Self { - self.backend = backend; - self - } - - /// Set max concurrent downloads - pub fn with_max_concurrent(mut self, max: usize) -> Self { - self.max_concurrent = max; - self - } - - /// Set retry count - pub fn with_retries(mut self, retries: usize) -> Self { - self.retries = retries; - self - } - - /// Set timeout in seconds - pub fn with_timeout(mut self, seconds: u64) -> Self { - self.timeout_seconds = seconds; - self - } - - /// Set custom command template - pub fn with_custom_command(mut self, command: impl Into) -> Self { - self.custom_command = Some(command.into()); - self - } - - /// Load configuration from environment variables - /// - /// Supported environment variables: - /// - DOWNLOADER_BACKEND: "trauma" (default) - /// - DOWNLOADER_MAX_CONCURRENT: number (default: 4) - /// - DOWNLOADER_RETRIES: number (default: 3) - /// - DOWNLOADER_TIMEOUT: seconds (default: 300) - /// - FETCHCOMMAND: custom download command (like Portage's FETCHCOMMAND) - pub fn from_env() -> Self { - let mut config = Self::default(); - - if let Ok(backend) = std::env::var("DOWNLOADER_BACKEND") { - config.backend = match backend.to_lowercase().as_str() { - "trauma" => DownloaderBackend::Trauma, - _ => { - eprintln!("Unknown DOWNLOADER_BACKEND: {}, using default", backend); - DownloaderBackend::Trauma - } - }; - } - - if let Ok(max_concurrent) = std::env::var("DOWNLOADER_MAX_CONCURRENT") { - if let Ok(max) = max_concurrent.parse() { - config.max_concurrent = max; - } - } - - if let Ok(retries) = std::env::var("DOWNLOADER_RETRIES") { - if let Ok(retry_count) = retries.parse() { - config.retries = retry_count; - } - } - - if let Ok(timeout) = std::env::var("DOWNLOADER_TIMEOUT") { - if let Ok(timeout_secs) = timeout.parse() { - config.timeout_seconds = timeout_secs; - } - } - - if let Ok(command) = std::env::var("FETCHCOMMAND") { - config.custom_command = Some(command); - } - - config - } - - /// Load configuration from JSON string - pub fn from_json(json: &str) -> Result { - serde_json::from_str(json) - } - - /// Serialize configuration to JSON string - pub fn to_json(&self) -> Result { - serde_json::to_string_pretty(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_default_config() { - let config = DownloadConfig::default(); - assert_eq!(config.backend, DownloaderBackend::Trauma); - assert_eq!(config.max_concurrent, 4); - assert_eq!(config.retries, 3); - assert_eq!(config.timeout_seconds, 300); - assert!(config.custom_command.is_none()); - } - - #[test] - fn test_builder_pattern() { - let config = DownloadConfig::new() - .with_backend(DownloaderBackend::Trauma) - .with_max_concurrent(8) - .with_retries(5) - .with_timeout(600) - .with_custom_command("wget -O \"${FILE}\" \"${URI}\""); - - assert_eq!(config.backend, DownloaderBackend::Trauma); - assert_eq!(config.max_concurrent, 8); - assert_eq!(config.retries, 5); - assert_eq!(config.timeout_seconds, 600); - assert!(config.custom_command.is_some()); - } - - #[test] - fn test_json_serialization() { - let config = DownloadConfig::new().with_max_concurrent(8); - let json = config.to_json().unwrap(); - let deserialized: DownloadConfig = DownloadConfig::from_json(&json).unwrap(); - - assert_eq!(config.backend, deserialized.backend); - assert_eq!(config.max_concurrent, deserialized.max_concurrent); - } -} diff --git a/src/downloader/error.rs b/src/downloader/error.rs deleted file mode 100644 index 7384726..0000000 --- a/src/downloader/error.rs +++ /dev/null @@ -1,95 +0,0 @@ -//! Error types for the downloader module - -use std::fmt; - -/// Error type for download operations -#[derive(Debug, Clone)] -pub struct DownloadError { - pub kind: ErrorKind, - pub message: String, -} - -/// Kinds of download errors -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ErrorKind { - /// Network error (connection failed, timeout, etc.) - Network, - /// File system error (permission denied, disk full, etc.) - FileSystem, - /// Invalid input (bad URL, invalid path, etc.) - InvalidInput, - /// Task not found - TaskNotFound, - /// Task already exists - TaskAlreadyExists, - /// Download was cancelled - Cancelled, - /// Operation not supported by this downloader implementation - Unsupported, - /// Unknown error - Unknown, -} - -impl DownloadError { - pub fn new(kind: ErrorKind, message: impl Into) -> Self { - Self { - kind, - message: message.into(), - } - } - - pub fn network(message: impl Into) -> Self { - Self::new(ErrorKind::Network, message) - } - - pub fn file_system(message: impl Into) -> Self { - Self::new(ErrorKind::FileSystem, message) - } - - pub fn invalid_input(message: impl Into) -> Self { - Self::new(ErrorKind::InvalidInput, message) - } - - pub fn task_not_found(task_id: impl fmt::Display) -> Self { - Self::new( - ErrorKind::TaskNotFound, - format!("Task not found: {}", task_id), - ) - } - - pub fn task_already_exists(task_id: impl fmt::Display) -> Self { - Self::new( - ErrorKind::TaskAlreadyExists, - format!("Task already exists: {}", task_id), - ) - } - - pub fn cancelled(message: impl Into) -> Self { - Self::new(ErrorKind::Cancelled, message) - } - - pub fn unsupported(message: impl Into) -> Self { - Self::new(ErrorKind::Unsupported, message) - } - - pub fn unknown(message: impl Into) -> Self { - Self::new(ErrorKind::Unknown, message) - } -} - -impl fmt::Display for DownloadError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}: {}", self.kind, self.message) - } -} - -impl std::error::Error for DownloadError {} - -impl From for DownloadError { - fn from(err: std::io::Error) -> Self { - Self::file_system(err.to_string()) - } -} - -/// Result type for download operations -pub type Result = std::result::Result; diff --git a/src/downloader/external_rpc_impl.rs b/src/downloader/external_rpc_impl.rs deleted file mode 100644 index e98debd..0000000 --- a/src/downloader/external_rpc_impl.rs +++ /dev/null @@ -1,290 +0,0 @@ -//! External RPC-based downloader implementation -//! -//! Delegates download operations to an external service via HTTP JSON-RPC 2.0. -//! The external service (e.g., a Kotlin-side GooglePlayDownloader) must implement -//! the standard downloader RPC protocol: -//! - download_submit(url, dest_path, headers?, cookies?) -> {task_id} -//! - download_get_status(task_id) -> TaskInfo -//! - download_wait_for_change(task_id, timeout_seconds) -> TaskInfo -//! - download_pause(task_id) -> bool -//! - download_resume(task_id) -> bool -//! - download_cancel(task_id) -> bool - -use super::error::{DownloadError, Result}; -use super::traits::{Downloader, DownloaderCapabilities, ProgressCallback, RequestOptions}; -use async_trait::async_trait; -use parking_lot::RwLock; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::path::{Path, PathBuf}; - -/// JSON-RPC 2.0 request structure -#[derive(Serialize)] -struct JsonRpcRequest<'a> { - jsonrpc: &'static str, - method: &'a str, - params: serde_json::Value, - id: u64, -} - -/// JSON-RPC 2.0 response structure -#[derive(Deserialize)] -struct JsonRpcResponse { - #[allow(dead_code)] - jsonrpc: Option, - result: Option, - error: Option, - #[allow(dead_code)] - id: Option, -} - -#[derive(Deserialize)] -struct JsonRpcError { - #[allow(dead_code)] - code: i64, - message: String, -} - -/// Task ID response from external download_submit -#[derive(Deserialize)] -struct ExternalTaskIdResponse { - task_id: String, -} - -/// Task info from external service (subset we care about) -#[derive(Deserialize, Debug)] -struct ExternalTaskInfo { - #[allow(dead_code)] - task_id: String, - state: String, - progress: ExternalProgress, - error: Option, -} - -#[derive(Deserialize, Debug)] -struct ExternalProgress { - downloaded_bytes: u64, - total_bytes: Option, - #[allow(dead_code)] - speed_bytes_per_sec: Option, - #[allow(dead_code)] - eta_seconds: Option, -} - -/// Downloader that delegates all operations to an external JSON-RPC service. -/// -/// The external service is expected to implement the full downloader protocol -/// (submit, status, wait_for_change, pause, resume, cancel). -pub struct ExternalRpcDownloader { - rpc_url: String, - http_client: reqwest::Client, - /// url -> external task_id (for routing cancel/pause/resume by url) - task_mapping: RwLock>, - /// Atomic request ID counter - request_id: std::sync::atomic::AtomicU64, - capabilities: DownloaderCapabilities, -} - -impl ExternalRpcDownloader { - /// Create a new external RPC downloader pointing at the given service URL. - pub fn new(rpc_url: String) -> Self { - Self { - rpc_url, - http_client: reqwest::Client::new(), - task_mapping: RwLock::new(HashMap::new()), - request_id: std::sync::atomic::AtomicU64::new(1), - capabilities: DownloaderCapabilities::all_enabled(), - } - } - - /// Make a JSON-RPC call to the external service. - async fn rpc_call( - &self, - method: &str, - params: serde_json::Value, - ) -> Result { - let id = self - .request_id - .fetch_add(1, std::sync::atomic::Ordering::Relaxed); - - let request = JsonRpcRequest { - jsonrpc: "2.0", - method, - params, - id, - }; - - let response = self - .http_client - .post(&self.rpc_url) - .json(&request) - .send() - .await - .map_err(|e| DownloadError::network(format!("RPC request failed: {}", e)))?; - - let rpc_response: JsonRpcResponse = response - .json() - .await - .map_err(|e| DownloadError::network(format!("RPC response parse failed: {}", e)))?; - - if let Some(err) = rpc_response.error { - return Err(DownloadError::network(format!( - "RPC error: {}", - err.message - ))); - } - - let result = rpc_response - .result - .ok_or_else(|| DownloadError::network("RPC response missing result"))?; - - serde_json::from_value(result) - .map_err(|e| DownloadError::network(format!("RPC result deserialize failed: {}", e))) - } -} - -#[async_trait] -impl Downloader for ExternalRpcDownloader { - async fn download( - &self, - url: &str, - dest: &Path, - progress: Option, - options: Option, - ) -> Result<()> { - // 1. Submit download task to external service - let params = serde_json::json!({ - "url": url, - "dest_path": dest.to_str().unwrap_or(""), - "headers": options.as_ref().and_then(|o| o.headers.clone()), - "cookies": options.as_ref().and_then(|o| o.cookies.clone()), - }); - - let submit_response: ExternalTaskIdResponse = - self.rpc_call("download_submit", params).await?; - let external_task_id = submit_response.task_id; - - // 2. Record mapping for cancel/pause/resume - self.task_mapping - .write() - .insert(url.to_string(), external_task_id.clone()); - - // 3. Poll for status changes until terminal state - loop { - let params = serde_json::json!({ - "task_id": &external_task_id, - "timeout_seconds": 30_u64, - }); - - let task_info: ExternalTaskInfo = - match self.rpc_call("download_wait_for_change", params).await { - Ok(info) => info, - Err(e) => { - // On poll error, try a direct status check - let status_params = serde_json::json!({ - "task_id": &external_task_id, - }); - match self - .rpc_call::("download_get_status", status_params) - .await - { - Ok(info) => info, - Err(_) => { - // Both failed, clean up and return error - self.task_mapping.write().remove(url); - return Err(e); - } - } - } - }; - - // Update progress callback - if let Some(ref cb) = progress { - cb( - task_info.progress.downloaded_bytes, - task_info.progress.total_bytes, - ); - } - - // Check terminal states - match task_info.state.as_str() { - "completed" => { - self.task_mapping.write().remove(url); - return Ok(()); - } - "failed" => { - self.task_mapping.write().remove(url); - let msg = task_info - .error - .unwrap_or_else(|| "External download failed".to_string()); - return Err(DownloadError::network(msg)); - } - "cancelled" => { - self.task_mapping.write().remove(url); - return Err(DownloadError::cancelled("Download was cancelled")); - } - _ => { - // pending, downloading, stopped — continue polling - continue; - } - } - } - } - - async fn download_batch(&self, tasks: Vec<(String, PathBuf)>) -> Vec> { - // Simple sequential implementation for external downloaders - let mut results = Vec::with_capacity(tasks.len()); - for (url, dest) in tasks { - let result = self.download(&url, &dest, None, None).await; - results.push(result); - } - results - } - - fn name(&self) -> &str { - "external_rpc" - } - - fn capabilities(&self) -> &DownloaderCapabilities { - &self.capabilities - } - - async fn cancel(&self, url: &str) -> Result<()> { - let ext_id = self.task_mapping.read().get(url).cloned(); - if let Some(ext_id) = ext_id { - let params = serde_json::json!({"task_id": ext_id}); - let _: bool = self.rpc_call("download_cancel", params).await?; - } - Ok(()) - } - - async fn pause(&self, url: &str) -> Result<()> { - let ext_id = self.task_mapping.read().get(url).cloned(); - if let Some(ext_id) = ext_id { - let params = serde_json::json!({"task_id": ext_id}); - let _: bool = self.rpc_call("download_pause", params).await?; - } - Ok(()) - } - - async fn resume(&self, url: &str) -> Result<()> { - let ext_id = self.task_mapping.read().get(url).cloned(); - if let Some(ext_id) = ext_id { - let params = serde_json::json!({"task_id": ext_id}); - let _: bool = self.rpc_call("download_resume", params).await?; - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_create_external_downloader() { - let dl = ExternalRpcDownloader::new("http://127.0.0.1:12345".to_string()); - assert_eq!(dl.name(), "external_rpc"); - assert!(dl.capabilities().supports_pause); - } -} diff --git a/src/downloader/hub_dispatch.rs b/src/downloader/hub_dispatch.rs deleted file mode 100644 index c6b753b..0000000 --- a/src/downloader/hub_dispatch.rs +++ /dev/null @@ -1,208 +0,0 @@ -//! Hub-based download dispatcher -//! -//! Routes download tasks to registered external downloaders based on hub_uuid. -//! If no external downloader is registered for the given hub_uuid, falls back -//! to the default built-in downloader (TraumaDownloader / HTTP). - -use super::error::Result; -use super::traits::{Downloader, DownloaderCapabilities, ProgressCallback, RequestOptions}; -use async_trait::async_trait; -use parking_lot::RwLock; -use std::collections::HashMap; -use std::path::{Path, PathBuf}; -use std::sync::Arc; - -/// Shared internal state for HubDispatchDownloader. -/// -/// Wrapped in Arc so that clones are cheap and share the same state. -/// This allows the RPC server to hold a reference for register/unregister -/// while DownloadTaskManager holds another reference as Box. -struct HubDispatchState { - /// hub_uuid -> external downloader - external: RwLock>>>, - /// Default downloader for unregistered hub_uuids (TraumaDownloader) - default: Arc>, - /// Active task tracking: url -> hub_uuid (for routing cancel/pause/resume) - active_tasks: RwLock>>, -} - -/// Downloads dispatcher that routes tasks by hub_uuid. -/// -/// - If `hub_uuid` is provided (via `RequestOptions.metadata["hub_uuid"]`) -/// and a downloader is registered for it, the task is routed there. -/// - Otherwise, the default built-in HTTP downloader handles it. -/// -/// This struct is cheaply cloneable (Arc-based shared state). -pub struct HubDispatchDownloader { - state: Arc, -} - -impl HubDispatchDownloader { - /// Create a new dispatcher with the given default downloader. - pub fn new(default: Box) -> Self { - Self { - state: Arc::new(HubDispatchState { - external: RwLock::new(HashMap::new()), - default: Arc::new(default), - active_tasks: RwLock::new(HashMap::new()), - }), - } - } - - /// Register an external downloader for a specific hub_uuid. - pub fn register(&self, hub_uuid: &str, downloader: Box) { - self.state - .external - .write() - .insert(hub_uuid.to_string(), Arc::new(downloader)); - } - - /// Unregister the external downloader for a specific hub_uuid. - pub fn unregister(&self, hub_uuid: &str) { - self.state.external.write().remove(hub_uuid); - } - - /// Check if a downloader is registered for the given hub_uuid. - pub fn has_downloader(&self, hub_uuid: &str) -> bool { - self.state.external.read().contains_key(hub_uuid) - } - - /// Resolve which downloader to use based on hub_uuid. - fn resolve(&self, hub_uuid: Option<&str>) -> Arc> { - if let Some(uuid) = hub_uuid { - if let Some(dl) = self.state.external.read().get(uuid) { - return dl.clone(); - } - } - self.state.default.clone() - } - - /// Extract hub_uuid from RequestOptions metadata. - fn extract_hub_uuid(options: Option<&RequestOptions>) -> Option { - options - .and_then(|o| o.metadata.as_ref()) - .and_then(|m| m.get("hub_uuid")) - .cloned() - } - - /// Record an active task for url -> hub_uuid routing. - fn track_task(&self, url: &str, hub_uuid: Option) { - self.state - .active_tasks - .write() - .insert(url.to_string(), hub_uuid); - } - - /// Remove an active task record. - fn untrack_task(&self, url: &str) { - self.state.active_tasks.write().remove(url); - } - - /// Look up which hub_uuid (if any) an active URL belongs to. - fn lookup_task_hub(&self, url: &str) -> Option> { - self.state.active_tasks.read().get(url).cloned() - } -} - -impl Clone for HubDispatchDownloader { - fn clone(&self) -> Self { - Self { - state: self.state.clone(), - } - } -} - -#[async_trait] -impl Downloader for HubDispatchDownloader { - async fn download( - &self, - url: &str, - dest: &Path, - progress: Option, - options: Option, - ) -> Result<()> { - let hub_uuid = Self::extract_hub_uuid(options.as_ref()); - let downloader = self.resolve(hub_uuid.as_deref()); - - // Track this task so cancel/pause/resume can find the right downloader - self.track_task(url, hub_uuid); - - let result = downloader.download(url, dest, progress, options).await; - - // Clean up tracking on completion (success or failure) - self.untrack_task(url); - - result - } - - async fn download_batch(&self, tasks: Vec<(String, PathBuf)>) -> Vec> { - // Batch downloads go through the default downloader since there's - // no per-task metadata available in the batch interface. - self.state.default.download_batch(tasks).await - } - - fn name(&self) -> &str { - "hub_dispatch" - } - - fn capabilities(&self) -> &DownloaderCapabilities { - // Return default backend capabilities - self.state.default.capabilities() - } - - async fn cancel(&self, url: &str) -> Result<()> { - let hub_uuid = self.lookup_task_hub(url).flatten(); - self.resolve(hub_uuid.as_deref()).cancel(url).await - } - - async fn pause(&self, url: &str) -> Result<()> { - let hub_uuid = self.lookup_task_hub(url).flatten(); - self.resolve(hub_uuid.as_deref()).pause(url).await - } - - async fn resume(&self, url: &str) -> Result<()> { - let hub_uuid = self.lookup_task_hub(url).flatten(); - self.resolve(hub_uuid.as_deref()).resume(url).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::downloader::TraumaDownloader; - - #[test] - fn test_create_dispatcher() { - let default = Box::new(TraumaDownloader::default_settings()); - let dispatcher = HubDispatchDownloader::new(default); - assert_eq!(dispatcher.name(), "hub_dispatch"); - assert!(!dispatcher.has_downloader("some-uuid")); - } - - #[test] - fn test_register_unregister() { - let default = Box::new(TraumaDownloader::default_settings()); - let dispatcher = HubDispatchDownloader::new(default); - - let ext = Box::new(TraumaDownloader::default_settings()); - dispatcher.register("test-uuid", ext); - assert!(dispatcher.has_downloader("test-uuid")); - - dispatcher.unregister("test-uuid"); - assert!(!dispatcher.has_downloader("test-uuid")); - } - - #[test] - fn test_clone_shares_state() { - let default = Box::new(TraumaDownloader::default_settings()); - let dispatcher = HubDispatchDownloader::new(default); - - let clone = dispatcher.clone(); - - let ext = Box::new(TraumaDownloader::default_settings()); - dispatcher.register("shared-uuid", ext); - - // Clone should see the registration - assert!(clone.has_downloader("shared-uuid")); - } -} diff --git a/src/downloader/mod.rs b/src/downloader/mod.rs deleted file mode 100644 index d4f2233..0000000 --- a/src/downloader/mod.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! Downloader module providing pluggable download functionality -//! -//! This module implements a flexible, trait-based downloader system that supports: -//! - Multiple backend implementations (trauma, reqwest, custom CLI, etc.) -//! - Task state management with progress tracking -//! - Long-polling for download status updates -//! - Configuration-driven backend selection -//! - JSON-RPC integration for remote control - -mod config; -mod error; -mod external_rpc_impl; -mod hub_dispatch; -mod state; -mod task_manager; -mod traits; -mod trauma_impl; - -pub use config::{DownloadConfig, DownloaderBackend}; -pub use error::{DownloadError, Result}; -pub use external_rpc_impl::ExternalRpcDownloader; -pub use hub_dispatch::HubDispatchDownloader; -pub use state::{DownloadProgress, DownloadState, SpeedCalculator, TaskInfo}; -pub use task_manager::DownloadTaskManager; -pub use traits::Downloader; -pub use trauma_impl::TraumaDownloader; - -/// Create a downloader instance based on the provided configuration -pub fn create_downloader(config: &DownloadConfig) -> Box { - match config.backend { - DownloaderBackend::Trauma => Box::new(TraumaDownloader::new( - config.max_concurrent, - config.retries, - config.timeout_seconds, - )), - // Future implementations can be added here - // DownloaderBackend::Reqwest => Box::new(ReqwestDownloader::new(config)), - // DownloaderBackend::Custom => Box::new(CliDownloader::new(config)), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_create_trauma_downloader() { - let config = DownloadConfig::default(); - let downloader = create_downloader(&config); - assert_eq!(downloader.name(), "reqwest"); - - // Test that capabilities are accessible - let caps = downloader.capabilities(); - assert!(caps.supports_pause); - assert!(caps.supports_resume); - assert!(caps.supports_cancellation); - } -} diff --git a/src/downloader/state.rs b/src/downloader/state.rs deleted file mode 100644 index 718b33b..0000000 --- a/src/downloader/state.rs +++ /dev/null @@ -1,532 +0,0 @@ -//! Download state and progress tracking types - -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::collections::VecDeque; -use std::time::{Instant, SystemTime, UNIX_EPOCH}; -use uuid::Uuid; - -/// Speed calculator using sliding window for smooth speed measurement -/// -/// Keeps track of download samples over a time window and calculates -/// average speed to avoid fluctuations. -#[derive(Debug, Clone)] -pub struct SpeedCalculator { - /// Samples: (timestamp, downloaded_bytes) - samples: VecDeque<(Instant, u64)>, - /// Window size in seconds - window_secs: u64, - /// Maximum number of samples to keep - max_samples: usize, - /// Start time for calculating speed when only one sample exists - start_time: Option, -} - -impl SpeedCalculator { - /// Create a new speed calculator with specified window size - pub fn new(window_secs: u64) -> Self { - Self { - samples: VecDeque::with_capacity(64), - window_secs, - max_samples: 64, - start_time: None, - } - } - - /// Create with default 5-second window - pub fn default_window() -> Self { - Self::new(5) - } - - /// Record a new sample - pub fn record(&mut self, downloaded_bytes: u64) { - let now = Instant::now(); - - // Initialize start time on first record - if self.start_time.is_none() { - self.start_time = Some(now); - } - - // Remove samples outside the window - let cutoff = now - std::time::Duration::from_secs(self.window_secs); - while let Some((time, _)) = self.samples.front() { - if *time < cutoff { - self.samples.pop_front(); - } else { - break; - } - } - - // Add new sample - self.samples.push_back((now, downloaded_bytes)); - - // Limit max samples - while self.samples.len() > self.max_samples { - self.samples.pop_front(); - } - } - - /// Calculate average speed in bytes per second - pub fn speed_bytes_per_sec(&self) -> Option { - let (last_time, last_bytes) = self.samples.back()?; - - // Use sliding window if we have multiple samples - if self.samples.len() >= 2 { - let (first_time, first_bytes) = self.samples.front()?; - let duration = last_time.duration_since(*first_time); - let duration_secs = duration.as_secs_f64().max(0.001); - let bytes_diff = last_bytes.saturating_sub(*first_bytes); - return Some((bytes_diff as f64 / duration_secs) as u64); - } - - // For single sample, calculate from start time - if let Some(start) = self.start_time { - let duration = last_time.duration_since(start); - let duration_secs = duration.as_secs_f64().max(0.001); - return Some((*last_bytes as f64 / duration_secs) as u64); - } - - None - } - - /// Reset the calculator - pub fn reset(&mut self) { - self.samples.clear(); - self.start_time = None; - } -} - -/// Serialize SystemTime as Unix timestamp in milliseconds -fn serialize_system_time(time: &SystemTime, serializer: S) -> Result -where - S: Serializer, -{ - let millis = time - .duration_since(UNIX_EPOCH) - .map(|d| d.as_millis() as i64) - .unwrap_or(0); - serializer.serialize_i64(millis) -} - -/// Serialize Option as Unix timestamp in milliseconds -fn serialize_option_system_time( - time: &Option, - serializer: S, -) -> Result -where - S: Serializer, -{ - match time { - Some(t) => { - let millis = t - .duration_since(UNIX_EPOCH) - .map(|d| d.as_millis() as i64) - .unwrap_or(0); - serializer.serialize_some(&millis) - } - None => serializer.serialize_none(), - } -} - -/// Deserialize Unix timestamp in milliseconds to SystemTime -fn deserialize_system_time<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let millis = i64::deserialize(deserializer)?; - Ok(UNIX_EPOCH + std::time::Duration::from_millis(millis as u64)) -} - -/// Deserialize Unix timestamp in milliseconds to Option -fn deserialize_option_system_time<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let opt: Option = Option::deserialize(deserializer)?; - Ok(opt.map(|millis| UNIX_EPOCH + std::time::Duration::from_millis(millis as u64))) -} - -/// Download task state -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum DownloadState { - /// Task is queued but not started yet - Pending, - /// Download is in progress - Downloading, - /// Download is paused (can be resumed) - Stopped, - /// Download completed successfully - Completed, - /// Download failed - Failed, - /// Download was cancelled - Cancelled, -} - -impl DownloadState { - /// Check if the task is in a terminal state (completed, failed, or cancelled) - pub fn is_terminal(&self) -> bool { - matches!( - self, - DownloadState::Completed | DownloadState::Failed | DownloadState::Cancelled - ) - } - - /// Check if the task is active (pending, downloading, or stopped/paused) - pub fn is_active(&self) -> bool { - matches!( - self, - DownloadState::Pending | DownloadState::Downloading | DownloadState::Stopped - ) - } - - /// Check if the task can be resumed - pub fn is_resumable(&self) -> bool { - matches!(self, DownloadState::Stopped | DownloadState::Failed) - } - - /// Check if the task can be paused - pub fn is_pausable(&self) -> bool { - matches!(self, DownloadState::Downloading) - } -} - -/// Download progress information -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct DownloadProgress { - /// Number of bytes downloaded so far - pub downloaded_bytes: u64, - - /// Total size in bytes (None if unknown) - pub total_bytes: Option, - - /// Download speed in bytes per second (average) - pub speed_bytes_per_sec: Option, - - /// Estimated time remaining in seconds (None if unknown) - pub eta_seconds: Option, -} - -impl DownloadProgress { - /// Create a new progress with downloaded bytes - pub fn new(downloaded_bytes: u64, total_bytes: Option) -> Self { - Self { - downloaded_bytes, - total_bytes, - speed_bytes_per_sec: None, - eta_seconds: None, - } - } - - /// Create a new progress with speed and ETA - pub fn with_speed( - downloaded_bytes: u64, - total_bytes: Option, - speed_bytes_per_sec: Option, - ) -> Self { - let eta_seconds = match (total_bytes, speed_bytes_per_sec) { - (Some(total), Some(speed)) if speed > 0 && total > downloaded_bytes => { - Some((total - downloaded_bytes) / speed) - } - _ => None, - }; - - Self { - downloaded_bytes, - total_bytes, - speed_bytes_per_sec, - eta_seconds, - } - } - - /// Calculate progress percentage (0-100) - pub fn percentage(&self) -> Option { - self.total_bytes.map(|total| { - if total == 0 { - 0.0 - } else { - (self.downloaded_bytes as f64 / total as f64) * 100.0 - } - }) - } - - /// Check if download is complete - pub fn is_complete(&self) -> bool { - if let Some(total) = self.total_bytes { - self.downloaded_bytes >= total - } else { - false - } - } -} - -/// Complete information about a download task -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct TaskInfo { - /// Unique task identifier - pub task_id: String, - - /// Source URL - pub url: String, - - /// Destination file path - pub dest_path: String, - - /// Current state - pub state: DownloadState, - - /// Progress information - pub progress: DownloadProgress, - - /// Resume offset in bytes (for resuming paused downloads) - pub resume_offset: u64, - - /// Whether the server supports HTTP Range requests (None if not tested yet) - pub supports_range: Option, - - /// Error message (if state is Failed) - pub error: Option, - - /// Task creation timestamp (Unix timestamp in milliseconds) - #[serde( - serialize_with = "serialize_system_time", - deserialize_with = "deserialize_system_time" - )] - pub created_at: SystemTime, - - /// Task start timestamp (Unix timestamp in milliseconds, None if not started yet) - #[serde( - serialize_with = "serialize_option_system_time", - deserialize_with = "deserialize_option_system_time" - )] - pub started_at: Option, - - /// Task completion timestamp (Unix timestamp in milliseconds, None if not completed) - #[serde( - serialize_with = "serialize_option_system_time", - deserialize_with = "deserialize_option_system_time" - )] - pub completed_at: Option, - - /// Task last paused timestamp (Unix timestamp in milliseconds, None if never paused) - #[serde( - serialize_with = "serialize_option_system_time", - deserialize_with = "deserialize_option_system_time" - )] - pub paused_at: Option, - - /// HTTP headers to include in requests - #[serde(default)] - pub headers: Option>, - - /// HTTP cookies to include in requests - #[serde(default)] - pub cookies: Option>, - - /// Hub UUID for dispatching to registered external downloaders. - /// When set, the download task manager routes this task to the - /// external downloader registered for this hub_uuid. - /// When None, the default built-in downloader is used. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub hub_uuid: Option, -} - -impl TaskInfo { - /// Create a new task info - pub fn new(url: impl Into, dest_path: impl Into) -> Self { - Self { - task_id: Uuid::new_v4().to_string(), - url: url.into(), - dest_path: dest_path.into(), - state: DownloadState::Pending, - progress: DownloadProgress::default(), - resume_offset: 0, - supports_range: None, - error: None, - created_at: SystemTime::now(), - started_at: None, - completed_at: None, - paused_at: None, - headers: None, - cookies: None, - hub_uuid: None, - } - } - - /// Create with a specific task ID - pub fn with_id( - task_id: impl Into, - url: impl Into, - dest_path: impl Into, - ) -> Self { - Self { - task_id: task_id.into(), - url: url.into(), - dest_path: dest_path.into(), - state: DownloadState::Pending, - progress: DownloadProgress::default(), - resume_offset: 0, - supports_range: None, - error: None, - created_at: SystemTime::now(), - started_at: None, - completed_at: None, - paused_at: None, - headers: None, - cookies: None, - hub_uuid: None, - } - } - - /// Create with headers, cookies, and optional hub_uuid - pub fn with_options( - task_id: impl Into, - url: impl Into, - dest_path: impl Into, - headers: Option>, - cookies: Option>, - hub_uuid: Option, - ) -> Self { - Self { - task_id: task_id.into(), - url: url.into(), - dest_path: dest_path.into(), - state: DownloadState::Pending, - progress: DownloadProgress::default(), - resume_offset: 0, - supports_range: None, - error: None, - created_at: SystemTime::now(), - started_at: None, - completed_at: None, - paused_at: None, - headers, - cookies, - hub_uuid, - } - } - - /// Mark task as started - pub fn mark_started(&mut self) { - self.state = DownloadState::Downloading; - self.started_at = Some(SystemTime::now()); - } - - /// Mark task as completed - pub fn mark_completed(&mut self) { - self.state = DownloadState::Completed; - self.completed_at = Some(SystemTime::now()); - } - - /// Mark task as failed with error message - pub fn mark_failed(&mut self, error: impl Into) { - self.state = DownloadState::Failed; - self.error = Some(error.into()); - self.completed_at = Some(SystemTime::now()); - } - - /// Mark task as cancelled - pub fn mark_cancelled(&mut self) { - self.state = DownloadState::Cancelled; - self.completed_at = Some(SystemTime::now()); - } - - /// Mark task as stopped/paused - pub fn mark_stopped(&mut self) { - self.state = DownloadState::Stopped; - self.resume_offset = self.progress.downloaded_bytes; - self.paused_at = Some(SystemTime::now()); - } - - /// Mark task as resumed from stopped state - pub fn mark_resumed(&mut self) { - self.state = DownloadState::Downloading; - self.paused_at = None; - } - - /// Update progress - pub fn update_progress(&mut self, progress: DownloadProgress) { - self.progress = progress; - } - - /// Set whether the server supports Range requests - pub fn set_range_support(&mut self, supports: bool) { - self.supports_range = Some(supports); - } - - /// Calculate elapsed time in seconds - pub fn elapsed_seconds(&self) -> Option { - self.started_at.and_then(|start| { - SystemTime::now() - .duration_since(start) - .ok() - .map(|d| d.as_secs()) - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_download_state() { - assert!(DownloadState::Completed.is_terminal()); - assert!(DownloadState::Failed.is_terminal()); - assert!(DownloadState::Cancelled.is_terminal()); - assert!(!DownloadState::Pending.is_terminal()); - assert!(!DownloadState::Downloading.is_terminal()); - assert!(!DownloadState::Stopped.is_terminal()); - - assert!(DownloadState::Pending.is_active()); - assert!(DownloadState::Downloading.is_active()); - assert!(DownloadState::Stopped.is_active()); - assert!(!DownloadState::Completed.is_active()); - - assert!(DownloadState::Downloading.is_pausable()); - assert!(!DownloadState::Stopped.is_pausable()); - assert!(!DownloadState::Completed.is_pausable()); - - assert!(DownloadState::Stopped.is_resumable()); - assert!(DownloadState::Failed.is_resumable()); - assert!(!DownloadState::Downloading.is_resumable()); - assert!(!DownloadState::Completed.is_resumable()); - } - - #[test] - fn test_download_progress() { - let mut progress = DownloadProgress::new(50, Some(100)); - assert_eq!(progress.percentage(), Some(50.0)); - assert!(!progress.is_complete()); - - progress.downloaded_bytes = 100; - assert_eq!(progress.percentage(), Some(100.0)); - assert!(progress.is_complete()); - } - - #[test] - fn test_task_info() { - let mut task = TaskInfo::new("http://example.com/file", "/tmp/file"); - assert_eq!(task.state, DownloadState::Pending); - assert!(task.started_at.is_none()); - assert!(task.completed_at.is_none()); - - task.mark_started(); - assert_eq!(task.state, DownloadState::Downloading); - assert!(task.started_at.is_some()); - - task.mark_completed(); - assert_eq!(task.state, DownloadState::Completed); - assert!(task.completed_at.is_some()); - } - - #[test] - fn test_task_info_failure() { - let mut task = TaskInfo::new("http://example.com/file", "/tmp/file"); - task.mark_failed("Network error"); - - assert_eq!(task.state, DownloadState::Failed); - assert_eq!(task.error, Some("Network error".to_string())); - assert!(task.completed_at.is_some()); - } -} diff --git a/src/downloader/task_manager.rs b/src/downloader/task_manager.rs deleted file mode 100644 index 4750517..0000000 --- a/src/downloader/task_manager.rs +++ /dev/null @@ -1,1009 +0,0 @@ -//! Download task manager with state tracking and long-polling support - -use super::config::DownloadConfig; -use super::error::{DownloadError, Result}; -use super::state::{DownloadProgress, DownloadState, SpeedCalculator, TaskInfo}; -use super::traits::{Downloader, DownloaderCapabilities}; -use parking_lot::RwLock; -use std::collections::HashMap; -use std::path::PathBuf; -use std::sync::Arc; -use std::time::{Duration, SystemTime}; -use tokio::sync::Notify; -use uuid::Uuid; - -/// Download task manager -/// -/// Manages all download tasks, tracks their state, and provides long-polling support -/// for status updates. -pub struct DownloadTaskManager { - /// Task storage: task_id -> TaskInfo - tasks: Arc>>, - - /// Downloader implementation - downloader: Arc>, - - /// Notification system for state changes (used for long-polling) - notifier: Arc, - - /// Downloader capabilities (cached from downloader at initialization) - capabilities: DownloaderCapabilities, -} - -impl DownloadTaskManager { - /// Create a new task manager with the given downloader - pub fn new(downloader: Box) -> Self { - // Cache capabilities from downloader at initialization - let capabilities = downloader.capabilities().clone(); - - Self { - tasks: Arc::new(RwLock::new(HashMap::new())), - downloader: Arc::new(downloader), - notifier: Arc::new(Notify::new()), - capabilities, - } - } - - /// Create a task manager from configuration - pub fn from_config(config: &DownloadConfig) -> Self { - let downloader = super::create_downloader(config); - Self::new(downloader) - } - - /// Submit a new download task - /// - /// # Arguments - /// * `url` - The URL to download from - /// * `dest_path` - The destination file path - /// - /// # Returns - /// * `Ok(task_id)` - The unique task ID - /// * `Err(DownloadError)` - If the task could not be created - pub fn submit_task( - &self, - url: impl Into, - dest_path: impl Into, - ) -> Result { - let task_id = Uuid::new_v4().to_string(); - let task = TaskInfo::with_id(task_id.clone(), url, dest_path); - - { - let mut tasks = self.tasks.write(); - if tasks.contains_key(&task_id) { - return Err(DownloadError::task_already_exists(&task_id)); - } - tasks.insert(task_id.clone(), task); - } - - // Notify listeners about new task - self.notifier.notify_waiters(); - - // Start download in background - let manager = self.clone_for_task(); - let task_id_clone = task_id.clone(); - tokio::spawn(async move { - manager.execute_task(&task_id_clone).await; - }); - - Ok(task_id) - } - - /// Submit a new download task with optional headers, cookies, and hub_uuid - /// - /// # Arguments - /// * `url` - The URL to download from - /// * `dest_path` - The destination file path - /// * `headers` - Optional HTTP headers - /// * `cookies` - Optional HTTP cookies - /// * `hub_uuid` - Optional hub UUID for routing to external downloaders - /// - /// # Returns - /// * `Ok(task_id)` - The unique task ID - /// * `Err(DownloadError)` - If the task could not be created - pub fn submit_task_with_options( - &self, - url: impl Into, - dest_path: impl Into, - headers: Option>, - cookies: Option>, - hub_uuid: Option, - ) -> Result { - let task_id = Uuid::new_v4().to_string(); - let task = - TaskInfo::with_options(task_id.clone(), url, dest_path, headers, cookies, hub_uuid); - - { - let mut tasks = self.tasks.write(); - if tasks.contains_key(&task_id) { - return Err(DownloadError::task_already_exists(&task_id)); - } - tasks.insert(task_id.clone(), task); - } - - // Notify listeners about new task - self.notifier.notify_waiters(); - - // Start download in background - let manager = self.clone_for_task(); - let task_id_clone = task_id.clone(); - tokio::spawn(async move { - manager.execute_task(&task_id_clone).await; - }); - - Ok(task_id) - } - - /// Submit multiple download tasks - /// - /// # Returns - /// Vector of task IDs for each submitted task - pub fn submit_batch(&self, tasks: Vec<(String, String)>) -> Result> { - let mut task_ids = Vec::new(); - - for (url, dest_path) in tasks { - let task_id = self.submit_task(url, dest_path)?; - task_ids.push(task_id); - } - - Ok(task_ids) - } - - /// Get task information by ID - pub fn get_task(&self, task_id: &str) -> Result { - let tasks = self.tasks.read(); - tasks - .get(task_id) - .cloned() - .ok_or_else(|| DownloadError::task_not_found(task_id)) - } - - /// Get all tasks - pub fn get_all_tasks(&self) -> Vec { - let tasks = self.tasks.read(); - tasks.values().cloned().collect() - } - - /// Get tasks by state - pub fn get_tasks_by_state(&self, state: DownloadState) -> Vec { - let tasks = self.tasks.read(); - tasks - .values() - .filter(|task| task.state == state) - .cloned() - .collect() - } - - /// Get active tasks (pending or downloading) - pub fn get_active_tasks(&self) -> Vec { - let tasks = self.tasks.read(); - tasks - .values() - .filter(|task| task.state.is_active()) - .cloned() - .collect() - } - - /// Get downloader capabilities - /// - /// Returns the capabilities of the underlying downloader implementation. - /// These capabilities are determined at initialization time. - pub fn get_capabilities(&self) -> &DownloaderCapabilities { - &self.capabilities - } - - /// Cancel a task - pub fn cancel_task(&self, task_id: &str) -> Result<()> { - let mut tasks = self.tasks.write(); - let task = tasks - .get_mut(task_id) - .ok_or_else(|| DownloadError::task_not_found(task_id))?; - - if task.state.is_terminal() { - return Err(DownloadError::invalid_input(format!( - "Task {} is already in terminal state: {:?}", - task_id, task.state - ))); - } - - task.mark_cancelled(); - self.notifier.notify_waiters(); - - Ok(()) - } - - /// Pause a download task - /// - /// # Arguments - /// * `task_id` - The task ID to pause - /// - /// # Returns - /// * `Ok(())` - Task paused successfully - /// * `Err(DownloadError)` - If task not found or cannot be paused - pub async fn pause_task(&self, task_id: &str) -> Result<()> { - // Check if downloader supports pause - if !self.capabilities.supports_pause { - return Err(DownloadError::unsupported( - "This downloader does not support pause functionality", - )); - } - - // Get task URL and verify state - let url = { - let tasks = self.tasks.read(); - let task = tasks - .get(task_id) - .ok_or_else(|| DownloadError::task_not_found(task_id))?; - - if !task.state.is_pausable() { - return Err(DownloadError::invalid_input(format!( - "Task {} cannot be paused in state: {:?}", - task_id, task.state - ))); - } - - task.url.clone() - }; - - // Call downloader pause - self.downloader.pause(&url).await?; - - // Update task state - { - let mut tasks = self.tasks.write(); - if let Some(task) = tasks.get_mut(task_id) { - task.mark_stopped(); - } - } - - self.notifier.notify_waiters(); - Ok(()) - } - - /// Resume a paused download task - /// - /// # Arguments - /// * `task_id` - The task ID to resume - /// - /// # Returns - /// * `Ok(())` - Task resumed successfully - /// * `Err(DownloadError)` - If task not found or cannot be resumed - pub async fn resume_task(&self, task_id: &str) -> Result<()> { - // Check if downloader supports resume - if !self.capabilities.supports_resume { - return Err(DownloadError::unsupported( - "This downloader does not support resume functionality", - )); - } - - // Get task details and verify state - let (url, dest_path, headers, cookies, hub_uuid) = { - let tasks = self.tasks.read(); - let task = tasks - .get(task_id) - .ok_or_else(|| DownloadError::task_not_found(task_id))?; - - if !task.state.is_resumable() { - return Err(DownloadError::invalid_input(format!( - "Task {} cannot be resumed from state: {:?}", - task_id, task.state - ))); - } - - ( - task.url.clone(), - task.dest_path.clone(), - task.headers.clone(), - task.cookies.clone(), - task.hub_uuid.clone(), - ) - }; - - // Mark as resumed - { - let mut tasks = self.tasks.write(); - if let Some(task) = tasks.get_mut(task_id) { - task.mark_resumed(); - } - } - - self.notifier.notify_waiters(); - - // Restart download in background - let manager = self.clone_for_task(); - let task_id_clone = task_id.to_string(); - tokio::spawn(async move { - // Create request options with headers, cookies, and hub_uuid metadata - let mut metadata = std::collections::HashMap::new(); - if let Some(uuid) = &hub_uuid { - metadata.insert("hub_uuid".to_string(), uuid.clone()); - } - - let options = if headers.is_some() || cookies.is_some() || !metadata.is_empty() { - Some(super::traits::RequestOptions { - headers, - cookies, - metadata: if metadata.is_empty() { - None - } else { - Some(metadata) - }, - }) - } else { - None - }; - - // Create progress callback with speed calculator - let tasks_clone = manager.tasks.clone(); - let task_id_for_callback = task_id_clone.clone(); - let notifier_clone = manager.notifier.clone(); - let speed_calc = Arc::new(RwLock::new(SpeedCalculator::default_window())); - - let progress_callback = Box::new(move |downloaded: u64, total: Option| { - // Record sample and calculate speed - let speed = { - let mut calc = speed_calc.write(); - calc.record(downloaded); - calc.speed_bytes_per_sec() - }; - - // Update task progress - let mut tasks = tasks_clone.write(); - if let Some(task) = tasks.get_mut(&task_id_for_callback) { - task.update_progress(DownloadProgress::with_speed(downloaded, total, speed)); - notifier_clone.notify_waiters(); - } - }); - - // Resume download - let result = manager - .downloader - .download( - &url, - &PathBuf::from(&dest_path), - Some(progress_callback), - options, - ) - .await; - - // Update task state - { - let mut tasks = manager.tasks.write(); - if let Some(task) = tasks.get_mut(&task_id_clone) { - match result { - Ok(_) => task.mark_completed(), - Err(e) => task.mark_failed(e.message), - } - } - } - - manager.notifier.notify_waiters(); - }); - - Ok(()) - } - - /// Wait for task state change (long-polling support) - /// - /// # Arguments - /// * `task_id` - The task ID to monitor - /// * `timeout` - Maximum time to wait for a change - /// - /// # Returns - /// * `Ok(TaskInfo)` - The updated task info - /// * `Err(DownloadError)` - If task not found or timeout occurred - pub async fn wait_for_change(&self, task_id: &str, timeout: Duration) -> Result { - let initial_state = { - let tasks = self.tasks.read(); - let task = tasks - .get(task_id) - .ok_or_else(|| DownloadError::task_not_found(task_id))?; - task.state - }; - - // If already in terminal state, return immediately - if initial_state.is_terminal() { - return self.get_task(task_id); - } - - // Wait for notification with timeout - let notifier = self.notifier.clone(); - let result = tokio::time::timeout(timeout, async { - loop { - notifier.notified().await; - - let tasks = self.tasks.read(); - if let Some(task) = tasks.get(task_id) { - if task.state != initial_state { - return Ok(task.clone()); - } - } else { - return Err(DownloadError::task_not_found(task_id)); - } - } - }) - .await; - - match result { - Ok(task_result) => task_result, - Err(_) => { - // Timeout - return current state - self.get_task(task_id) - } - } - } - - /// Remove completed/failed tasks older than the specified duration - pub fn cleanup_old_tasks(&self, max_age: Duration) { - let mut tasks = self.tasks.write(); - let now = SystemTime::now(); - - tasks.retain(|_, task| { - if !task.state.is_terminal() { - return true; // Keep active tasks - } - - if let Some(completed_at) = task.completed_at { - if let Ok(age) = now.duration_since(completed_at) { - return age < max_age; - } - } - - true - }); - - self.notifier.notify_waiters(); - } - - /// Remove a specific task - pub fn remove_task(&self, task_id: &str) -> Result<()> { - let mut tasks = self.tasks.write(); - let task = tasks - .get(task_id) - .ok_or_else(|| DownloadError::task_not_found(task_id))?; - - if !task.state.is_terminal() { - return Err(DownloadError::invalid_input(format!( - "Cannot remove active task: {}", - task_id - ))); - } - - tasks.remove(task_id); - self.notifier.notify_waiters(); - - Ok(()) - } - - /// Execute a download task - async fn execute_task(&self, task_id: &str) { - // Mark task as started - { - let mut tasks = self.tasks.write(); - if let Some(task) = tasks.get_mut(task_id) { - if task.state == DownloadState::Cancelled { - return; // Task was cancelled before it started - } - task.mark_started(); - } else { - return; // Task not found - } - } - - self.notifier.notify_waiters(); - - // Get task details - let (url, dest_path, headers, cookies, hub_uuid) = { - let tasks = self.tasks.read(); - if let Some(task) = tasks.get(task_id) { - ( - task.url.clone(), - task.dest_path.clone(), - task.headers.clone(), - task.cookies.clone(), - task.hub_uuid.clone(), - ) - } else { - return; - } - }; - - // Create request options with headers, cookies, and hub_uuid metadata - let mut metadata = std::collections::HashMap::new(); - if let Some(uuid) = &hub_uuid { - metadata.insert("hub_uuid".to_string(), uuid.clone()); - } - - let options = if headers.is_some() || cookies.is_some() || !metadata.is_empty() { - Some(super::traits::RequestOptions { - headers, - cookies, - metadata: if metadata.is_empty() { - None - } else { - Some(metadata) - }, - }) - } else { - None - }; - - // Create progress callback with speed calculator - let tasks_clone = self.tasks.clone(); - let task_id_clone = task_id.to_string(); - let notifier_clone = self.notifier.clone(); - let speed_calc = Arc::new(RwLock::new(SpeedCalculator::default_window())); - - let progress_callback = Box::new(move |downloaded: u64, total: Option| { - // Record sample and calculate speed - let speed = { - let mut calc = speed_calc.write(); - calc.record(downloaded); - calc.speed_bytes_per_sec() - }; - - // Update task progress - let mut tasks = tasks_clone.write(); - if let Some(task) = tasks.get_mut(&task_id_clone) { - task.update_progress(DownloadProgress::with_speed(downloaded, total, speed)); - notifier_clone.notify_waiters(); - } - }); - - // Perform download - let result = self - .downloader - .download( - &url, - &PathBuf::from(&dest_path), - Some(progress_callback), - options, - ) - .await; - - // Update task state - { - let mut tasks = self.tasks.write(); - if let Some(task) = tasks.get_mut(task_id) { - match result { - Ok(_) => task.mark_completed(), - Err(e) => task.mark_failed(e.message), - } - } - } - - self.notifier.notify_waiters(); - } - - /// Clone for task execution - fn clone_for_task(&self) -> Self { - Self { - tasks: self.tasks.clone(), - downloader: self.downloader.clone(), - notifier: self.notifier.clone(), - capabilities: self.capabilities.clone(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::downloader::TraumaDownloader; - - #[test] - fn test_create_manager() { - let downloader = Box::new(TraumaDownloader::default_settings()); - let manager = DownloadTaskManager::new(downloader); - - let all_tasks = manager.get_all_tasks(); - assert!(all_tasks.is_empty()); - } - - #[tokio::test] - async fn test_submit_task() { - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - let task_id = manager - .submit_task("http://example.com/file", "/tmp/file") - .unwrap(); - assert!(!task_id.is_empty()); - - let task = manager.get_task(&task_id).unwrap(); - assert_eq!(task.url, "http://example.com/file"); - assert_eq!(task.dest_path, "/tmp/file"); - } - - #[tokio::test] - async fn test_cancel_task() { - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - let task_id = manager - .submit_task("http://example.com/file", "/tmp/file") - .unwrap(); - - // Wait a bit for task to potentially start - tokio::time::sleep(Duration::from_millis(100)).await; - - let result = manager.cancel_task(&task_id); - // May succeed or fail depending on task state - let _ = result; - } - - #[tokio::test] - async fn test_get_tasks_by_state() { - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - let _ = manager.submit_task("http://example.com/file1", "/tmp/file1"); - let _ = manager.submit_task("http://example.com/file2", "/tmp/file2"); - - let pending_tasks = manager.get_tasks_by_state(DownloadState::Pending); - assert!(pending_tasks.len() <= 2); // May have started downloading - } - - // ======================================================================== - // Integration Tests - TaskManager with Mock HTTP Server - // ======================================================================== - - /// Test Scenario 5: Headers and Cookies transmission to downloader - /// Verifies: Custom headers and cookies are correctly sent to server - #[tokio::test] - async fn test_custom_headers_and_cookies_transmission() { - use mockito::Server; - use std::collections::HashMap; - use tempfile::tempdir; - - // Create Mock HTTP server - let mut server = Server::new_async().await; - - // Mock verifies custom headers and cookies - // Note: Cookie order is not guaranteed due to HashMap iteration - let mock = server - .mock("GET", "/protected-file.txt") - .match_header("authorization", "Bearer test-token-123") - .match_header("x-custom-header", "custom-value") - .match_header( - "cookie", - mockito::Matcher::Regex(".*session_id=abc123.*".to_string()), - ) - .match_header( - "cookie", - mockito::Matcher::Regex(".*user_id=456.*".to_string()), - ) - .with_status(200) - .with_body(b"Protected content") - .create(); - - // Prepare custom headers and cookies - let mut headers = HashMap::new(); - headers.insert( - "Authorization".to_string(), - "Bearer test-token-123".to_string(), - ); - headers.insert("X-Custom-Header".to_string(), "custom-value".to_string()); - - let mut cookies = HashMap::new(); - cookies.insert("session_id".to_string(), "abc123".to_string()); - cookies.insert("user_id".to_string(), "456".to_string()); - - // Create task manager - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - // Prepare download destination - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("protected-file.txt"); - let url = format!("{}/protected-file.txt", server.url()); - - // Submit task with headers and cookies - let task_id = manager - .submit_task_with_options( - &url, - dest.to_str().unwrap(), - Some(headers), - Some(cookies), - None, - ) - .unwrap(); - - // Wait for download to complete - tokio::time::sleep(Duration::from_millis(500)).await; - - // Verify task status - let task_info = manager.get_task(&task_id).unwrap(); - assert!( - task_info.state == DownloadState::Completed - || task_info.state == DownloadState::Downloading, - "Task should be completed or downloading" - ); - - // Wait for task to fully complete - for _ in 0..20 { - tokio::time::sleep(Duration::from_millis(100)).await; - let task_info = manager.get_task(&task_id).unwrap(); - if task_info.state == DownloadState::Completed { - break; - } - } - - // Verify mock was called (headers and cookies transmitted correctly) - mock.assert(); - - // Verify file downloaded successfully - assert!(dest.exists(), "File should be downloaded"); - let content = std::fs::read_to_string(&dest).unwrap(); - assert_eq!(content, "Protected content"); - } - - /// Test Scenario 6: Task lifecycle state tracking - /// Verifies: Complete flow from Pending → Downloading → Completed - #[tokio::test] - async fn test_task_lifecycle_and_state_transitions() { - use mockito::Server; - use tempfile::tempdir; - - // Create Mock HTTP server - let mut server = Server::new_async().await; - let test_data = b"Task lifecycle test data"; - - let mock = server - .mock("GET", "/lifecycle-test.txt") - .with_status(200) - .with_header("content-length", &test_data.len().to_string()) - .with_body(test_data.as_slice()) - .create(); - - // Create task manager - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - // Prepare download destination - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("lifecycle-test.txt"); - let url = format!("{}/lifecycle-test.txt", server.url()); - - // Submit task - let task_id = manager.submit_task(&url, dest.to_str().unwrap()).unwrap(); - - // Track state changes - let mut states_observed = Vec::new(); - - // Initial state should be Pending - let initial_task = manager.get_task(&task_id).unwrap(); - states_observed.push(initial_task.state); - assert_eq!( - initial_task.state, - DownloadState::Pending, - "Initial state should be Pending" - ); - - // Monitor state transitions (max 3 seconds) - for _ in 0..30 { - tokio::time::sleep(Duration::from_millis(100)).await; - - if let Ok(task_info) = manager.get_task(&task_id) { - let current_state = task_info.state; - - // Record new state - if states_observed.last() != Some(¤t_state) { - states_observed.push(current_state); - } - - // If completed, verify file exists - if current_state == DownloadState::Completed { - assert!(dest.exists(), "File should exist when completed"); - let content = std::fs::read(&dest).unwrap(); - assert_eq!(content, test_data); - break; - } - - // If failed, record error - if current_state == DownloadState::Failed { - if let Some(error) = &task_info.error { - eprintln!("Download failed: {}", error); - } - panic!("Download should not fail in this test"); - } - } - } - - // Verify state transition sequence - assert!( - states_observed.contains(&DownloadState::Pending), - "Should have observed Pending state" - ); - assert!( - states_observed.contains(&DownloadState::Downloading) - || states_observed.contains(&DownloadState::Completed), - "Should have observed Downloading or Completed state" - ); - - // Final state should be Completed - let final_task = manager.get_task(&task_id).unwrap(); - assert_eq!( - final_task.state, - DownloadState::Completed, - "Final state should be Completed. States observed: {:?}", - states_observed - ); - - // Verify progress information - assert_eq!( - final_task.progress.downloaded_bytes, - test_data.len() as u64, - "Downloaded bytes should match content length" - ); - assert_eq!( - final_task.progress.total_bytes, - Some(test_data.len() as u64), - "Total bytes should be known" - ); - - mock.assert(); - } - - /// Test Scenario 7: Long-polling wait_for_change mechanism - /// Verifies: Correct notification on state change, timeout mechanism works - #[tokio::test] - async fn test_wait_for_change_notification() { - use mockito::Server; - use tempfile::tempdir; - - // Create Mock HTTP server with slow response - let mut server = Server::new_async().await; - let test_data = vec![b'W'; 2048]; // 2KB data - - let mock = server - .mock("GET", "/slow-file.bin") - .with_status(200) - .with_header("content-length", &test_data.len().to_string()) - .with_chunked_body(move |w| { - // Send chunks slowly - for chunk in test_data.chunks(512) { - std::thread::sleep(Duration::from_millis(200)); - w.write_all(chunk)?; - } - Ok(()) - }) - .create(); - - // Create task manager - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - // Prepare download destination - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("slow-file.bin"); - let url = format!("{}/slow-file.bin", server.url()); - - // Submit task - let task_id = manager.submit_task(&url, dest.to_str().unwrap()).unwrap(); - - // Test 1: Wait for state change (should succeed) - let wait_result = manager - .wait_for_change(&task_id, Duration::from_secs(2)) - .await; - assert!(wait_result.is_ok(), "Wait for change should succeed"); - - let task_after_change = wait_result.unwrap(); - assert!( - task_after_change.state == DownloadState::Downloading - || task_after_change.state == DownloadState::Completed, - "State should have changed from Pending" - ); - - // Test 2: Short timeout test (should timeout when state is stable) - // Wait for task to complete or enter stable state - tokio::time::sleep(Duration::from_secs(1)).await; - - // Test 3: Verify task eventually completes - for _ in 0..15 { - tokio::time::sleep(Duration::from_millis(300)).await; - let task_info = manager.get_task(&task_id).unwrap(); - - if task_info.state == DownloadState::Completed { - // Verify download success - assert!(dest.exists(), "File should exist"); - let file_size = std::fs::metadata(&dest).unwrap().len(); - assert_eq!(file_size, 2048, "File size should match"); - break; - } - } - - mock.assert(); - } - - /// Test Scenario 8: Batch task submission and management - /// Verifies: Batch submission, deduplication, concurrency control - #[tokio::test] - async fn test_batch_task_submission_and_management() { - use mockito::Server; - use tempfile::tempdir; - - // Create Mock HTTP server - let mut server = Server::new_async().await; - - // Create 3 different files - let files = vec![ - ("batch1.txt", b"Batch file 1".to_vec()), - ("batch2.txt", b"Batch file 2".to_vec()), - ("batch3.txt", b"Batch file 3".to_vec()), - ]; - - let mut mocks = Vec::new(); - for (filename, content) in &files { - let mock = server - .mock("GET", format!("/{}", filename).as_str()) - .with_status(200) - .with_header("content-length", &content.len().to_string()) - .with_body(content.as_slice()) - .create(); - mocks.push(mock); - } - - // Create task manager - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - // Prepare batch tasks - let temp_dir = tempdir().unwrap(); - let mut tasks = Vec::new(); - - for (filename, _) in &files { - let url = format!("{}/{}", server.url(), filename); - let dest = temp_dir.path().join(filename); - tasks.push((url, dest.to_string_lossy().to_string())); - } - - // Submit batch tasks - let task_ids = manager.submit_batch(tasks).unwrap(); - assert_eq!(task_ids.len(), 3, "Should submit 3 tasks"); - - // Wait for all tasks to complete - for task_id in &task_ids { - for _ in 0..30 { - tokio::time::sleep(Duration::from_millis(100)).await; - - if let Ok(task_info) = manager.get_task(task_id) { - if task_info.state == DownloadState::Completed { - break; - } - if task_info.state == DownloadState::Failed { - panic!("Task {} failed: {:?}", task_id, task_info.error); - } - } - } - } - - // Verify all files downloaded successfully - for (filename, expected_content) in &files { - let dest = temp_dir.path().join(filename); - assert!(dest.exists(), "File {} should exist", filename); - - let content = std::fs::read(&dest).unwrap(); - assert_eq!( - content, *expected_content, - "Content of {} should match", - filename - ); - } - - // Verify all mocks were called - for mock in mocks { - mock.assert(); - } - - // Verify get_all_tasks returns all tasks - let all_tasks = manager.get_all_tasks(); - assert!(all_tasks.len() >= 3, "Should have at least 3 tasks"); - - // Verify get_tasks_by_state - let completed_tasks = manager.get_tasks_by_state(DownloadState::Completed); - assert!( - completed_tasks.len() >= 3, - "Should have at least 3 completed tasks" - ); - } -} diff --git a/src/downloader/traits.rs b/src/downloader/traits.rs deleted file mode 100644 index ba8cdde..0000000 --- a/src/downloader/traits.rs +++ /dev/null @@ -1,262 +0,0 @@ -//! Core trait definitions for the downloader system - -use super::error::Result; -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::path::Path; - -/// Progress callback function type -/// Parameters: (downloaded_bytes, total_bytes_optional) -pub type ProgressCallback = Box) + Send + Sync>; - -/// HTTP request options for downloads -#[derive(Debug, Clone, Default)] -pub struct RequestOptions { - /// HTTP headers to include in the request - pub headers: Option>, - /// HTTP cookies to include in the request - pub cookies: Option>, - /// Extra metadata for dispatch (e.g., hub_uuid for routing to external downloaders) - pub metadata: Option>, -} - -/// Downloader capability information -/// -/// This struct defines what features a downloader implementation supports. -/// These capabilities are determined at initialization time and remain constant -/// throughout the downloader's lifetime. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] -pub struct DownloaderCapabilities { - /// Whether the downloader supports pausing downloads - pub supports_pause: bool, - /// Whether the downloader supports resuming paused downloads - pub supports_resume: bool, - /// Whether the downloader supports cancelling downloads - pub supports_cancellation: bool, - /// Whether the downloader supports HTTP Range requests for breakpoint resume - pub supports_range_requests: bool, - /// Whether the downloader supports batch download operations - pub supports_batch_download: bool, -} - -impl DownloaderCapabilities { - /// Create a new DownloaderCapabilities with all features enabled - pub fn all_enabled() -> Self { - Self { - supports_pause: true, - supports_resume: true, - supports_cancellation: true, - supports_range_requests: true, - supports_batch_download: true, - } - } - - /// Create a new DownloaderCapabilities with all features disabled - pub fn all_disabled() -> Self { - Self::default() - } -} - -/// Core downloader trait - all downloader implementations must implement this -/// -/// This trait provides a pluggable interface for different download backends, -/// allowing easy switching between implementations (trauma, reqwest, CLI tools, etc.) -#[async_trait] -pub trait Downloader: Send + Sync { - /// Download a single file from URL to destination path - /// - /// # Arguments - /// * `url` - The URL to download from - /// * `dest` - The destination file path - /// * `progress` - Optional progress callback (downloaded_bytes, total_bytes) - /// * `options` - Optional request options (headers, cookies) - /// - /// # Returns - /// * `Ok(())` - Download completed successfully - /// * `Err(DownloadError)` - Download failed - async fn download( - &self, - url: &str, - dest: &Path, - progress: Option, - options: Option, - ) -> Result<()>; - - /// Download multiple files concurrently - /// - /// # Arguments - /// * `tasks` - Vector of (url, destination_path) tuples - /// - /// # Returns - /// * `Ok(Vec>)` - Vector of results for each download task - async fn download_batch(&self, tasks: Vec<(String, std::path::PathBuf)>) -> Vec>; - - /// Get the name of this downloader implementation - fn name(&self) -> &str; - - /// Get the capabilities of this downloader implementation - /// - /// This method returns a reference to the capability information that was - /// determined at initialization time. Capabilities define what features - /// the downloader supports (pause, resume, cancellation, etc.). - /// - /// # Returns - /// A reference to the DownloaderCapabilities struct - fn capabilities(&self) -> &DownloaderCapabilities; - - /// Cancel an ongoing download (if supported by the implementation) - /// - /// Default implementation does nothing and returns Ok - async fn cancel(&self, _url: &str) -> Result<()> { - Ok(()) - } - - /// Pause an ongoing download (if supported by the implementation) - /// - /// # Arguments - /// * `url` - The URL of the download to pause - /// - /// # Returns - /// * `Ok(())` - Download paused successfully - /// * `Err(DownloadError)` - Failed to pause download - /// - /// Default implementation returns an error - async fn pause(&self, _url: &str) -> Result<()> { - Err(super::error::DownloadError::unsupported( - "Pause not supported by this downloader", - )) - } - - /// Resume a paused download (if supported by the implementation) - /// - /// # Arguments - /// * `url` - The URL of the download to resume - /// - /// # Returns - /// * `Ok(())` - Download resumed successfully - /// * `Err(DownloadError)` - Failed to resume download - /// - /// Default implementation returns an error - async fn resume(&self, _url: &str) -> Result<()> { - Err(super::error::DownloadError::unsupported( - "Resume not supported by this downloader", - )) - } - - /// Check if this downloader supports cancellation - /// - /// # Deprecated - /// Use `capabilities().supports_cancellation` instead. - /// This method will be removed in a future version. - #[deprecated( - since = "0.2.0", - note = "Use capabilities().supports_cancellation instead" - )] - fn supports_cancellation(&self) -> bool { - self.capabilities().supports_cancellation - } - - /// Check if this downloader supports pause/resume - /// - /// # Deprecated - /// Use `capabilities().supports_pause` instead. - /// This method will be removed in a future version. - #[deprecated(since = "0.2.0", note = "Use capabilities().supports_pause instead")] - fn supports_pause(&self) -> bool { - self.capabilities().supports_pause - } - - /// Check if this downloader supports resume/partial downloads - /// - /// # Deprecated - /// Use `capabilities().supports_resume` instead. - /// This method will be removed in a future version. - #[deprecated(since = "0.2.0", note = "Use capabilities().supports_resume instead")] - fn supports_resume(&self) -> bool { - self.capabilities().supports_resume - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::path::PathBuf; - - // Mock downloader for testing - struct MockDownloader { - capabilities: DownloaderCapabilities, - } - - impl MockDownloader { - fn new() -> Self { - Self { - capabilities: DownloaderCapabilities::all_disabled(), - } - } - } - - #[async_trait] - impl Downloader for MockDownloader { - async fn download( - &self, - _url: &str, - _dest: &Path, - _progress: Option, - _options: Option, - ) -> Result<()> { - Ok(()) - } - - async fn download_batch(&self, tasks: Vec<(String, PathBuf)>) -> Vec> { - tasks.into_iter().map(|_| Ok(())).collect() - } - - fn name(&self) -> &str { - "mock" - } - - fn capabilities(&self) -> &DownloaderCapabilities { - &self.capabilities - } - } - - #[tokio::test] - async fn test_mock_downloader() { - let downloader = MockDownloader::new(); - assert_eq!(downloader.name(), "mock"); - - // Test new capabilities() method - let caps = downloader.capabilities(); - assert!(!caps.supports_cancellation); - assert!(!caps.supports_pause); - assert!(!caps.supports_resume); - assert!(!caps.supports_range_requests); - assert!(!caps.supports_batch_download); - - // Test deprecated methods (should still work) - #[allow(deprecated)] - { - assert!(!downloader.supports_cancellation()); - assert!(!downloader.supports_pause()); - assert!(!downloader.supports_resume()); - } - - let result = downloader - .download( - "http://example.com/file", - Path::new("/tmp/file"), - None, - None, - ) - .await; - assert!(result.is_ok()); - - // Test default pause/resume implementations - let pause_result = downloader.pause("http://example.com/file").await; - assert!(pause_result.is_err()); - - let resume_result = downloader.resume("http://example.com/file").await; - assert!(resume_result.is_err()); - } -} diff --git a/src/downloader/trauma_impl.rs b/src/downloader/trauma_impl.rs deleted file mode 100644 index c350617..0000000 --- a/src/downloader/trauma_impl.rs +++ /dev/null @@ -1,751 +0,0 @@ -//! Reqwest-based downloader implementation with pause/resume support - -use super::error::{DownloadError, Result}; -use super::traits::{Downloader, DownloaderCapabilities, ProgressCallback, RequestOptions}; -use async_trait::async_trait; -use futures::StreamExt; -use parking_lot::RwLock; -use std::collections::HashMap; -use std::path::{Path, PathBuf}; -use std::sync::Arc; -use tokio::fs::{File, OpenOptions}; -use tokio::io::AsyncWriteExt; -use tokio_util::sync::CancellationToken; - -/// Task state for managing pause/resume -#[derive(Clone)] -struct TaskState { - url: String, - dest: PathBuf, - cancel_token: CancellationToken, - supports_range: Option, -} - -/// Reqwest-based downloader implementation -/// -/// This implementation uses reqwest with rustls for secure, concurrent downloads -/// and supports pause/resume with HTTP Range requests -pub struct TraumaDownloader { - max_concurrent: usize, - retries: usize, - timeout_seconds: u64, - client: Arc, - // Track active download tasks for pause/resume - tasks: Arc>>, - // Capabilities of this downloader - capabilities: DownloaderCapabilities, -} - -impl TraumaDownloader { - /// Create a new TraumaDownloader with specified parameters - /// - /// # Arguments - /// * `max_concurrent` - Maximum number of concurrent downloads - /// * `retries` - Number of retry attempts for failed downloads - /// * `timeout_seconds` - Timeout for each download in seconds - pub fn new(max_concurrent: usize, retries: usize, timeout_seconds: u64) -> Self { - let client = reqwest::Client::builder() - .timeout(std::time::Duration::from_secs(timeout_seconds)) - .build() - .expect("Failed to create HTTP client"); - - Self { - max_concurrent, - retries, - timeout_seconds, - client: Arc::new(client), - tasks: Arc::new(RwLock::new(HashMap::new())), - capabilities: DownloaderCapabilities::all_enabled(), - } - } - - /// Create a downloader with default settings - pub fn default_settings() -> Self { - Self::new(4, 3, 300) - } - - /// Test if a server supports HTTP Range requests - async fn test_range_support(client: &reqwest::Client, url: &str) -> bool { - // Try HEAD request first - if let Ok(response) = client.head(url).send().await { - if let Some(accept_ranges) = response.headers().get("accept-ranges") { - if let Ok(value) = accept_ranges.to_str() { - return value.to_lowercase() == "bytes"; - } - } - } - - // Fallback: try a fake range request - if let Ok(response) = client.get(url).header("Range", "bytes=0-0").send().await { - return response.status() == reqwest::StatusCode::PARTIAL_CONTENT - || response.status() == reqwest::StatusCode::OK; - } - - false - } - - /// Download a file with retry and pause support - async fn download_with_retry( - &self, - url: &str, - dest: &Path, - progress: Option<&ProgressCallback>, - cancel_token: &CancellationToken, - options: Option<&RequestOptions>, - ) -> Result<()> { - let mut last_error = None; - - for attempt in 0..=self.retries { - if cancel_token.is_cancelled() { - return Err(DownloadError::cancelled("Download was paused")); - } - - // For retries, we don't pass progress callback to avoid multiple progress reports - let current_progress = if attempt == 0 { progress } else { None }; - - match self - .download_once(url, dest, current_progress, cancel_token, options) - .await - { - Ok(()) => return Ok(()), - Err(e) => { - if cancel_token.is_cancelled() { - return Err(DownloadError::cancelled("Download was paused")); - } - last_error = Some(e); - if attempt < self.retries { - tokio::time::sleep(std::time::Duration::from_secs(1 << attempt)).await; - } - } - } - } - - Err(last_error.unwrap_or_else(|| DownloadError::network("Download failed".to_string()))) - } - - /// Download a file once (no retry), with pause support - async fn download_once( - &self, - url: &str, - dest: &Path, - progress: Option<&ProgressCallback>, - cancel_token: &CancellationToken, - options: Option<&RequestOptions>, - ) -> Result<()> { - // Check if temp file exists (resume case) - let temp_dest = dest.with_extension("tmp"); - let existing_size = if temp_dest.exists() { - tokio::fs::metadata(&temp_dest) - .await - .ok() - .map(|m| m.len()) - .unwrap_or(0) - } else { - 0 - }; - - // Test range support if not already known - let supports_range = if existing_size > 0 { - Self::test_range_support(&self.client, url).await - } else { - false - }; - - // Build request with Range header if resuming - let mut request = self.client.get(url); - if existing_size > 0 && supports_range { - request = request.header("Range", format!("bytes={}-", existing_size)); - } - - // Apply custom headers if provided - if let Some(opts) = options { - if let Some(headers) = &opts.headers { - for (key, value) in headers { - request = request.header(key, value); - } - } - // Apply cookies if provided - if let Some(cookies) = &opts.cookies { - let cookie_string = cookies - .iter() - .map(|(k, v)| format!("{}={}", k, v)) - .collect::>() - .join("; "); - if !cookie_string.is_empty() { - request = request.header("Cookie", cookie_string); - } - } - } - - // Send request - let response = request - .send() - .await - .map_err(|e| DownloadError::network(format!("Failed to send request: {}", e)))?; - - // Check status - let status = response.status(); - if !status.is_success() && status != reqwest::StatusCode::PARTIAL_CONTENT { - return Err(DownloadError::network(format!( - "HTTP error {}: {}", - status, url - ))); - } - - // Get content length - let content_length = response.content_length(); - let total_size = if status == reqwest::StatusCode::PARTIAL_CONTENT { - // For partial content, add existing size to content length - content_length.map(|len| len + existing_size) - } else { - content_length - }; - - // Open file (append mode if resuming, create mode otherwise) - let mut file = if existing_size > 0 && status == reqwest::StatusCode::PARTIAL_CONTENT { - OpenOptions::new() - .append(true) - .open(&temp_dest) - .await - .map_err(|e| DownloadError::file_system(format!("Failed to open file: {}", e)))? - } else { - File::create(&temp_dest) - .await - .map_err(|e| DownloadError::file_system(format!("Failed to create file: {}", e)))? - }; - - // Stream download with progress tracking - let mut stream = response.bytes_stream(); - let mut downloaded: u64 = existing_size; - - while let Some(chunk_result) = stream.next().await { - // Check for pause signal - if cancel_token.is_cancelled() { - // Flush and close file before pausing - file.flush().await.ok(); - drop(file); - return Err(DownloadError::cancelled("Download paused")); - } - - let chunk = chunk_result - .map_err(|e| DownloadError::network(format!("Failed to read chunk: {}", e)))?; - - file.write_all(&chunk) - .await - .map_err(|e| DownloadError::file_system(format!("Failed to write chunk: {}", e)))?; - - downloaded += chunk.len() as u64; - - // Call progress callback - if let Some(callback) = progress { - callback(downloaded, total_size); - } - } - - // Flush and close file - file.flush() - .await - .map_err(|e| DownloadError::file_system(format!("Failed to flush file: {}", e)))?; - drop(file); - - // Rename temp file to final destination - tokio::fs::rename(&temp_dest, dest).await.map_err(|e| { - DownloadError::file_system(format!("Failed to rename downloaded file: {}", e)) - })?; - - Ok(()) - } - - /// Register a download task - fn register_task(&self, url: String, dest: PathBuf) -> CancellationToken { - let cancel_token = CancellationToken::new(); - let state = TaskState { - url: url.clone(), - dest, - cancel_token: cancel_token.clone(), - supports_range: None, - }; - self.tasks.write().insert(url, state); - cancel_token - } - - /// Unregister a download task - fn unregister_task(&self, url: &str) { - self.tasks.write().remove(url); - } - - /// Get task cancel token - fn get_cancel_token(&self, url: &str) -> Option { - self.tasks - .read() - .get(url) - .map(|state| state.cancel_token.clone()) - } -} - -#[async_trait] -impl Downloader for TraumaDownloader { - async fn download( - &self, - url: &str, - dest: &Path, - progress: Option, - options: Option, - ) -> Result<()> { - // Validate inputs - if url.is_empty() { - return Err(DownloadError::invalid_input("URL cannot be empty")); - } - - // Ensure parent directory exists - if let Some(parent) = dest.parent() { - tokio::fs::create_dir_all(parent).await.map_err(|e| { - DownloadError::file_system(format!("Failed to create directory: {}", e)) - })?; - } - - // Register task - let cancel_token = self.register_task(url.to_string(), dest.to_path_buf()); - - // Start download - let result = self - .download_with_retry( - url, - dest, - progress.as_ref(), - &cancel_token, - options.as_ref(), - ) - .await; - - // Unregister task - self.unregister_task(url); - - result - } - - async fn download_batch(&self, tasks: Vec<(String, PathBuf)>) -> Vec> { - if tasks.is_empty() { - return vec![]; - } - - // Use semaphore to limit concurrent downloads - let semaphore = Arc::new(tokio::sync::Semaphore::new(self.max_concurrent)); - let mut handles = vec![]; - - for (url, dest) in tasks { - let sem = semaphore.clone(); - let cancel_token = self.register_task(url.clone(), dest.clone()); - let downloader = self.clone_for_task(); - let url_for_handle = url.clone(); - - let handle = tokio::spawn(async move { - let _permit = sem.acquire().await.unwrap(); - - // Ensure parent directory exists - if let Some(parent) = dest.parent() { - if let Err(e) = tokio::fs::create_dir_all(parent).await { - return Err(DownloadError::file_system(format!( - "Failed to create directory: {}", - e - ))); - } - } - - // Download with retry - let result = downloader - .download_with_retry(&url, &dest, None, &cancel_token, None) - .await; - - result - }); - - handles.push((url_for_handle, handle)); - } - - // Wait for all downloads to complete - let mut results = vec![]; - for (url, handle) in handles { - match handle.await { - Ok(result) => { - self.unregister_task(&url); - results.push(result); - } - Err(e) => { - self.unregister_task(&url); - results.push(Err(DownloadError::network(format!( - "Task join error: {}", - e - )))); - } - } - } - - results - } - - fn name(&self) -> &str { - "reqwest" - } - - fn capabilities(&self) -> &DownloaderCapabilities { - &self.capabilities - } - - async fn pause(&self, url: &str) -> Result<()> { - if let Some(cancel_token) = self.get_cancel_token(url) { - cancel_token.cancel(); - Ok(()) - } else { - Err(DownloadError::task_not_found(url)) - } - } - - async fn resume(&self, url: &str) -> Result<()> { - // Get task state - let state = self.tasks.read().get(url).cloned(); - - if let Some(task_state) = state { - // Create new cancel token for resumed download - let new_cancel_token = - self.register_task(task_state.url.clone(), task_state.dest.clone()); - - // Start download from where it left off - let result = self - .download_with_retry( - &task_state.url, - &task_state.dest, - None, - &new_cancel_token, - None, - ) - .await; - - if result.is_err() { - self.unregister_task(url); - } - - result - } else { - Err(DownloadError::task_not_found(url)) - } - } -} - -impl TraumaDownloader { - /// Clone essential fields for spawned tasks - fn clone_for_task(&self) -> Self { - Self { - max_concurrent: self.max_concurrent, - retries: self.retries, - timeout_seconds: self.timeout_seconds, - client: self.client.clone(), - tasks: self.tasks.clone(), - capabilities: self.capabilities.clone(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use tempfile::tempdir; - - #[test] - fn test_create_trauma_downloader() { - let downloader = TraumaDownloader::new(8, 5, 600); - assert_eq!(downloader.name(), "reqwest"); - assert_eq!(downloader.max_concurrent, 8); - assert_eq!(downloader.retries, 5); - - // Test capabilities - let caps = downloader.capabilities(); - assert!(caps.supports_pause); - assert!(caps.supports_resume); - assert!(caps.supports_cancellation); - } - - #[tokio::test] - async fn test_download_invalid_url() { - let downloader = TraumaDownloader::default_settings(); - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("test.txt"); - - let result = downloader.download("", &dest, None, None).await; - assert!(result.is_err()); - - if let Err(e) = result { - assert_eq!(e.kind, super::super::error::ErrorKind::InvalidInput); - } - } - - #[tokio::test] - async fn test_download_batch_empty() { - let downloader = TraumaDownloader::default_settings(); - let results = downloader.download_batch(vec![]).await; - assert!(results.is_empty()); - } - - #[tokio::test] - async fn test_pause_nonexistent_task() { - let downloader = TraumaDownloader::default_settings(); - let result = downloader.pause("http://nonexistent.com/file").await; - assert!(result.is_err()); - } - - // ======================================================================== - // Integration Tests with Mock HTTP Server - // ======================================================================== - - /// Test Scenario 1: Complete file download flow - /// Verifies: File downloaded correctly, content matches, temp file cleanup - #[tokio::test] - async fn test_download_complete_file() { - use mockito::Server; - - // Create Mock HTTP server - let mut server = Server::new_async().await; - let test_data = b"Hello, this is test data for download!"; - - let mock = server - .mock("GET", "/test-file.txt") - .with_status(200) - .with_header("content-length", &test_data.len().to_string()) - .with_body(test_data.as_slice()) - .create(); - - // Prepare download destination - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("downloaded.txt"); - - // Execute download - let downloader = TraumaDownloader::default_settings(); - let url = format!("{}/test-file.txt", server.url()); - let result = downloader.download(&url, &dest, None, None).await; - - // Verify results - assert!(result.is_ok(), "Download should succeed"); - assert!(dest.exists(), "Downloaded file should exist"); - - let downloaded_content = std::fs::read(&dest).unwrap(); - assert_eq!(downloaded_content, test_data, "Content should match"); - - // Verify temporary file was cleaned up - let temp_file = dest.with_extension("tmp"); - assert!(!temp_file.exists(), "Temporary file should be cleaned up"); - - mock.assert(); - } - - /// Test Scenario 2: Concurrent downloads of multiple files - /// Verifies: Parallel execution, concurrency control, all files downloaded - #[tokio::test] - async fn test_concurrent_downloads() { - use mockito::Server; - - // Create Mock HTTP server - let mut server = Server::new_async().await; - - // Prepare 5 different test files - let test_files = vec![ - ("file1.txt", b"Content of file 1".to_vec()), - ("file2.txt", b"Content of file 2".to_vec()), - ("file3.txt", b"Content of file 3".to_vec()), - ("file4.txt", b"Content of file 4".to_vec()), - ("file5.txt", b"Content of file 5".to_vec()), - ]; - - // Create mock for each file - let mut mocks = Vec::new(); - for (filename, content) in &test_files { - let mock = server - .mock("GET", format!("/{}", filename).as_str()) - .with_status(200) - .with_header("content-length", &content.len().to_string()) - .with_body(content.as_slice()) - .create(); - mocks.push(mock); - } - - // Prepare download tasks - let temp_dir = tempdir().unwrap(); - let mut tasks = Vec::new(); - - for (filename, _) in &test_files { - let url = format!("{}/{}", server.url(), filename); - let dest = temp_dir.path().join(filename); - tasks.push((url, dest)); - } - - // Execute concurrent downloads (max 3 concurrent) - let downloader = TraumaDownloader::new(3, 2, 30); - let results = downloader.download_batch(tasks.clone()).await; - - // Verify all downloads succeeded - assert_eq!(results.len(), 5, "Should have 5 results"); - for (i, result) in results.iter().enumerate() { - assert!(result.is_ok(), "Download {} should succeed", i); - } - - // Verify file contents - for (i, (filename, expected_content)) in test_files.iter().enumerate() { - let dest = temp_dir.path().join(filename); - assert!(dest.exists(), "File {} should exist", filename); - - let content = std::fs::read(&dest).unwrap(); - assert_eq!( - content, *expected_content, - "Content of file {} should match", - i - ); - } - - // Verify all mocks were called - for mock in mocks { - mock.assert(); - } - } - - /// Test Scenario 3: Pause and resume during download - /// Verifies: Pause mechanism, temp file retention, successful continuation - #[tokio::test] - async fn test_pause_and_resume_download() { - use mockito::Server; - use std::time::Duration; - - // Create Mock HTTP server - simulate large file with chunked response - let mut server = Server::new_async().await; - let test_data = vec![b'X'; 10 * 1024]; // 10KB data - - let mock = server - .mock("GET", "/large-file.bin") - .with_status(200) - .with_header("content-length", &test_data.len().to_string()) - .with_chunked_body(move |w| { - // Simulate slow download to allow time for pause - for chunk in test_data.chunks(1024) { - std::thread::sleep(Duration::from_millis(50)); - w.write_all(chunk)?; - } - Ok(()) - }) - .create(); - - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("large-file.bin"); - let url = format!("{}/large-file.bin", server.url()); - - let downloader = TraumaDownloader::default_settings(); - - // Start download and pause quickly - let url_clone = url.clone(); - let dest_clone = dest.clone(); - let downloader_clone = downloader.clone_for_task(); - - let download_handle = tokio::spawn(async move { - downloader_clone - .download(&url_clone, &dest_clone, None, None) - .await - }); - - // Wait for download to start - tokio::time::sleep(Duration::from_millis(100)).await; - - // Pause download - let pause_result = downloader.pause(&url).await; - assert!(pause_result.is_ok(), "Pause should succeed"); - - // Wait for download task to respond to pause signal - let download_result = download_handle.await.unwrap(); - assert!(download_result.is_err(), "Download should be cancelled"); - - // Verify temporary file exists (partially downloaded data) - let temp_file = dest.with_extension("tmp"); - assert!( - temp_file.exists(), - "Temporary file should exist after pause" - ); - - mock.assert(); - } - - /// Test Scenario 4: Resume from breakpoint - /// Verifies: Range request, resume from breakpoint, complete file download - #[tokio::test] - async fn test_resume_from_breakpoint() { - use mockito::Server; - - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("resume-test.bin"); - let temp_file = dest.with_extension("tmp"); - - // Prepare complete test data - let full_data = vec![b'A'; 5000]; // 5KB - let partial_size = 2000; // First 2KB already downloaded - - // Simulate partially downloaded data - std::fs::write(&temp_file, &full_data[..partial_size]).unwrap(); - assert_eq!( - std::fs::metadata(&temp_file).unwrap().len(), - partial_size as u64, - "Partial file should exist" - ); - - // Create Mock HTTP server with Range support - let mut server = Server::new_async().await; - - // First Mock: HEAD request to detect Range support - let _head_mock = server - .mock("HEAD", "/resume-file.bin") - .with_status(200) - .with_header("accept-ranges", "bytes") - .with_header("content-length", &full_data.len().to_string()) - .create(); - - // Second Mock: GET request with Range support - let range_mock = server - .mock("GET", "/resume-file.bin") - .match_header("range", format!("bytes={}-", partial_size).as_str()) - .with_status(206) // Partial Content - .with_header( - "content-range", - format!( - "bytes {}-{}/{}", - partial_size, - full_data.len() - 1, - full_data.len() - ) - .as_str(), - ) - .with_header( - "content-length", - &(full_data.len() - partial_size).to_string(), - ) - .with_body(&full_data[partial_size..]) - .create(); - - // Execute resume download - let downloader = TraumaDownloader::default_settings(); - let url = format!("{}/resume-file.bin", server.url()); - let result = downloader.download(&url, &dest, None, None).await; - - // Verify download succeeded - assert!(result.is_ok(), "Resume download should succeed"); - assert!(dest.exists(), "Final file should exist"); - assert!(!temp_file.exists(), "Temporary file should be removed"); - - // Verify complete file content - let downloaded_data = std::fs::read(&dest).unwrap(); - assert_eq!( - downloaded_data.len(), - full_data.len(), - "File size should match" - ); - assert_eq!( - downloaded_data, full_data, - "Content should match completely" - ); - - range_mock.assert(); - } -} diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 313146e..0000000 --- a/src/error.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::result; - -#[derive(Debug)] -pub enum Error { - Io(std::io::Error), - Other(String), - Getter(GetterError), -} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::Io(e) => write!(f, "IO error: {e}"), - Error::Other(msg) => write!(f, "{msg}"), - Error::Getter(e) => write!(f, "{e}"), - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::Io(e) => Some(e), - Error::Getter(e) => e.source(), - Error::Other(_) => None, - } - } -} - -impl From for Error { - fn from(e: std::io::Error) -> Self { - Error::Io(e) - } -} - -impl From for Error { - fn from(e: GetterError) -> Self { - Error::Getter(e) - } -} - -pub type Result = result::Result; - -#[derive(Debug)] -pub struct GetterError { - pub tag: String, - pub message: String, - pub err: Option>, -} - -impl GetterError { - pub fn new_nobase(tag: &str, message: &str) -> Self { - Self { - tag: tag.to_string(), - message: message.to_string(), - err: None, - } - } - - pub fn new(tag: &str, message: &str, err: Box) -> Self { - Self { - tag: tag.to_string(), - message: message.to_string(), - err: Some(err), - } - } -} - -impl std::fmt::Display for GetterError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}: {}", self.tag, self.message) - } -} - -impl std::error::Error for GetterError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match &self.err { - Some(err) => Some(err.as_ref()), - None => None, - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 4c1491e..4ffc0b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,64 @@ -mod cache; -mod core; -pub mod database; -pub mod downloader; -mod error; -mod locale; -pub mod manager; -pub mod rpc; -mod utils; -mod websdk; - -// rustls-platform-verifier -#[cfg(feature = "rustls-platform-verifier-android")] +//! Root getter library facade for the UpgradeAll rewrite. +//! +//! The root crate is intentionally small during the rewrite. Product/domain +//! behavior lives in split crates such as `getter-core` and `getter-storage`; +//! Android/Flutter hosts embed this crate through a stable facade. + +pub use getter_core as core; +pub use getter_storage as storage; + +#[cfg(feature = "rustls-platform-verifier")] pub use rustls_platform_verifier; + +pub mod rpc { + pub mod server { + use std::future::pending; + + /// Error returned by the temporary in-process RPC server facade. + #[derive(Debug, thiserror::Error)] + pub enum RpcServerError { + #[error("failed to bind RPC server at {address}: {source}")] + Bind { + address: String, + #[source] + source: std::io::Error, + }, + #[error("failed to read local RPC server address: {0}")] + LocalAddress(#[source] std::io::Error), + #[error("RPC server startup callback failed")] + StartupCallback, + } + + /// Start a placeholder local RPC endpoint and keep it alive. + /// + /// This preserves the Android `api_proxy` dependency target while the + /// full getter RPC surface is rewritten. The function binds a real local + /// TCP listener so callers receive a concrete URL, then parks forever. + pub async fn run_server_hanging( + address: &str, + on_listening: F, + ) -> Result<(), RpcServerError> + where + F: FnOnce(&str) -> Result<(), RpcServerError> + Send + 'static, + { + let listener = tokio::net::TcpListener::bind(address) + .await + .map_err(|source| RpcServerError::Bind { + address: address.to_owned(), + source, + })?; + let local_addr = listener + .local_addr() + .map_err(RpcServerError::LocalAddress)?; + let url = format!("ws://{local_addr}"); + on_listening(&url)?; + + // Keep the listener alive until the host process stops. The full + // JSON-RPC implementation is added in a later behavior slice. + let _listener = listener; + pending::<()>().await; + #[allow(unreachable_code)] + Ok(()) + } + } +} diff --git a/src/locale.rs b/src/locale.rs deleted file mode 100644 index 82b257f..0000000 --- a/src/locale.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::env; -use std::io::{self, ErrorKind}; -use std::path::PathBuf; - -pub struct DataDir { - pub cache_dir: PathBuf, - pub data_dir: PathBuf, -} - -#[cfg(all( - target_family = "unix", - not(target_os = "macos"), - not(target_os = "android") -))] -pub fn all_dir() -> Result { - let home_dir = env::var("HOME") - .map_err(|_| io::Error::new(ErrorKind::NotFound, "HOME not found")) - .map(PathBuf::from)?; - let cache_dir = home_dir.join(".cache/upa/"); - let data_dir = home_dir.join(".local/share/upa/"); - Ok(DataDir { - cache_dir, - data_dir, - }) -} - -#[cfg(target_os = "macos")] -pub fn all_dir() -> Result { - let home_dir = env::var("HOME") - .map_err(|_| io::Error::new(ErrorKind::NotFound, "HOME not found")) - .map(|home| PathBuf::from(home))?; - let cache_dir = home_dir.join("Library/Caches/upa/"); - let data_dir = home_dir.join("Library/Application Support/upa/"); - Ok(DataDir { - cache_dir, - data_dir, - }) -} - -#[cfg(target_family = "windows")] -pub fn all_dir() -> Result { - let home_dir = env::var("APPDATA") - .map_err(|_| io::Error::new(ErrorKind::NotFound, "APPDATA not found")) - .map(|home| PathBuf::from(home))?; - let cache_dir = home_dir.join("upa/cache/"); - let data_dir = home_dir.join("upa/data/"); - Ok(DataDir { - cache_dir, - data_dir, - }) -} - -#[cfg(target_os = "android")] -pub fn all_dir() -> Result { - let home_dir = env::var("HOME") - .map_err(|_| io::Error::new(ErrorKind::NotFound, "HOME not found")) - .map(|home| PathBuf::from(home))?; - let cache_dir = home_dir.join(".upa/cache/"); - let data_dir = home_dir.join(".upa/data/"); - Ok(DataDir { - cache_dir, - data_dir, - }) -} diff --git a/src/main.rs b/src/main.rs index e7a11a9..c9bfeea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,17 @@ +#[cfg(feature = "cli")] fn main() { - println!("Hello, world!"); + let output = getter_cli::run(std::env::args()); + if !output.stdout.is_empty() { + print!("{}", output.stdout); + } + if !output.stderr.is_empty() { + eprint!("{}", output.stderr); + } + std::process::exit(output.exit_code.code()); +} + +#[cfg(not(feature = "cli"))] +fn main() { + eprintln!("getter binary requires the 'cli' feature"); + std::process::exit(2); } diff --git a/src/manager/android_api.rs b/src/manager/android_api.rs deleted file mode 100644 index 5e39948..0000000 --- a/src/manager/android_api.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::collections::HashMap; - -use jsonrpsee::core::client::ClientT; -use jsonrpsee::http_client::HttpClientBuilder; -use jsonrpsee::rpc_params; -use once_cell::sync::OnceCell; -use serde::{Deserialize, Serialize}; - -use crate::database::models::app::AppRecord; - -/// Global Android API client, registered via `register_android_api` RPC. -static ANDROID_API: OnceCell = OnceCell::new(); - -pub fn set_android_api(url: String) { - let _ = ANDROID_API.set(AndroidApi { url }); -} - -pub fn get_android_api() -> Option<&'static AndroidApi> { - ANDROID_API.get() -} - -/// Rust-side HTTP JSON-RPC client that calls back into the Kotlin -/// KotlinHubRpcServer for Android-specific functionality. -/// -/// Kotlin exposes `get_local_version` and `get_installed_apps` methods -/// on the same Ktor HTTP server that handles hub provider calls. -pub struct AndroidApi { - url: String, -} - -#[derive(Serialize, Deserialize, Debug)] -struct GetLocalVersionParams { - app_id: HashMap>, -} - -#[derive(Serialize, Deserialize, Debug)] -struct GetInstalledAppsParams { - ignore_system: bool, -} - -impl AndroidApi { - /// Query Kotlin for the locally-installed version of an app. - /// - /// Calls `get_local_version({app_id})` on the Kotlin Ktor server. - /// Returns `None` if the app is not installed or the call fails. - pub async fn get_local_version( - &self, - app_id: &HashMap>, - ) -> Option { - let client = HttpClientBuilder::default().build(&self.url).ok()?; - let params = rpc_params!(GetLocalVersionParams { - app_id: app_id.clone(), - }); - client - .request::, _>("get_local_version", params) - .await - .unwrap_or(None) - } - - /// Query Kotlin for all installed Android apps and Magisk modules. - /// - /// Calls `get_installed_apps({ignore_system})` on the Kotlin Ktor server. - /// Returns an empty list if the call fails. - pub async fn get_installed_apps(&self, ignore_system: bool) -> Vec { - let client = match HttpClientBuilder::default().build(&self.url) { - Ok(c) => c, - Err(_) => return vec![], - }; - let params = rpc_params!(GetInstalledAppsParams { ignore_system }); - client - .request::, _>("get_installed_apps", params) - .await - .unwrap_or_default() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_get_local_version_params_serialization() { - let app_id: HashMap> = HashMap::from([( - "android_app_package".to_string(), - Some("com.example.app".to_string()), - )]); - let params = GetLocalVersionParams { - app_id: app_id.clone(), - }; - let json = serde_json::to_string(¶ms).unwrap(); - assert!(json.contains("android_app_package")); - assert!(json.contains("com.example.app")); - } - - #[test] - fn test_get_installed_apps_params_serialization() { - let params = GetInstalledAppsParams { - ignore_system: true, - }; - let json = serde_json::to_string(¶ms).unwrap(); - assert!(json.contains("ignore_system")); - assert!(json.contains("true")); - } -} diff --git a/src/manager/app_manager.rs b/src/manager/app_manager.rs deleted file mode 100644 index 12b0b32..0000000 --- a/src/manager/app_manager.rs +++ /dev/null @@ -1,462 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; -use tokio::sync::{RwLock, Semaphore}; - -use super::android_api; -use super::app_status::AppStatus; -use super::data_getter::DataGetter; -use super::notification::{notify_if_registered, ManagerEvent}; -use super::updater::get_release_status; -use super::version_map::VersionMap; -use crate::database::get_db; -use crate::database::models::app::AppRecord; -use crate::error::Result; - -pub type UpdateCallback = Arc; - -/// Manages all tracked apps and their version data. -/// -/// Mirrors Kotlin's `AppManager`. -pub struct AppManager { - /// Saved apps from the database (keyed by record id) - apps: Arc>>, - /// Virtual apps from Android installed packages (not persisted) - virtual_apps: Arc>>, - /// In-memory version maps keyed by app record id - version_maps: Arc>>, - data_getter: Arc, -} - -impl AppManager { - pub fn load() -> Result { - let records = get_db().load_apps()?; - let apps = records.into_iter().map(|a| (a.id.clone(), a)).collect(); - Ok(Self { - apps: Arc::new(RwLock::new(apps)), - virtual_apps: Arc::new(RwLock::new(vec![])), - version_maps: Arc::new(RwLock::new(HashMap::new())), - data_getter: Arc::new(DataGetter::new()), - }) - } - - /// Replace the virtual app list (installed Android packages). - pub async fn set_virtual_apps(&self, apps: Vec) { - *self.virtual_apps.write().await = apps; - } - - pub async fn get_all_apps(&self) -> Vec { - let mut result: Vec = self.apps.read().await.values().cloned().collect(); - result.extend(self.virtual_apps.read().await.clone()); - result - } - - pub async fn get_saved_apps(&self) -> Vec { - self.apps.read().await.values().cloned().collect() - } - - pub async fn find_app_by_id( - &self, - app_id: &HashMap>, - ) -> Option { - self.apps - .read() - .await - .values() - .find(|a| &a.app_id == app_id) - .cloned() - } - - pub async fn get_app(&self, record_id: &str) -> Option { - self.apps.read().await.get(record_id).cloned() - } - - /// Persist (insert or update) an app record. - pub async fn save_app(&self, mut record: AppRecord) -> Result { - if record.id.is_empty() { - record.id = uuid::Uuid::new_v4().to_string(); - } - get_db().upsert_app(&record)?; - self.apps - .write() - .await - .insert(record.id.clone(), record.clone()); - Ok(record) - } - - /// Remove a saved app. - pub async fn remove_app(&self, record_id: &str) -> Result { - let deleted = get_db().delete_app(record_id)?; - self.apps.write().await.remove(record_id); - self.version_maps.write().await.remove(record_id); - Ok(deleted) - } - - /// Return the current AppStatus for an app. - /// - /// Queries Kotlin for the locally-installed version via `AndroidApi.get_local_version()` - /// if a callback URL has been registered; otherwise local_version is `None`. - pub async fn get_app_status(&self, record_id: &str) -> AppStatus { - let app = match self.apps.read().await.get(record_id).cloned() { - Some(a) => a, - None => return AppStatus::AppInactive, - }; - - // Query local version from Android via registered callback - let local_version: Option = match android_api::get_android_api() { - Some(api) => api.get_local_version(&app.app_id).await, - None => None, - }; - - let mut maps = self.version_maps.write().await; - let vm = maps.entry(record_id.to_string()).or_insert_with(|| { - VersionMap::new( - app.invalid_version_number_field_regex.clone(), - app.include_version_number_field_regex.clone(), - ) - }); - get_release_status( - vm, - local_version.as_deref(), - app.ignore_version_number.as_deref(), - true, - ) - } - - /// Refresh version data for all saved apps. - /// - /// Uses a semaphore (max 10 concurrent hub requests) matching Kotlin's logic. - /// Apps that have version-filter regexes (`need_complete_version`) skip the batch - /// API entirely and go straight to the full release-list path, mirroring Kotlin's - /// `simpleMap` / `completeMap` split in `AppManager.renewAppList()`. - /// Fires `RenewProgress` notifications to Kotlin UI as each app completes. - pub async fn renew_all( - &self, - hubs: &[crate::database::models::hub::HubRecord], - progress_cb: Option, - ) { - let apps = self.get_saved_apps().await; - let total = apps.len(); - let semaphore = Arc::new(Semaphore::new(10)); - - // Group apps by hub, splitting into simple (batch-eligible) vs complete-only. - let mut hub_simple_map: HashMap> = HashMap::new(); - let mut hub_complete_map: HashMap> = HashMap::new(); - for app in &apps { - let sorted_hubs = app.get_sorted_hub_uuids(); - let effective_hubs: Vec<&str> = if sorted_hubs.is_empty() { - hubs.iter().map(|h| h.uuid.as_str()).collect() - } else { - sorted_hubs.iter().map(String::as_str).collect() - }; - let dest = if need_complete_version(app) { - &mut hub_complete_map - } else { - &mut hub_simple_map - }; - for hub_uuid in effective_hubs { - dest.entry(hub_uuid.to_string()) - .or_default() - .push(app.clone()); - } - } - - let completed = Arc::new(std::sync::atomic::AtomicUsize::new(0)); - let mut handles = vec![]; - - for hub in hubs { - let simple_apps = hub_simple_map.get(&hub.uuid).cloned().unwrap_or_default(); - let complete_apps = hub_complete_map.get(&hub.uuid).cloned().unwrap_or_default(); - if simple_apps.is_empty() && complete_apps.is_empty() { - continue; - } - - let hub = hub.clone(); - let getter = self.data_getter.clone(); - let version_maps = self.version_maps.clone(); - let sem = semaphore.clone(); - let completed = completed.clone(); - let cb = progress_cb.clone(); - - let handle = tokio::spawn(async move { - let _permit = sem.acquire().await.unwrap(); - - // --- Batch path (simple apps only) --- - let mut need_full: Vec = complete_apps; // complete-only apps go straight here - if !simple_apps.is_empty() { - let app_ids: Vec>> = - simple_apps.iter().map(|a| a.app_id.clone()).collect(); - let latest_results = getter.get_latest_releases(&hub, &app_ids).await; - - for (app, (_, maybe_release)) in simple_apps.iter().zip(latest_results.iter()) { - if let Some(release) = maybe_release { - let new_status = { - let mut maps = version_maps.write().await; - let vm = maps.entry(app.id.clone()).or_insert_with(|| { - VersionMap::new( - app.invalid_version_number_field_regex.clone(), - app.include_version_number_field_regex.clone(), - ) - }); - vm.add_single_release(&hub.uuid, release.clone()); - let local_version: Option = - match android_api::get_android_api() { - Some(api) => api.get_local_version(&app.app_id).await, - None => None, - }; - get_release_status( - vm, - local_version.as_deref(), - app.ignore_version_number.as_deref(), - true, - ) - }; - let done = - completed.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1; - if let Some(ref f) = cb { - f(done, total); - } - notify_if_registered(ManagerEvent::RenewProgress { done, total }).await; - if new_status != AppStatus::AppPending { - notify_if_registered(ManagerEvent::AppStatusChanged { - record_id: app.id.clone(), - app_id: app.app_id.clone(), - old_status: AppStatus::AppPending, - new_status, - }) - .await; - } - } else { - // Batch returned nothing for this app — escalate to full list. - need_full.push(app.clone()); - } - } - } - - // --- Full release-list path --- - for app in need_full { - let new_status = { - let mut maps = version_maps.write().await; - let vm = maps.entry(app.id.clone()).or_insert_with(|| { - VersionMap::new( - app.invalid_version_number_field_regex.clone(), - app.include_version_number_field_regex.clone(), - ) - }); - if let Some(releases) = getter.get_release_list(&hub, &app.app_id).await { - vm.add_release_list(&hub.uuid, releases); - } else { - vm.set_error(&hub.uuid); - } - let local_version: Option = match android_api::get_android_api() { - Some(api) => api.get_local_version(&app.app_id).await, - None => None, - }; - get_release_status( - vm, - local_version.as_deref(), - app.ignore_version_number.as_deref(), - true, - ) - }; - let done = completed.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1; - if let Some(ref f) = cb { - f(done, total); - } - notify_if_registered(ManagerEvent::RenewProgress { done, total }).await; - if new_status != AppStatus::AppPending { - notify_if_registered(ManagerEvent::AppStatusChanged { - record_id: app.id.clone(), - app_id: app.app_id.clone(), - old_status: AppStatus::AppPending, - new_status, - }) - .await; - } - } - }); - handles.push(handle); - } - - for handle in handles { - let _ = handle.await; - } - } - - /// Return record IDs of saved apps that have no valid hub configured. - /// - /// An app is considered "invalid" when its `enable_hub_list` is non-empty but - /// none of the listed hub UUIDs exist in `known_hub_uuids`. Apps with an empty - /// hub list (meaning "use all hubs") are never reported as invalid. - pub async fn check_invalid_applications(&self, known_hub_uuids: &[String]) -> Vec { - let apps = self.apps.read().await; - apps.values() - .filter_map(|app| { - let hub_uuids = app.get_sorted_hub_uuids(); - // Empty list means "match any hub" — not invalid. - if hub_uuids.is_empty() { - return None; - } - let has_valid = hub_uuids.iter().any(|uuid| known_hub_uuids.contains(uuid)); - if has_valid { - None - } else { - Some(app.id.clone()) - } - }) - .collect() - } -} - -/// Returns `true` if the app requires the full release list rather than the single -/// latest-release batch API. -/// -/// Mirrors Kotlin's `App.needCompleteVersion`: -/// ```kotlin -/// val needCompleteVersion: Boolean -/// get() = db.includeVersionNumberFieldRegexString != null -/// || db.invalidVersionNumberFieldRegexString != null -/// ``` -fn need_complete_version(app: &AppRecord) -> bool { - app.include_version_number_field_regex.is_some() - || app.invalid_version_number_field_regex.is_some() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::database; - - #[test] - fn test_app_record_save_and_load() { - let dir = tempfile::tempdir().unwrap(); - let db = database::Database::open(dir.path()).unwrap(); - - let app = AppRecord::new( - "MyApp".to_string(), - HashMap::from([("owner".to_string(), Some("alice".to_string()))]), - ); - db.upsert_app(&app).unwrap(); - let loaded = db.load_apps().unwrap(); - assert_eq!(loaded.len(), 1); - assert_eq!(loaded[0].name, "MyApp"); - } - - #[tokio::test] - async fn test_version_map_get_status_no_data() { - let mut vm = VersionMap::new(None, None); - let status = get_release_status(&mut vm, Some("1.0.0"), None, true); - // Empty version map + is_saved → NetworkError (no hub_status entries) - assert_eq!(status, AppStatus::NetworkError); - } - - // ------------------------------------------------------------------------- - // Phase 7A: need_complete_version - // ------------------------------------------------------------------------- - - #[test] - fn test_need_complete_version_false_when_no_regex() { - let app = AppRecord::new("App".to_string(), HashMap::new()); - assert!(!need_complete_version(&app)); - } - - #[test] - fn test_need_complete_version_true_when_invalid_regex() { - let mut app = AppRecord::new("App".to_string(), HashMap::new()); - app.invalid_version_number_field_regex = Some("alpha|beta".to_string()); - assert!(need_complete_version(&app)); - } - - #[test] - fn test_need_complete_version_true_when_include_regex() { - let mut app = AppRecord::new("App".to_string(), HashMap::new()); - app.include_version_number_field_regex = Some(r"\d+\.\d+".to_string()); - assert!(need_complete_version(&app)); - } - - #[test] - fn test_need_complete_version_true_when_both_regex() { - let mut app = AppRecord::new("App".to_string(), HashMap::new()); - app.invalid_version_number_field_regex = Some("alpha".to_string()); - app.include_version_number_field_regex = Some(r"\d+".to_string()); - assert!(need_complete_version(&app)); - } - - // ------------------------------------------------------------------------- - // Phase 7B: check_invalid_applications - // ------------------------------------------------------------------------- - - fn make_app_with_hubs(name: &str, hubs: &[&str]) -> AppRecord { - let mut app = AppRecord::new(name.to_string(), HashMap::new()); - let hub_strs: Vec = hubs.iter().map(|s| s.to_string()).collect(); - app.set_sorted_hub_uuids(&hub_strs); - app - } - - async fn app_manager_with_apps(apps: Vec) -> AppManager { - let map = apps.into_iter().map(|a| (a.id.clone(), a)).collect(); - AppManager { - apps: Arc::new(RwLock::new(map)), - virtual_apps: Arc::new(RwLock::new(vec![])), - version_maps: Arc::new(RwLock::new(HashMap::new())), - data_getter: Arc::new(DataGetter::new()), - } - } - - #[tokio::test] - async fn test_check_invalid_no_apps() { - let mgr = app_manager_with_apps(vec![]).await; - let invalid = mgr.check_invalid_applications(&["hub-1".to_string()]).await; - assert!(invalid.is_empty()); - } - - #[tokio::test] - async fn test_check_invalid_empty_hub_list_not_reported() { - // App with no hub list means "use all hubs" — never invalid. - let app = AppRecord::new("NoHubs".to_string(), HashMap::new()); - let mgr = app_manager_with_apps(vec![app]).await; - let invalid = mgr.check_invalid_applications(&[]).await; - assert!(invalid.is_empty()); - } - - #[tokio::test] - async fn test_check_invalid_all_hubs_known() { - let app = make_app_with_hubs("GoodApp", &["hub-a", "hub-b"]); - let mgr = app_manager_with_apps(vec![app]).await; - let known = vec!["hub-a".to_string(), "hub-b".to_string()]; - let invalid = mgr.check_invalid_applications(&known).await; - assert!(invalid.is_empty()); - } - - #[tokio::test] - async fn test_check_invalid_one_hub_known_is_valid() { - // Even if only one of the listed hubs is known, the app is valid. - let app = make_app_with_hubs("SemiGood", &["hub-a", "hub-unknown"]); - let mgr = app_manager_with_apps(vec![app]).await; - let known = vec!["hub-a".to_string()]; - let invalid = mgr.check_invalid_applications(&known).await; - assert!(invalid.is_empty()); - } - - #[tokio::test] - async fn test_check_invalid_all_hubs_unknown() { - let app = make_app_with_hubs("BadApp", &["hub-x", "hub-y"]); - let app_id = app.id.clone(); - let mgr = app_manager_with_apps(vec![app]).await; - let known = vec!["hub-a".to_string()]; - let invalid = mgr.check_invalid_applications(&known).await; - assert_eq!(invalid, vec![app_id]); - } - - #[tokio::test] - async fn test_check_invalid_mixed_apps() { - let good = make_app_with_hubs("Good", &["hub-a"]); - let bad = make_app_with_hubs("Bad", &["hub-z"]); - let bad_id = bad.id.clone(); - let no_hub = AppRecord::new("NoHub".to_string(), HashMap::new()); - let mgr = app_manager_with_apps(vec![good, bad, no_hub]).await; - let known = vec!["hub-a".to_string()]; - let mut invalid = mgr.check_invalid_applications(&known).await; - invalid.sort(); - assert_eq!(invalid, vec![bad_id]); - } -} diff --git a/src/manager/app_status.rs b/src/manager/app_status.rs deleted file mode 100644 index 088ce22..0000000 --- a/src/manager/app_status.rs +++ /dev/null @@ -1,18 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum AppStatus { - /// App exists only in hub's auto-discovery; not in user's saved list - AppInactive, - /// Version data is being fetched - AppPending, - /// Network request failed - NetworkError, - /// Local version is up to date - AppLatest, - /// A newer version is available - AppOutdated, - /// No local version found (e.g. not installed) - AppNoLocal, -} diff --git a/src/manager/auto_template.rs b/src/manager/auto_template.rs deleted file mode 100644 index bfb640d..0000000 --- a/src/manager/auto_template.rs +++ /dev/null @@ -1,206 +0,0 @@ -use std::collections::HashMap; - -/// URL template regex: matches `%placeholder` tokens. -/// Mirrors Kotlin's `URL_ARG_REGEX = "(%.*?)\\w*"`. -const URL_ARG_PATTERN: &str = r"(%[^%/?\s]+)"; - -/// Given a URL and a list of templates, return the first template that fully -/// matches the URL, with all placeholder values extracted. -/// -/// Template format: `https://github.com/%owner/%repo/releases` -/// Placeholders are `%key` tokens. The returned map has keys without the `%`. -/// -/// Returns `None` if no template matches fully. -/// -/// Mirrors Kotlin's `AutoTemplate.urlToAppId()`. -pub fn url_to_app_id(url: &str, templates: &[String]) -> Option> { - if url.is_empty() || templates.is_empty() { - return None; - } - for template in templates { - if let Some(args) = match_template(url, template) { - return Some(args); - } - } - None -} - -/// Attempt to match `url` against a single `template`. -/// -/// The algorithm splits the template into alternating [literal, placeholder] -/// segments, then uses literals to cut the URL apart and extract placeholder -/// values. Mirrors Kotlin's `AutoTemplate.matchArgs()` and `checkFull()`. -fn match_template(url: &str, template: &str) -> Option> { - let re = regex::Regex::new(URL_ARG_PATTERN).ok()?; - - // Build ordered list of segments: either a literal string or a %placeholder - let mut segments: Vec = Vec::new(); - let mut last = 0; - for m in re.find_iter(template) { - if m.start() > last { - segments.push(Segment::Literal(template[last..m.start()].to_string())); - } - segments.push(Segment::Placeholder(m.as_str().to_string())); - last = m.end(); - } - if last < template.len() { - segments.push(Segment::Literal(template[last..].to_string())); - } - - // Collect expected placeholder keys (in order) - let expected_keys: Vec = segments - .iter() - .filter_map(|s| { - if let Segment::Placeholder(p) = s { - Some(p.trim_start_matches('%').to_string()) - } else { - None - } - }) - .collect(); - - if expected_keys.is_empty() { - return None; - } - - // Walk through segments: use literals to split the URL and assign values to - // adjacent placeholders. - let mut args: HashMap = HashMap::new(); - let mut remaining = url.to_string(); - - for (i, seg) in segments.iter().enumerate() { - match seg { - Segment::Literal(lit) => { - if lit.is_empty() { - continue; - } - // Find the literal in `remaining`, split on it - match remaining.split_once(lit.as_str()) { - Some((before, after)) => { - // `before` belongs to the preceding placeholder (if any) - if i > 0 { - if let Segment::Placeholder(key) = &segments[i - 1] { - let k = key.trim_start_matches('%').to_string(); - if !before.is_empty() { - args.insert(k, before.to_string()); - } - } - } - remaining = after.to_string(); - } - None => return None, // literal not found → no match - } - } - Segment::Placeholder(_) => { - // Value will be filled in when the next literal is processed, - // or captured from trailing remaining string at end. - } - } - } - - // If the last segment is a placeholder, the rest of `remaining` is its value - if let Some(Segment::Placeholder(key)) = segments.last() { - let k = key.trim_start_matches('%').to_string(); - // Strip trailing slash or query string for cleanliness - let val = remaining.trim_end_matches('/').to_string(); - if !val.is_empty() { - args.insert(k, val); - } - } - - // Verify all expected keys were matched - for key in &expected_keys { - if !args.contains_key(key) { - return None; - } - } - - Some(args) -} - -#[derive(Debug)] -enum Segment { - Literal(String), - Placeholder(String), -} - -#[cfg(test)] -mod tests { - use super::*; - - fn templates(list: &[&str]) -> Vec { - list.iter().map(|s| s.to_string()).collect() - } - - #[test] - fn test_github_url() { - let url = "https://github.com/DUpdateSystem/UpgradeAll"; - let result = url_to_app_id(url, &templates(&["https://github.com/%owner/%repo"])); - let map = result.unwrap(); - assert_eq!(map["owner"], "DUpdateSystem"); - assert_eq!(map["repo"], "UpgradeAll"); - } - - #[test] - fn test_github_url_with_trailing_slash() { - let url = "https://github.com/foo/bar/"; - let result = url_to_app_id(url, &templates(&["https://github.com/%owner/%repo/"])); - let map = result.unwrap(); - assert_eq!(map["owner"], "foo"); - assert_eq!(map["repo"], "bar"); - } - - #[test] - fn test_gitlab_url() { - let url = "https://gitlab.com/AuroraOSS/AuroraStore"; - let result = url_to_app_id(url, &templates(&["https://gitlab.com/%owner/%repo"])); - let map = result.unwrap(); - assert_eq!(map["owner"], "AuroraOSS"); - assert_eq!(map["repo"], "AuroraStore"); - } - - #[test] - fn test_no_match_returns_none() { - let url = "https://example.com/something/else"; - let result = url_to_app_id(url, &templates(&["https://github.com/%owner/%repo"])); - assert!(result.is_none()); - } - - #[test] - fn test_first_matching_template_wins() { - let url = "https://github.com/user/proj"; - let result = url_to_app_id( - url, - &templates(&[ - "https://gitlab.com/%owner/%repo", - "https://github.com/%owner/%repo", - ]), - ); - let map = result.unwrap(); - assert_eq!(map["owner"], "user"); - assert_eq!(map["repo"], "proj"); - } - - #[test] - fn test_empty_url_returns_none() { - let result = url_to_app_id("", &templates(&["https://github.com/%owner/%repo"])); - assert!(result.is_none()); - } - - #[test] - fn test_empty_templates_returns_none() { - let result = url_to_app_id("https://github.com/a/b", &[]); - assert!(result.is_none()); - } - - #[test] - fn test_single_placeholder() { - let url = "https://f-droid.org/packages/com.example.app/"; - let result = url_to_app_id( - url, - &templates(&["https://f-droid.org/packages/%package_name/"]), - ); - let map = result.unwrap(); - assert_eq!(map["package_name"], "com.example.app"); - } -} diff --git a/src/manager/cloud_config_getter.rs b/src/manager/cloud_config_getter.rs deleted file mode 100644 index b4fbc66..0000000 --- a/src/manager/cloud_config_getter.rs +++ /dev/null @@ -1,403 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; -use tokio::sync::RwLock; - -use crate::database::models::app::AppRecord; -use crate::database::models::hub::HubRecord; -use crate::error::{Error, Result}; -use crate::manager::app_manager::AppManager; -use crate::manager::auto_template::url_to_app_id; -use crate::manager::hub_manager::HubManager; -use crate::websdk::cloud_rules::cloud_rules_manager::CloudRules; -use crate::websdk::cloud_rules::data::app_item::AppItem; -use crate::websdk::cloud_rules::data::hub_item::HubItem; - -/// Manages downloading, caching, and applying cloud hub/app configurations. -/// -/// Mirrors Kotlin's `CloudConfigGetter` singleton. -pub struct CloudConfigGetter { - api_url: String, - cloud_rules: Arc>>, -} - -impl CloudConfigGetter { - pub fn new(api_url: String) -> Self { - Self { - api_url, - cloud_rules: Arc::new(RwLock::new(None)), - } - } - - /// Download and cache the latest cloud config list. - pub async fn renew(&self) -> Result<()> { - let mut rules = CloudRules::new(&self.api_url); - rules - .renew() - .await - .map_err(|e| Error::Other(e.to_string()))?; - *self.cloud_rules.write().await = Some(rules); - Ok(()) - } - - /// Returns all available app configs from the cached cloud config. - pub async fn app_config_list(&self) -> Vec { - match self.cloud_rules.read().await.as_ref() { - Some(rules) => rules - .get_config_list() - .app_config_list - .into_iter() - .cloned() - .collect(), - None => vec![], - } - } - - /// Returns all available hub configs from the cached cloud config. - pub async fn hub_config_list(&self) -> Vec { - match self.cloud_rules.read().await.as_ref() { - Some(rules) => rules - .get_config_list() - .hub_config_list - .into_iter() - .cloned() - .collect(), - None => vec![], - } - } - - /// Apply a cloud hub config by UUID: download config → convert → upsert into HubManager. - /// - /// Returns true if the hub was installed or updated. - pub async fn apply_hub_config(&self, uuid: &str, hub_mgr: &mut HubManager) -> Result { - let hub_item = self - .find_hub_item(uuid) - .await - .ok_or_else(|| Error::Other(format!("Hub config not found: {uuid}")))?; - - let record = hub_item_to_record(&hub_item, hub_mgr).await; - hub_mgr.upsert_hub(record).await?; - Ok(true) - } - - /// Apply a cloud app config by UUID: - /// 1. Ensure hub dependency is installed. - /// 2. Extract app_id from the app's URL using the hub's URL templates. - /// 3. Merge with `extra_map` from the cloud config. - /// 4. Upsert into AppManager. - /// - /// Returns true if the app was installed or updated. - pub async fn apply_app_config( - &self, - uuid: &str, - app_mgr: &mut AppManager, - hub_mgr: &mut HubManager, - ) -> Result { - let app_item = self - .find_app_item(uuid) - .await - .ok_or_else(|| Error::Other(format!("App config not found: {uuid}")))?; - - // Ensure hub dependency is present - self.solve_hub_dependency(&app_item.base_hub_uuid, hub_mgr) - .await?; - - // Build the app_id map - let app_id = build_app_id(&app_item, hub_mgr).await; - - // Find existing record by cloud UUID or create a new one - let mut record = app_mgr - .find_app_by_cloud_uuid(&app_item.uuid) - .await - .unwrap_or_else(|| { - let mut r = AppRecord::new(app_item.info.name.clone(), app_id.clone()); - r.id = String::new(); // let save_app assign UUID - r - }); - - record.name = app_item.info.name.clone(); - record.app_id = app_id; - record.cloud_config = Some(app_item.clone()); - - // Ensure base_hub_uuid is first in the hub priority list - let mut hub_uuids = record.get_sorted_hub_uuids(); - if !hub_uuids.contains(&app_item.base_hub_uuid) { - hub_uuids.insert(0, app_item.base_hub_uuid.clone()); - } else if hub_uuids[0] != app_item.base_hub_uuid { - hub_uuids.retain(|u| u != &app_item.base_hub_uuid); - hub_uuids.insert(0, app_item.base_hub_uuid.clone()); - } - record.set_sorted_hub_uuids(&hub_uuids); - - app_mgr.save_app(record).await?; - Ok(true) - } - - /// Bulk-update all installed apps and hubs whose cloud config version has increased. - /// - /// Mirrors Kotlin's `renewAllAppConfigFromCloud` + `renewAllHubConfigFromCloud`. - pub async fn renew_all_from_cloud( - &self, - app_mgr: &mut AppManager, - hub_mgr: &mut HubManager, - ) -> Result<()> { - // Renew hubs - let installed_hubs = hub_mgr.get_hub_list().await; - for hub in &installed_hubs { - if let Some(cloud_hub) = self.find_hub_item(&hub.uuid).await { - if cloud_hub.config_version > hub.hub_config.config_version { - let record = hub_item_to_record(&cloud_hub, hub_mgr).await; - hub_mgr.upsert_hub(record).await?; - } - } - } - - // Renew apps - let installed_apps = app_mgr.get_saved_apps().await; - for app in &installed_apps { - let cloud_uuid = match app.cloud_config.as_ref().map(|c| c.uuid.as_str()) { - Some(u) if !u.is_empty() => u.to_string(), - _ => continue, - }; - if let Some(cloud_app) = self.find_app_item(&cloud_uuid).await { - let installed_version = app - .cloud_config - .as_ref() - .map(|c| c.config_version) - .unwrap_or(0); - if cloud_app.config_version > installed_version { - let _ = self.apply_app_config(&cloud_uuid, app_mgr, hub_mgr).await; - } - } - } - - Ok(()) - } - - /// Ensure a hub is installed; if not, download and install it from cloud config. - async fn solve_hub_dependency(&self, hub_uuid: &str, hub_mgr: &mut HubManager) -> Result<()> { - if hub_mgr.get_hub(hub_uuid).await.is_some() { - // Already installed — check if update needed - let installed = hub_mgr.get_hub(hub_uuid).await.unwrap(); - if let Some(cloud) = self.find_hub_item(hub_uuid).await { - if cloud.config_version > installed.hub_config.config_version { - let record = hub_item_to_record(&cloud, hub_mgr).await; - hub_mgr.upsert_hub(record).await?; - } - } - } else { - // Not installed — download and install - self.apply_hub_config(hub_uuid, hub_mgr).await?; - } - Ok(()) - } - - async fn find_app_item(&self, uuid: &str) -> Option { - self.cloud_rules - .read() - .await - .as_ref()? - .get_config_list() - .app_config_list - .into_iter() - .find(|a| a.uuid == uuid) - .cloned() - } - - async fn find_hub_item(&self, uuid: &str) -> Option { - self.cloud_rules - .read() - .await - .as_ref()? - .get_config_list() - .hub_config_list - .into_iter() - .find(|h| h.uuid == uuid) - .cloned() - } -} - -/// Convert a `HubItem` from cloud config to a `HubRecord`, preserving any -/// existing auth / ignore lists if the hub is already installed. -async fn hub_item_to_record(hub_item: &HubItem, hub_mgr: &HubManager) -> HubRecord { - if let Some(existing) = hub_mgr.get_hub(&hub_item.uuid).await { - // Preserve mutable fields, update config fields - HubRecord { - hub_config: hub_item.clone(), - ..existing - } - } else { - HubRecord::new(hub_item.uuid.clone(), hub_item.clone()) - } -} - -/// Build the `app_id` map from a cloud `AppItem`. -/// -/// 1. Try to extract from `info.url` using the hub's `app_url_templates`. -/// 2. Merge with `info.extra_map` (extra_map takes precedence for any shared keys). -async fn build_app_id(app_item: &AppItem, hub_mgr: &HubManager) -> HashMap> { - let mut app_id: HashMap> = HashMap::new(); - - // Try URL template extraction - if let Some(hub) = hub_mgr.get_hub(&app_item.base_hub_uuid).await { - let templates = &hub.hub_config.app_url_templates; - if !app_item.info.url.is_empty() { - if let Some(extracted) = url_to_app_id(&app_item.info.url, templates) { - for (k, v) in extracted { - app_id.insert(k, Some(v)); - } - } - } - } - - // Merge extra_map — extra_map values always win over URL-extracted values - for (k, v) in &app_item.info.extra_map { - app_id.insert(k.clone(), Some(v.clone())); - } - - app_id -} - -// Extension on AppManager to find by cloud UUID -impl AppManager { - pub async fn find_app_by_cloud_uuid(&self, cloud_uuid: &str) -> Option { - self.get_saved_apps().await.into_iter().find(|a| { - a.cloud_config - .as_ref() - .map(|c| c.uuid == cloud_uuid) - .unwrap_or(false) - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::websdk::cloud_rules::data::app_item::AppInfo; - use crate::websdk::cloud_rules::data::hub_item::Info; - - fn make_hub_item(uuid: &str, templates: Vec) -> HubItem { - HubItem { - base_version: 6, - config_version: 1, - uuid: uuid.to_string(), - info: Info { - hub_name: "GitHub".to_string(), - hub_icon_url: None, - }, - api_keywords: vec!["owner".to_string(), "repo".to_string()], - auth_keywords: vec![], - app_url_templates: templates, - target_check_api: None, - } - } - - fn make_app_item(uuid: &str, hub_uuid: &str, url: &str) -> AppItem { - AppItem { - base_version: 2, - config_version: 1, - uuid: uuid.to_string(), - base_hub_uuid: hub_uuid.to_string(), - info: AppInfo { - name: "TestApp".to_string(), - url: url.to_string(), - extra_map: HashMap::new(), - }, - } - } - - #[tokio::test] - async fn test_build_app_id_from_url() { - let dir = tempfile::tempdir().unwrap(); - let _ = crate::database::init_db(dir.path()); - - let hub_uuid = "fd9b2602-62c5-4d55-bd1e-0d6537714ca0"; - let db = crate::database::Database::open(dir.path()).unwrap(); - let hub = HubRecord::new( - hub_uuid.to_string(), - make_hub_item( - hub_uuid, - vec!["https://github.com/%owner/%repo/".to_string()], - ), - ); - db.upsert_hub(&hub).unwrap(); - - let hub_mgr = HubManager::load().unwrap_or_else(|_| { - // fallback: create manager from local db - let records = db.load_hubs().unwrap(); - let _ = records; // suppress unused warning - // We can't easily construct HubManager without global DB, so use a helper - panic!("This test requires global DB init") - }); - let _ = hub_mgr; - } - - #[test] - fn test_build_app_id_extra_map_wins() { - // extra_map should take precedence over URL-extracted values - let app_item = AppItem { - base_version: 2, - config_version: 1, - uuid: "test-uuid".to_string(), - base_hub_uuid: "hub-uuid".to_string(), - info: AppInfo { - name: "TestApp".to_string(), - url: "https://github.com/owner/repo".to_string(), - extra_map: HashMap::from([ - ("android_app_package".to_string(), "com.example".to_string()), - ("owner".to_string(), "override_owner".to_string()), - ]), - }, - }; - - // Simulate what build_app_id does with extra_map override - let mut app_id: HashMap> = HashMap::new(); - // Pretend URL extraction gave owner=owner, repo=repo - app_id.insert("owner".to_string(), Some("owner".to_string())); - app_id.insert("repo".to_string(), Some("repo".to_string())); - // extra_map override - for (k, v) in &app_item.info.extra_map { - app_id.insert(k.clone(), Some(v.clone())); - } - // owner should be overridden by extra_map - assert_eq!(app_id["owner"], Some("override_owner".to_string())); - assert_eq!( - app_id["android_app_package"], - Some("com.example".to_string()) - ); - // repo still from URL extraction - assert_eq!(app_id["repo"], Some("repo".to_string())); - } - - #[test] - fn test_hub_item_to_record_preserves_auth() { - // hub_item_to_record should preserve auth from existing record - // We test the logic inline since it's async and needs a HubManager - let hub_item = make_hub_item( - "hub-1", - vec!["https://github.com/%owner/%repo/".to_string()], - ); - let existing = HubRecord { - uuid: "hub-1".to_string(), - hub_config: make_hub_item("hub-1", vec![]), - auth: HashMap::from([("token".to_string(), "secret".to_string())]), - ignore_app_id_list: vec![], - applications_mode: 1, - user_ignore_app_id_list: vec![], - sort_point: -5, - }; - - // Simulate hub_item_to_record(hub_item, existing) - let record = HubRecord { - hub_config: hub_item.clone(), - ..existing.clone() - }; - - assert_eq!(record.auth["token"], "secret"); - assert_eq!(record.applications_mode, 1); - assert_eq!(record.sort_point, -5); - assert_eq!( - record.hub_config.app_url_templates[0], - "https://github.com/%owner/%repo/" - ); - } -} diff --git a/src/manager/data_getter.rs b/src/manager/data_getter.rs deleted file mode 100644 index 49312ef..0000000 --- a/src/manager/data_getter.rs +++ /dev/null @@ -1,99 +0,0 @@ -use std::collections::{BTreeMap, HashMap}; -use std::sync::Arc; -use tokio::sync::Mutex; - -use crate::database::models::hub::HubRecord; -use crate::websdk::repo::api; -use crate::websdk::repo::data::release::ReleaseData; - -/// Result of a batch latest-release request. -/// Each entry is (app_id, Option) — None means the app wasn't found. -pub type LatestReleaseResults = Vec<(HashMap>, Option)>; - -/// Fetches release data from hub providers. -/// -/// Mirrors Kotlin's `DataGetter`. -pub struct DataGetter { - /// Per-hub mutex to prevent duplicate concurrent requests. - hub_locks: Arc>>>>, -} - -impl DataGetter { - pub fn new() -> Self { - Self { - hub_locks: Arc::new(Mutex::new(HashMap::new())), - } - } - - async fn hub_lock(&self, hub_uuid: &str) -> Arc> { - let mut locks = self.hub_locks.lock().await; - locks - .entry(hub_uuid.to_string()) - .or_insert_with(|| Arc::new(Mutex::new(()))) - .clone() - } - - /// Fetch the latest single release for each app from one hub. - /// - /// Returns a vec of `(app_id, Option)`. - /// `None` means the hub didn't return data for that app. - pub async fn get_latest_releases( - &self, - hub: &HubRecord, - app_ids: &[HashMap>], - ) -> LatestReleaseResults { - let lock = self.hub_lock(&hub.uuid).await; - let _guard = lock.lock().await; - - let mut results = Vec::with_capacity(app_ids.len()); - for app_id in app_ids { - let app_data = build_app_data(app_id, &hub.hub_config.api_keywords); - if app_data.is_empty() { - results.push((app_id.clone(), None)); - continue; - } - let hub_data = build_hub_data(&hub.auth); - let release = api::get_latest_release(&hub.uuid, &app_data, &hub_data).await; - results.push((app_id.clone(), release)); - } - results - } - - /// Fetch the full release list for a single app from one hub. - pub async fn get_release_list( - &self, - hub: &HubRecord, - app_id: &HashMap>, - ) -> Option> { - let lock = self.hub_lock(&hub.uuid).await; - let _guard = lock.lock().await; - - let app_data = build_app_data(app_id, &hub.hub_config.api_keywords); - if app_data.is_empty() { - return None; - } - let hub_data = build_hub_data(&hub.auth); - api::get_releases(&hub.uuid, &app_data, &hub_data).await - } -} - -fn build_app_data<'a>( - app_id: &'a HashMap>, - api_keywords: &[String], -) -> BTreeMap<&'a str, &'a str> { - app_id - .iter() - .filter(|(k, v)| api_keywords.contains(k) && v.is_some()) - .map(|(k, v)| (k.as_str(), v.as_deref().unwrap())) - .collect() -} - -fn build_hub_data(auth: &HashMap) -> BTreeMap<&str, &str> { - auth.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect() -} - -impl Default for DataGetter { - fn default() -> Self { - Self::new() - } -} diff --git a/src/manager/hub_manager.rs b/src/manager/hub_manager.rs deleted file mode 100644 index 9c52503..0000000 --- a/src/manager/hub_manager.rs +++ /dev/null @@ -1,173 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; -use tokio::sync::RwLock; - -use crate::database::get_db; -use crate::database::models::hub::HubRecord; -use crate::error::Result; - -/// In-memory hub registry backed by the JSONL database. -/// -/// Mirrors Kotlin's `HubManager`. -pub struct HubManager { - hubs: Arc>>, -} - -impl HubManager { - /// Load all hubs from the database. - pub fn load() -> Result { - let records = get_db().load_hubs()?; - let map = records.into_iter().map(|h| (h.uuid.clone(), h)).collect(); - Ok(Self { - hubs: Arc::new(RwLock::new(map)), - }) - } - - pub async fn get_hub_list(&self) -> Vec { - self.hubs.read().await.values().cloned().collect() - } - - pub async fn get_hub(&self, uuid: &str) -> Option { - self.hubs.read().await.get(uuid).cloned() - } - - /// Insert or update a hub (persists to database). - pub async fn upsert_hub(&self, record: HubRecord) -> Result<()> { - get_db().upsert_hub(&record)?; - self.hubs.write().await.insert(record.uuid.clone(), record); - Ok(()) - } - - /// Remove a hub by UUID (persists deletion to database). - pub async fn remove_hub(&self, uuid: &str) -> Result { - let deleted = get_db().delete_hub(uuid)?; - self.hubs.write().await.remove(uuid); - Ok(deleted) - } - - pub async fn is_applications_mode_enabled(&self) -> bool { - self.hubs - .read() - .await - .values() - .any(|h| h.applications_mode_enabled()) - } - - /// Update the auth map for a hub identified by UUID and persist the change. - /// - /// Returns `false` if no hub with the given UUID exists. - pub async fn update_auth(&self, uuid: &str, auth: HashMap) -> Result { - let mut hubs = self.hubs.write().await; - let hub = match hubs.get_mut(uuid) { - Some(h) => h, - None => return Ok(false), - }; - hub.auth = auth; - get_db().upsert_hub(hub)?; - Ok(true) - } - - /// Return hubs whose api_keywords contain any of the given app_id keys. - pub async fn hubs_for_app(&self, app_id: &HashMap>) -> Vec { - let app_keys: Vec<&str> = app_id.keys().map(String::as_str).collect(); - self.hubs - .read() - .await - .values() - .filter(|h| { - h.hub_config - .api_keywords - .iter() - .any(|kw| app_keys.contains(&kw.as_str())) - }) - .cloned() - .collect() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::database; - use crate::websdk::cloud_rules::data::hub_item::{HubItem, Info}; - use tempfile::TempDir; - - fn setup_db() -> TempDir { - let dir = tempfile::tempdir().unwrap(); - database::init_db(dir.path()).ok(); // may already be init in other tests - dir - } - - fn make_hub(uuid: &str) -> HubRecord { - HubRecord::new( - uuid.to_string(), - HubItem { - base_version: 6, - config_version: 1, - uuid: uuid.to_string(), - info: Info { - hub_name: "TestHub".to_string(), - hub_icon_url: None, - }, - api_keywords: vec!["owner".to_string(), "repo".to_string()], - auth_keywords: vec![], - app_url_templates: vec![], - target_check_api: None, - }, - ) - } - - // These tests use a fresh TempDir + DB each time via open() directly, - // bypassing the global singleton to allow parallel test runs. - #[test] - fn test_upsert_and_list() { - let dir = tempfile::tempdir().unwrap(); - let db = crate::database::Database::open(dir.path()).unwrap(); - let hub = make_hub("uuid-1"); - db.upsert_hub(&hub).unwrap(); - let hubs = db.load_hubs().unwrap(); - assert_eq!(hubs.len(), 1); - assert_eq!(hubs[0].uuid, "uuid-1"); - } - - #[test] - fn test_delete_hub() { - let dir = tempfile::tempdir().unwrap(); - let db = crate::database::Database::open(dir.path()).unwrap(); - let hub = make_hub("uuid-2"); - db.upsert_hub(&hub).unwrap(); - let deleted = db.delete_hub("uuid-2").unwrap(); - assert!(deleted); - assert!(db.load_hubs().unwrap().is_empty()); - } - - #[tokio::test] - async fn test_update_auth() { - let dir = tempfile::tempdir().unwrap(); - crate::database::init_db(dir.path()).ok(); - - // Insert the hub via HubManager so it is in both the global DB and in-memory state. - let mgr = HubManager::load().unwrap(); - let hub = make_hub("uuid-auth"); - mgr.upsert_hub(hub).await.unwrap(); - - let new_auth: HashMap = - [("token".to_string(), "ghp_test123".to_string())].into(); - let ok = mgr - .update_auth("uuid-auth", new_auth.clone()) - .await - .unwrap(); - assert!(ok); - - // Verify in-memory state updated. - let updated = mgr.get_hub("uuid-auth").await.unwrap(); - assert_eq!(updated.auth, new_auth); - - // Returns false for unknown UUID. - let not_found = mgr - .update_auth("no-such-uuid", HashMap::new()) - .await - .unwrap(); - assert!(!not_found); - } -} diff --git a/src/manager/mod.rs b/src/manager/mod.rs deleted file mode 100644 index 275d73c..0000000 --- a/src/manager/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub mod android_api; -pub mod app_manager; -pub mod app_status; -pub mod auto_template; -pub mod cloud_config_getter; -pub mod data_getter; -pub mod hub_manager; -pub mod notification; -pub mod updater; -pub mod url_replace; -pub mod version; -pub mod version_map; diff --git a/src/manager/notification.rs b/src/manager/notification.rs deleted file mode 100644 index ef23840..0000000 --- a/src/manager/notification.rs +++ /dev/null @@ -1,152 +0,0 @@ -use once_cell::sync::OnceCell; -use serde::{Deserialize, Serialize}; - -use crate::database::models::app::AppRecord; -use crate::manager::app_status::AppStatus; - -/// Global notification dispatcher, registered via `register_notification` RPC. -static NOTIFICATION: OnceCell = OnceCell::new(); - -pub fn set_notification(url: String) { - let _ = NOTIFICATION.set(NotificationDispatcher { url }); -} - -pub fn get_notification() -> Option<&'static NotificationDispatcher> { - NOTIFICATION.get() -} - -/// Events emitted by the Rust manager layer to the Kotlin UI. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "type", rename_all = "snake_case")] -pub enum ManagerEvent { - AppStatusChanged { - record_id: String, - app_id: std::collections::HashMap>, - old_status: AppStatus, - new_status: AppStatus, - }, - RenewProgress { - done: usize, - total: usize, - }, - AppAdded { - record: AppRecord, - }, - AppDeleted { - record_id: String, - }, - AppDatabaseChanged { - record: AppRecord, - }, -} - -/// Dispatches manager events to the Kotlin UI layer via HTTP JSON-RPC. -/// -/// Kotlin registers a notification URL via `register_notification` RPC. -/// When an event fires, Rust POSTs `on_manager_event({event})` to that URL. -/// The Kotlin server handles it and updates the UI (ViewModel / LiveData). -pub struct NotificationDispatcher { - url: String, -} - -impl NotificationDispatcher { - /// Fire an event notification to Kotlin. Best-effort: errors are logged but not propagated. - pub async fn notify(&self, event: ManagerEvent) { - let body = match build_jsonrpc_request("on_manager_event", &event) { - Ok(b) => b, - Err(e) => { - eprintln!("NotificationDispatcher: failed to serialize event: {e}"); - return; - } - }; - - let client = reqwest::Client::new(); - match client - .post(&self.url) - .header("Content-Type", "application/json") - .body(body) - .send() - .await - { - Ok(_) => {} - Err(e) => { - eprintln!("NotificationDispatcher: failed to send notification: {e}"); - } - } - } -} - -fn build_jsonrpc_request( - method: &str, - params: &T, -) -> Result { - #[derive(Serialize)] - struct JsonRpcRequest<'a, P: Serialize> { - jsonrpc: &'a str, - method: &'a str, - params: &'a P, - id: u64, - } - serde_json::to_string(&JsonRpcRequest { - jsonrpc: "2.0", - method, - params, - id: 1, - }) -} - -/// Convenience: notify if a dispatcher is registered (no-op otherwise). -pub async fn notify_if_registered(event: ManagerEvent) { - if let Some(dispatcher) = get_notification() { - dispatcher.notify(event).await; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_event_serialization_status_changed() { - let event = ManagerEvent::AppStatusChanged { - record_id: "abc-123".to_string(), - app_id: std::collections::HashMap::from([( - "owner".to_string(), - Some("alice".to_string()), - )]), - old_status: AppStatus::AppPending, - new_status: AppStatus::AppOutdated, - }; - let json = serde_json::to_string(&event).unwrap(); - assert!(json.contains("app_status_changed")); - assert!(json.contains("abc-123")); - assert!(json.contains("app_outdated")); - } - - #[test] - fn test_event_serialization_renew_progress() { - let event = ManagerEvent::RenewProgress { done: 3, total: 10 }; - let json = serde_json::to_string(&event).unwrap(); - assert!(json.contains("renew_progress")); - assert!(json.contains("10")); - } - - #[test] - fn test_event_serialization_app_deleted() { - let event = ManagerEvent::AppDeleted { - record_id: "del-456".to_string(), - }; - let json = serde_json::to_string(&event).unwrap(); - assert!(json.contains("app_deleted")); - assert!(json.contains("del-456")); - } - - #[test] - fn test_jsonrpc_request_format() { - let event = ManagerEvent::RenewProgress { done: 1, total: 5 }; - let body = build_jsonrpc_request("on_manager_event", &event).unwrap(); - assert!(body.contains("\"jsonrpc\":\"2.0\"")); - assert!(body.contains("\"method\":\"on_manager_event\"")); - assert!(body.contains("renew_progress")); - } -} diff --git a/src/manager/updater.rs b/src/manager/updater.rs deleted file mode 100644 index ec2fd24..0000000 --- a/src/manager/updater.rs +++ /dev/null @@ -1,151 +0,0 @@ -use super::app_status::AppStatus; -use super::version::VersionInfo; -use super::version_map::{HubStatus, VersionMap}; -use std::collections::HashMap; - -/// Determines the release status for an app given its version map and local version. -/// -/// Mirrors Kotlin's `Updater.getReleaseStatus()`. -pub fn get_release_status( - version_map: &mut VersionMap, - local_version: Option<&str>, - ignore_version: Option<&str>, - is_saved: bool, -) -> AppStatus { - let versions = version_map.get_version_list(); - - if versions.is_empty() { - if version_map.is_renewing() { - return AppStatus::AppPending; - } - let all_error = !version_map.hub_status.is_empty() - && version_map - .hub_status - .values() - .all(|s| *s == HubStatus::Error); - if all_error || is_saved { - return AppStatus::NetworkError; - } - return AppStatus::AppInactive; - } - - let latest_name = &versions[0].version_info.name; - - // If the latest version matches what the user chose to ignore - if let Some(ignored) = ignore_version { - if ignored == latest_name { - return AppStatus::AppLatest; - } - } - - let effective_local = local_version.or(ignore_version); - - match effective_local { - None => AppStatus::AppNoLocal, - Some(local_str) => { - let local_info = VersionInfo::new(local_str, None, None, HashMap::new()); - let latest_info = &versions[0].version_info; - if is_latest(&local_info, latest_info) { - AppStatus::AppLatest - } else { - AppStatus::AppOutdated - } - } - } -} - -fn is_latest(local: &VersionInfo, latest: &VersionInfo) -> bool { - use std::cmp::Ordering; - match local.compare(latest) { - Some(Ordering::Greater) | Some(Ordering::Equal) => true, - Some(Ordering::Less) => false, - None => { - // Fallback: string equality check - local.name == latest.name - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::manager::version_map::VersionMap; - use crate::websdk::repo::data::release::{AssetData, ReleaseData}; - - fn release(v: &str) -> ReleaseData { - ReleaseData { - version_number: v.to_string(), - changelog: String::new(), - assets: vec![AssetData { - file_name: "app.apk".to_string(), - file_type: "apk".to_string(), - download_url: "https://x.com".to_string(), - }], - extra: None, - } - } - - fn vm_with(versions: &[&str]) -> VersionMap { - let mut vm = VersionMap::new(None, None); - vm.add_release_list("hub1", versions.iter().map(|v| release(v)).collect()); - vm - } - - #[test] - fn test_latest() { - let mut vm = vm_with(&["2.0.0", "1.0.0"]); - let status = get_release_status(&mut vm, Some("2.0.0"), None, true); - assert_eq!(status, AppStatus::AppLatest); - } - - #[test] - fn test_outdated() { - let mut vm = vm_with(&["2.0.0", "1.0.0"]); - let status = get_release_status(&mut vm, Some("1.0.0"), None, true); - assert_eq!(status, AppStatus::AppOutdated); - } - - #[test] - fn test_no_local() { - let mut vm = vm_with(&["2.0.0"]); - let status = get_release_status(&mut vm, None, None, true); - assert_eq!(status, AppStatus::AppNoLocal); - } - - #[test] - fn test_ignored_version() { - let mut vm = vm_with(&["2.0.0"]); - let status = get_release_status(&mut vm, None, Some("2.0.0"), true); - assert_eq!(status, AppStatus::AppLatest); - } - - #[test] - fn test_network_error() { - let mut vm = VersionMap::new(None, None); - vm.set_error("hub1"); - let status = get_release_status(&mut vm, Some("1.0.0"), None, true); - assert_eq!(status, AppStatus::NetworkError); - } - - #[test] - fn test_pending() { - let mut vm = VersionMap::new(None, None); - vm.mark_renewing("hub1"); - let status = get_release_status(&mut vm, Some("1.0.0"), None, true); - assert_eq!(status, AppStatus::AppPending); - } - - #[test] - fn test_inactive_unsaved() { - let mut vm = VersionMap::new(None, None); - let status = get_release_status(&mut vm, Some("1.0.0"), None, false); - assert_eq!(status, AppStatus::AppInactive); - } - - #[test] - fn test_local_newer_than_remote() { - let mut vm = vm_with(&["1.0.0"]); - let status = get_release_status(&mut vm, Some("2.0.0"), None, true); - assert_eq!(status, AppStatus::AppLatest); - } -} diff --git a/src/manager/url_replace.rs b/src/manager/url_replace.rs deleted file mode 100644 index e205d0e..0000000 --- a/src/manager/url_replace.rs +++ /dev/null @@ -1,170 +0,0 @@ -/// Applies URL replacement rules from an ExtraHub configuration. -/// -/// Mirrors Kotlin's `URLReplace.replaceURL()`. -/// -/// Three replacement modes: -/// 1. Plain regex: `replace(search_regex, replace_str)` across the full URL. -/// 2. Host-only: if `replace_str` looks like a bare host URL (no path), only -/// the host portion of the original URL is replaced. -/// 3. Template: if `replace_str` contains `{DOWNLOAD_URL}`, the original URL -/// is embedded as a parameter (e.g. proxy wrappers). -pub fn apply_url_replace(url: &str, search: Option<&str>, replace: Option<&str>) -> String { - let replace_str = match replace { - Some(r) if !r.is_empty() => r, - _ => return url.to_string(), - }; - - // Mode 3: template substitution — replace_str contains {DOWNLOAD_URL} - if replace_str.contains("{DOWNLOAD_URL}") { - return replace_str.replace("{DOWNLOAD_URL}", url); - } - - // Mode 2: host-only replacement — replace_str is a bare host URL (no path component) - if is_host_only(replace_str) { - return replace_host(url, replace_str); - } - - // Mode 1: plain regex replacement (or literal if no search) - match search { - Some(pattern) if !pattern.is_empty() => match regex::Regex::new(pattern) { - Ok(re) => re.replace_all(url, replace_str).into_owned(), - Err(_) => url.replace(pattern, replace_str), - }, - // No search pattern: nothing to replace - _ => url.to_string(), - } -} - -/// Returns true if `s` looks like a bare host URL with no meaningful path. -/// e.g. "https://mirror.example.com" or "https://mirror.example.com/" -fn is_host_only(s: &str) -> bool { - match url::Url::parse(s) { - Ok(u) => { - let path = u.path(); - path.is_empty() || path == "/" - } - Err(_) => false, - } -} - -/// Replace only the host (scheme + host + port) of `original_url` with the -/// host from `host_url`, keeping the original path, query and fragment. -fn replace_host(original_url: &str, host_url: &str) -> String { - let orig = match url::Url::parse(original_url) { - Ok(u) => u, - Err(_) => return original_url.to_string(), - }; - let host = match url::Url::parse(host_url) { - Ok(u) => u, - Err(_) => return original_url.to_string(), - }; - - // Rebuild: scheme + host from `host`, everything else from `orig` - let mut result = host.clone(); - result.set_path(orig.path()); - result.set_query(orig.query()); - result.set_fragment(orig.fragment()); - result.to_string() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_no_replace_returns_original() { - let url = "https://github.com/user/repo/releases/download/v1.0/app.apk"; - assert_eq!(apply_url_replace(url, None, None), url); - assert_eq!(apply_url_replace(url, None, Some("")), url); - } - - #[test] - fn test_download_url_template() { - let url = "https://github.com/user/repo/releases/download/v1.0/app.apk"; - let replace = "https://ghproxy.com/?url={DOWNLOAD_URL}"; - let result = apply_url_replace(url, None, Some(replace)); - assert_eq!( - result, - "https://ghproxy.com/?url=https://github.com/user/repo/releases/download/v1.0/app.apk" - ); - } - - #[test] - fn test_host_only_replacement() { - let url = "https://github.com/user/repo/releases/download/v1.0/app.apk"; - let result = apply_url_replace(url, None, Some("https://mirror.ghproxy.com")); - assert!(result.contains("mirror.ghproxy.com")); - assert!(result.contains("/user/repo/releases/download/v1.0/app.apk")); - assert!(!result.contains("github.com")); - } - - #[test] - fn test_host_only_with_trailing_slash() { - let url = "https://github.com/owner/repo/archive/v2.zip"; - let result = apply_url_replace(url, None, Some("https://mirror.example.com/")); - assert!(result.contains("mirror.example.com")); - assert!(result.contains("/owner/repo/archive/v2.zip")); - } - - #[test] - fn test_regex_replacement() { - let url = "https://github.com/user/repo/releases/download/v1.0/app.apk"; - let result = apply_url_replace(url, Some("github\\.com"), Some("github.com.cnpmjs.org")); - assert!(result.contains("github.com.cnpmjs.org")); - assert!(!result.contains("//github.com/")); - } - - #[test] - fn test_invalid_regex_falls_back_to_literal() { - let url = "https://github.com/user/repo"; - // Invalid regex pattern — should fall back to literal string replace - let result = apply_url_replace(url, Some("github.com"), Some("gitlab.com")); - assert!(result.contains("gitlab.com")); - } - - // ------------------------------------------------------------------------- - // Phase 8: chained GLOBAL + hub-specific rules (as applied in get_download) - // ------------------------------------------------------------------------- - - #[test] - fn test_global_then_hub_specific_chain() { - // Simulate: GLOBAL rule replaces github.com host, then hub rule wraps via template. - let url = "https://github.com/owner/repo/releases/download/v1.0/app.apk"; - - // Step 1 — GLOBAL rule: replace host with mirror - let after_global = apply_url_replace(url, None, Some("https://mirror.example.com")); - assert!(after_global.contains("mirror.example.com")); - assert!(!after_global.contains("github.com")); - - // Step 2 — hub-specific rule: wrap with proxy template - let after_hub = apply_url_replace( - &after_global, - None, - Some("https://proxy.example.com/?url={DOWNLOAD_URL}"), - ); - assert!(after_hub.starts_with("https://proxy.example.com/?url=")); - assert!(after_hub.contains("mirror.example.com")); - } - - #[test] - fn test_no_global_rule_hub_rule_applies() { - let url = "https://github.com/owner/repo/archive/v2.zip"; - - // GLOBAL has no replace rule — url unchanged - let after_global = apply_url_replace(url, None, None); - assert_eq!(after_global, url); - - // hub-specific regex rule - let after_hub = - apply_url_replace(&after_global, Some("github\\.com"), Some("gh.example.com")); - assert!(after_hub.contains("gh.example.com")); - } - - #[test] - fn test_both_rules_none_url_unchanged() { - let url = "https://github.com/owner/repo/releases/v1.apk"; - let after_global = apply_url_replace(url, None, None); - let after_hub = apply_url_replace(&after_global, None, None); - assert_eq!(after_hub, url); - } -} diff --git a/src/manager/version.rs b/src/manager/version.rs deleted file mode 100644 index 85547e7..0000000 --- a/src/manager/version.rs +++ /dev/null @@ -1,136 +0,0 @@ -use crate::utils::versioning::Version as VersionUtil; -use crate::websdk::repo::data::release::ReleaseData; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -/// A parsed, comparable version identifier. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct VersionInfo { - /// Normalized version name (regex-filtered) - pub name: String, - /// Extra metadata (e.g. version_code from Android) - pub extra: HashMap, -} - -impl VersionInfo { - pub fn new( - raw_name: &str, - invalid_regex: Option<&str>, - include_regex: Option<&str>, - extra: HashMap, - ) -> Self { - let name = normalize_version(raw_name, invalid_regex, include_regex); - Self { name, extra } - } - - /// Compare using libversion. Returns Some(Ordering) if both are parseable. - pub fn compare(&self, other: &VersionInfo) -> Option { - let v1 = VersionUtil::new(self.name.clone()); - let v2 = VersionUtil::new(other.name.clone()); - v1.partial_cmp(&v2) - } -} - -impl PartialEq for VersionInfo { - fn eq(&self, other: &Self) -> bool { - self.name == other.name - } -} - -impl Eq for VersionInfo {} - -impl std::hash::Hash for VersionInfo { - fn hash(&self, state: &mut H) { - self.name.hash(state); - } -} - -impl PartialOrd for VersionInfo { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for VersionInfo { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.compare(other).unwrap_or(std::cmp::Ordering::Less) - } -} - -/// Strip unwanted parts from a version string using optional regex filters. -fn normalize_version( - raw: &str, - invalid_regex: Option<&str>, - include_regex: Option<&str>, -) -> String { - let mut result = raw.to_string(); - - if let Some(pattern) = invalid_regex { - if let Ok(re) = regex::Regex::new(pattern) { - result = re.replace_all(&result, "").to_string(); - } - } - - if let Some(pattern) = include_regex { - if let Ok(re) = regex::Regex::new(pattern) { - let matched: Vec<&str> = re.find_iter(&result).map(|m| m.as_str()).collect(); - result = matched.join(""); - } - } - - result.trim().to_string() -} - -/// A release from one hub, paired with its assets. -#[derive(Debug, Clone)] -pub struct VersionWrapper { - pub hub_uuid: String, - pub release: ReleaseData, - /// (release_index, asset_index) pairs - pub asset_indices: Vec<(usize, usize)>, -} - -/// Snapshot of a single version with all hub-provided wrappers. -#[derive(Debug, Clone)] -pub struct Version { - pub version_info: VersionInfo, - pub wrappers: Vec, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_version_info_compare() { - let v1 = VersionInfo::new("1.0.0", None, None, HashMap::new()); - let v2 = VersionInfo::new("1.0.1", None, None, HashMap::new()); - assert!(v1 < v2); - assert!(v2 > v1); - } - - #[test] - fn test_version_info_equal() { - let v1 = VersionInfo::new("1.0.0", None, None, HashMap::new()); - let v2 = VersionInfo::new("1.0.0", None, None, HashMap::new()); - assert_eq!(v1, v2); - } - - #[test] - fn test_normalize_version_invalid_regex() { - let name = normalize_version("v1.0.0", Some("^v"), None); - assert_eq!(name, "1.0.0"); - } - - #[test] - fn test_normalize_version_include_regex() { - let name = normalize_version("Release 1.0.0 (stable)", None, Some(r"\d+\.\d+\.\d+")); - assert_eq!(name, "1.0.0"); - } - - #[test] - fn test_normalize_version_both_filters() { - let name = normalize_version("v1.0.0-beta", Some(r"-beta"), Some(r"\d+\.\d+\.\d+")); - assert_eq!(name, "1.0.0"); - } -} diff --git a/src/manager/version_map.rs b/src/manager/version_map.rs deleted file mode 100644 index 9d275cb..0000000 --- a/src/manager/version_map.rs +++ /dev/null @@ -1,192 +0,0 @@ -use std::collections::HashMap; - -use super::version::{Version, VersionInfo, VersionWrapper}; -use crate::websdk::repo::data::release::ReleaseData; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum HubStatus { - Renewing, - Error, - /// Got latest release only (single entry per hub) - Single, - /// Got full release list - Full, -} - -/// In-memory version data for a single app, keyed by VersionInfo. -/// -/// Mirrors Kotlin's `VersionMap`. -#[derive(Debug)] -pub struct VersionMap { - pub invalid_version_regex: Option, - pub include_version_regex: Option, - /// Aggregated releases, grouped by normalized VersionInfo - entries: HashMap>, - pub hub_status: HashMap, - /// Cached sorted list, invalidated on mutation - sorted_cache: Option>, -} - -impl VersionMap { - pub fn new(invalid_regex: Option, include_regex: Option) -> Self { - Self { - invalid_version_regex: invalid_regex, - include_version_regex: include_regex, - entries: HashMap::new(), - hub_status: HashMap::new(), - sorted_cache: None, - } - } - - pub fn is_renewing(&self) -> bool { - self.hub_status.values().any(|s| *s == HubStatus::Renewing) - } - - pub fn mark_renewing(&mut self, hub_uuid: &str) { - self.hub_status - .insert(hub_uuid.to_string(), HubStatus::Renewing); - self.sorted_cache = None; - } - - pub fn set_error(&mut self, hub_uuid: &str) { - self.hub_status - .insert(hub_uuid.to_string(), HubStatus::Error); - } - - pub fn add_release_list(&mut self, hub_uuid: &str, releases: Vec) { - for (rel_idx, release) in releases.iter().enumerate() { - let info = self.make_version_info(&release.version_number); - let wrapper = VersionWrapper { - hub_uuid: hub_uuid.to_string(), - release: release.clone(), - asset_indices: (0..release.assets.len()).map(|i| (rel_idx, i)).collect(), - }; - self.entries.entry(info).or_default().push(wrapper); - } - self.hub_status - .insert(hub_uuid.to_string(), HubStatus::Full); - self.sorted_cache = None; - } - - pub fn add_single_release(&mut self, hub_uuid: &str, release: ReleaseData) { - let info = self.make_version_info(&release.version_number); - let wrapper = VersionWrapper { - hub_uuid: hub_uuid.to_string(), - asset_indices: (0..release.assets.len()).map(|i| (0, i)).collect(), - release, - }; - self.entries.entry(info).or_default().push(wrapper); - self.hub_status - .insert(hub_uuid.to_string(), HubStatus::Single); - self.sorted_cache = None; - } - - /// Returns versions sorted descending (newest first). - pub fn get_version_list(&mut self) -> &[Version] { - if self.sorted_cache.is_none() { - let mut versions: Vec = self - .entries - .iter() - .filter(|(info, _)| !info.name.is_empty()) - .map(|(info, wrappers)| Version { - version_info: info.clone(), - wrappers: wrappers.clone(), - }) - .collect(); - versions.sort_by(|a, b| b.version_info.cmp(&a.version_info)); - self.sorted_cache = Some(versions); - } - self.sorted_cache.as_deref().unwrap() - } - - fn make_version_info(&self, raw: &str) -> VersionInfo { - VersionInfo::new( - raw, - self.invalid_version_regex.as_deref(), - self.include_version_regex.as_deref(), - HashMap::new(), - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::websdk::repo::data::release::AssetData; - - fn make_release(version: &str) -> ReleaseData { - ReleaseData { - version_number: version.to_string(), - changelog: String::new(), - assets: vec![AssetData { - file_name: "app.apk".to_string(), - file_type: "apk".to_string(), - download_url: "https://example.com".to_string(), - }], - extra: None, - } - } - - #[test] - fn test_add_and_sort() { - let mut vm = VersionMap::new(None, None); - vm.add_release_list( - "hub1", - vec![ - make_release("1.0.0"), - make_release("2.0.0"), - make_release("1.5.0"), - ], - ); - let list = vm.get_version_list(); - assert_eq!(list[0].version_info.name, "2.0.0"); - assert_eq!(list[1].version_info.name, "1.5.0"); - assert_eq!(list[2].version_info.name, "1.0.0"); - } - - #[test] - fn test_single_release() { - let mut vm = VersionMap::new(None, None); - vm.add_single_release("hub1", make_release("3.0.0")); - let list = vm.get_version_list(); - assert_eq!(list.len(), 1); - assert_eq!(list[0].version_info.name, "3.0.0"); - assert_eq!(vm.hub_status["hub1"], HubStatus::Single); - } - - #[test] - fn test_hub_status_error() { - let mut vm = VersionMap::new(None, None); - vm.set_error("hub1"); - assert_eq!(vm.hub_status["hub1"], HubStatus::Error); - } - - #[test] - fn test_is_renewing() { - let mut vm = VersionMap::new(None, None); - assert!(!vm.is_renewing()); - vm.mark_renewing("hub1"); - assert!(vm.is_renewing()); - vm.set_error("hub1"); - assert!(!vm.is_renewing()); - } - - #[test] - fn test_dedup_versions_across_hubs() { - let mut vm = VersionMap::new(None, None); - vm.add_single_release("hub1", make_release("1.0.0")); - vm.add_single_release("hub2", make_release("1.0.0")); - // Same version from two hubs → merged under one VersionInfo key - let list = vm.get_version_list(); - assert_eq!(list.len(), 1); - assert_eq!(list[0].wrappers.len(), 2); - } - - #[test] - fn test_regex_filtering() { - let mut vm = VersionMap::new(Some("^v".to_string()), None); - vm.add_single_release("hub1", make_release("v1.2.3")); - let list = vm.get_version_list(); - assert_eq!(list[0].version_info.name, "1.2.3"); - } -} diff --git a/src/rpc.rs b/src/rpc.rs deleted file mode 100644 index 162790c..0000000 --- a/src/rpc.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod client; -pub mod data; -pub mod server; diff --git a/src/rpc/client.rs b/src/rpc/client.rs deleted file mode 100644 index 94c6091..0000000 --- a/src/rpc/client.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::websdk::repo::data::release::ReleaseData; - -use super::data::*; -use jsonrpsee::core::client::ClientT; -use jsonrpsee::core::client::Error; -use jsonrpsee::http_client::HttpClient; -use jsonrpsee::http_client::HttpClientBuilder; -use std::collections::BTreeMap; - -pub struct Client { - client: HttpClient, -} - -impl Client { - pub fn new(url: impl AsRef) -> Result { - let client = HttpClientBuilder::default().build(url)?; - Ok(Self { client }) - } - - pub async fn check_app_available( - &self, - hub_uuid: &str, - app_data: BTreeMap<&str, &str>, - hub_data: BTreeMap<&str, &str>, - ) -> Result { - let data = RpcAppRequest { - hub_uuid, - app_data, - hub_data, - }; - self.client.request("check_app_available", data).await - } - - pub async fn get_latest_release( - &self, - hub_uuid: &str, - app_data: BTreeMap<&str, &str>, - hub_data: BTreeMap<&str, &str>, - ) -> Result { - let data = RpcAppRequest { - hub_uuid, - app_data, - hub_data, - }; - self.client.request("get_latest_release", data).await - } - - pub async fn get_releases( - &self, - hub_uuid: &str, - app_data: BTreeMap<&str, &str>, - hub_data: BTreeMap<&str, &str>, - ) -> Result, Error> { - let data = RpcAppRequest { - hub_uuid, - app_data, - hub_data, - }; - self.client.request("get_releases", data).await - } - - pub async fn get_download( - &self, - hub_uuid: &str, - app_data: BTreeMap<&str, &str>, - hub_data: BTreeMap<&str, &str>, - asset_index: &[i32], - ) -> Result, Error> { - let data = RpcDownloadInfoRequest { - hub_uuid, - app_data, - hub_data, - asset_index: asset_index.to_vec(), - }; - self.client.request("get_download", data).await - } -} diff --git a/src/rpc/data.rs b/src/rpc/data.rs deleted file mode 100644 index 77f3a88..0000000 --- a/src/rpc/data.rs +++ /dev/null @@ -1,411 +0,0 @@ -use crate::downloader::{DownloadState, TaskInfo}; -use jsonrpsee::core::traits::ToRpcParams; -use serde::{Deserialize, Serialize}; -use serde_json::value::to_raw_value; -use std::collections::{BTreeMap, HashMap}; - -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcInitRequest<'a> { - pub data_path: &'a str, - pub cache_path: &'a str, - pub global_expire_time: u64, -} - -impl ToRpcParams for RpcInitRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcAppRequest<'a> { - pub hub_uuid: &'a str, - pub app_data: BTreeMap<&'a str, &'a str>, - pub hub_data: BTreeMap<&'a str, &'a str>, -} - -impl ToRpcParams for RpcAppRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcCloudConfigRequest<'a> { - pub api_url: &'a str, -} - -impl ToRpcParams for RpcCloudConfigRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -// ============================================================================ -// Provider Registration RPC Data Structures -// ============================================================================ - -/// Request to register an external provider -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcRegisterProviderRequest<'a> { - pub hub_uuid: &'a str, - pub url: &'a str, -} - -impl ToRpcParams for RpcRegisterProviderRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to get download info for an app -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDownloadInfoRequest<'a> { - pub hub_uuid: &'a str, - pub app_data: BTreeMap<&'a str, &'a str>, - pub hub_data: BTreeMap<&'a str, &'a str>, - pub asset_index: Vec, -} - -impl ToRpcParams for RpcDownloadInfoRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Download item data returned by get_download -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DownloadItemData { - pub name: Option, - pub url: String, - #[serde(default)] - pub headers: Option>, - #[serde(default)] - pub cookies: Option>, -} - -// ============================================================================ -// Downloader RPC Data Structures -// ============================================================================ - -/// Request to submit a download task -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDownloadRequest<'a> { - pub url: &'a str, - pub dest_path: &'a str, - #[serde(default)] - pub headers: Option>, - #[serde(default)] - pub cookies: Option>, - /// Hub UUID for routing to registered external downloaders. - /// When set, routes to external downloader; when None, uses default HTTP downloader. - #[serde(default)] - pub hub_uuid: Option, -} - -impl ToRpcParams for RpcDownloadRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to submit multiple download tasks -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDownloadBatchRequest { - pub tasks: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDownloadTask { - pub url: String, - pub dest_path: String, -} - -impl ToRpcParams for RpcDownloadBatchRequest { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Response with task ID -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct RpcTaskIdResponse { - pub task_id: String, -} - -/// Response with multiple task IDs -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct RpcTaskIdsResponse { - pub task_ids: Vec, -} - -/// Request to query task status -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcTaskStatusRequest<'a> { - pub task_id: &'a str, -} - -impl ToRpcParams for RpcTaskStatusRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to wait for task state change (long-polling) -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcWaitForChangeRequest<'a> { - pub task_id: &'a str, - pub timeout_seconds: u64, -} - -impl ToRpcParams for RpcWaitForChangeRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to cancel a task -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcCancelTaskRequest<'a> { - pub task_id: &'a str, -} - -impl ToRpcParams for RpcCancelTaskRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to pause a task -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcPauseTaskRequest<'a> { - pub task_id: &'a str, -} - -impl ToRpcParams for RpcPauseTaskRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to resume a task -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcResumeTaskRequest<'a> { - pub task_id: &'a str, -} - -impl ToRpcParams for RpcResumeTaskRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to query tasks by state -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcTasksByStateRequest { - pub state: DownloadState, -} - -impl ToRpcParams for RpcTasksByStateRequest { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Response with task information -pub type RpcTaskInfoResponse = TaskInfo; - -/// Response with multiple tasks -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct RpcTasksResponse { - pub tasks: Vec, -} - -// ============================================================================ -// Downloader Registration RPC Data Structures -// ============================================================================ - -/// Request to register an external downloader for a hub_uuid -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcRegisterDownloaderRequest<'a> { - pub hub_uuid: &'a str, - pub rpc_url: &'a str, -} - -impl ToRpcParams for RpcRegisterDownloaderRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to unregister an external downloader for a hub_uuid -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcUnregisterDownloaderRequest<'a> { - pub hub_uuid: &'a str, -} - -impl ToRpcParams for RpcUnregisterDownloaderRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -// ============================================================================ -// Manager RPC Data Structures -// ============================================================================ - -use crate::database::models::app::AppRecord; -use crate::database::models::extra_hub::ExtraHubRecord; -use crate::database::models::hub::HubRecord; -use crate::manager::app_status::AppStatus; - -/// Response wrapping a list of apps with their current status. -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct AppWithStatus { - pub record: AppRecord, - pub status: AppStatus, -} - -/// Request to save/update an app record. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSaveAppRequest { - pub record: AppRecord, -} - -/// Request to delete an app by record id. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDeleteAppRequest { - pub record_id: String, -} - -/// Request to get a single app by record id. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcGetAppRequest { - pub record_id: String, -} - -/// Request to save/update a hub record. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSaveHubRequest { - pub record: HubRecord, -} - -/// Request to delete a hub by UUID. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDeleteHubRequest { - pub hub_uuid: String, -} - -/// Request to get a hub by UUID. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcGetHubRequest { - pub hub_uuid: String, -} - -/// Request to set the applications mode for a hub. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSetApplicationsModeRequest { - pub hub_uuid: String, - pub enable: bool, -} - -/// Request to ignore/unignore an app in a hub. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcHubIgnoreAppRequest { - pub hub_uuid: String, - pub app_id: HashMap>, - pub ignore: bool, -} - -/// Request to set virtual (installed) apps list from Android. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSetVirtualAppsRequest { - pub apps: Vec, -} - -/// Request to get app status. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcGetAppStatusRequest { - pub record_id: String, -} - -/// Request to save/update an ExtraHub record. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSaveExtraHubRequest { - pub record: ExtraHubRecord, -} - -/// Request to get an ExtraHub by id. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcGetExtraHubRequest { - pub id: String, -} - -// ============================================================================ -// Android API / Notification Registration RPC Data Structures -// ============================================================================ - -/// Request to register the Kotlin Android API callback URL. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcRegisterAndroidApiRequest { - pub url: String, -} - -/// Request to register the Kotlin notification callback URL. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcRegisterNotificationRequest { - pub url: String, -} - -// ============================================================================ -// ExtraApp RPC Data Structures -// ============================================================================ - -use crate::database::models::extra_app::ExtraAppRecord; - -/// Request to get an ExtraApp record by app_id map. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcGetExtraAppRequest { - pub app_id: HashMap>, -} - -/// Request to save/update an ExtraApp record. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSaveExtraAppRequest { - pub record: ExtraAppRecord, -} - -/// Request to delete an ExtraApp by database id. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDeleteExtraAppRequest { - pub id: String, -} - -// ============================================================================ -// Cloud Config Manager RPC Data Structures -// ============================================================================ - -/// Request to apply a specific cloud hub/app config by UUID. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcCloudConfigApplyRequest { - pub uuid: String, -} - -/// Request to initialise the CloudConfigGetter with an API URL. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcCloudConfigInitRequest { - pub api_url: String, -} - -/// Request to update the auth map for a hub. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcUpdateHubAuthRequest { - pub hub_uuid: String, - pub auth: HashMap, -} - -impl ToRpcParams for RpcUpdateHubAuthRequest { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} diff --git a/src/rpc/server.rs b/src/rpc/server.rs deleted file mode 100644 index 6e1e54f..0000000 --- a/src/rpc/server.rs +++ /dev/null @@ -1,1255 +0,0 @@ -use super::data::*; -use crate::cache::init_cache_manager_with_expire; -use crate::core::config::world::{init_world_list, world_list}; -use crate::database::get_db; -use crate::database::models::extra_hub::GLOBAL_HUB_ID; -use crate::downloader::{DownloadConfig, DownloadTaskManager}; -use crate::manager::android_api; -use crate::manager::app_manager::AppManager; -use crate::manager::cloud_config_getter::CloudConfigGetter; -use crate::manager::hub_manager::HubManager; -use crate::manager::notification; -use crate::manager::url_replace::apply_url_replace; -use crate::websdk::cloud_rules::cloud_rules_manager::CloudRules; -use crate::websdk::repo::api; -use jsonrpsee::server::{RpcModule, Server, ServerConfig, ServerHandle}; -use jsonrpsee::types::{ErrorCode, ErrorObjectOwned}; -use once_cell::sync::OnceCell; -use std::net::SocketAddr; -use std::path::Path; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; -use std::time::Duration; -use tokio::sync::RwLock; - -/// Global manager state initialised on first `init` RPC call. -static APP_MANAGER: OnceCell>> = OnceCell::new(); -static HUB_MANAGER: OnceCell>> = OnceCell::new(); -static CLOUD_CONFIG_GETTER: OnceCell>> = OnceCell::new(); - -fn get_app_manager() -> Option>> { - APP_MANAGER.get().cloned() -} - -fn get_hub_manager() -> Option>> { - HUB_MANAGER.get().cloned() -} - -fn get_cloud_config_getter() -> Option>> { - CLOUD_CONFIG_GETTER.get().cloned() -} - -fn manager_not_init_err() -> ErrorObjectOwned { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Manager not initialized. Call init first.", - None::, - ) -} - -fn map_manager_err(e: impl std::fmt::Display) -> ErrorObjectOwned { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - e.to_string(), - None::, - ) -} - -// Default 2GB size limit for WebSocket messages -// Can be overridden at runtime by setting GETTER_WS_MAX_MESSAGE_SIZE environment variable -// Example: GETTER_WS_MAX_MESSAGE_SIZE=1073741824 ./getter (for 1GB) -const DEFAULT_MAX_SIZE: u32 = 2 * 1024 * 1024 * 1024; // 2GB - -fn get_max_message_size() -> u32 { - // Allow runtime configuration via environment variable - match std::env::var("GETTER_WS_MAX_MESSAGE_SIZE") { - Ok(size_str) => size_str.parse().unwrap_or(DEFAULT_MAX_SIZE), - Err(_) => DEFAULT_MAX_SIZE, - } -} - -pub async fn run_server( - addr: &str, - is_running: Arc, -) -> Result<(String, ServerHandle), Box> { - let addr = if addr.is_empty() { "127.0.0.1:0" } else { addr }; - let max_size = get_max_message_size(); - let config = ServerConfig::builder() - .max_request_body_size(max_size) - .max_response_body_size(max_size) - .build(); - let server = Server::builder() - .set_config(config) - .build(addr.parse::()?) - .await?; - let mut module = RpcModule::new(()); - // Register the shutdown method - let run_flag = is_running.clone(); - module.register_async_method("shutdown", move |_, _, _| { - let flag = run_flag.clone(); - async move { - flag.store(false, Ordering::SeqCst); - } - })?; - module.register_method("ping", |_, _, _| "pong")?; - module.register_async_method("init", |params, _, _| async move { - let request = params.parse::()?; - let data_dir = Path::new(request.data_path); - let cache_dir = Path::new(request.cache_path); - // Initialize world list, cache, and database. - let world_list_path = data_dir.join(world_list::WORLD_CONFIG_LIST_NAME); - init_world_list(&world_list_path).await.map_err(|e| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Internal error", - Some(e.to_string()), - ) - })?; - let local_cache_path = cache_dir.join("local_cache"); - init_cache_manager_with_expire(local_cache_path.as_path(), request.global_expire_time) - .await; - crate::database::init_db(data_dir).map_err(|e| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Internal error", - Some(e.to_string()), - ) - })?; - - // Initialize managers (idempotent: only on first call) - if APP_MANAGER.get().is_none() { - let hub_mgr = HubManager::load().map_err(map_manager_err)?; - let app_mgr = AppManager::load().map_err(map_manager_err)?; - let _ = HUB_MANAGER.set(Arc::new(RwLock::new(hub_mgr))); - let _ = APP_MANAGER.set(Arc::new(RwLock::new(app_mgr))); - } - - Ok::(true) - })?; - module.register_async_method( - "check_app_available", - |params, _context, _extensions| async move { - let request = params.parse::()?; - let result = - api::check_app_available(request.hub_uuid, &request.app_data, &request.hub_data) - .await - .unwrap_or(false); - Ok::(result) - }, - )?; - module.register_async_method( - "get_latest_release", - |params, _context, _extensions| async move { - let request = params.parse::()?; - if let Some(result) = - api::get_latest_release(request.hub_uuid, &request.app_data, &request.hub_data) - .await - { - Ok(result) - } else { - Err(ErrorObjectOwned::owned( - -32001, - "No release found", - None::, - )) - } - }, - )?; - module.register_async_method("get_releases", |params, _context, _extensions| async move { - let request = params.parse::()?; - if let Some(result) = - api::get_releases(request.hub_uuid, &request.app_data, &request.hub_data).await - { - Ok(result) - } else { - Err(ErrorObjectOwned::owned( - -32001, - "No releases found", - None::, - )) - } - })?; - - // register_provider: Dynamically register an external provider (e.g., Kotlin hub via HTTP JSON-RPC) - module.register_async_method( - "register_provider", - |params, _context, _extensions| async move { - let request = params.parse::()?; - api::add_outside_provider(request.hub_uuid, request.url); - Ok::(true) - }, - )?; - - // get_download: Get download info for an app's asset. - // After retrieving download URLs from the provider, applies URL replacement - // rules from ExtraHub configs (GLOBAL first, then hub-specific), mirroring - // Kotlin's URLReplace.replaceURL() in the download pipeline. - module.register_async_method("get_download", |params, _context, _extensions| async move { - let request = params.parse::()?; - let mut items = api::get_download( - request.hub_uuid, - &request.app_data, - &request.hub_data, - &request.asset_index, - ) - .await - .ok_or_else(|| ErrorObjectOwned::owned(-32001, "No download info found", None::))?; - - // Load URL-replace rules from ExtraHub configs. - // Priority: hub-specific rule overrides GLOBAL rule. - let db = get_db(); - let global_extra = db.find_extra_hub(GLOBAL_HUB_ID).unwrap_or(None); - let hub_extra = db.find_extra_hub(request.hub_uuid).unwrap_or(None); - - // Apply rules to every download URL in the result. - for item in &mut items { - // Apply GLOBAL rule first (lower priority) - if let Some(ref g) = global_extra { - item.url = apply_url_replace( - &item.url, - g.url_replace_search.as_deref(), - g.url_replace_string.as_deref(), - ); - } - // Apply hub-specific rule second (higher priority, may override) - if let Some(ref h) = hub_extra { - item.url = apply_url_replace( - &item.url, - h.url_replace_search.as_deref(), - h.url_replace_string.as_deref(), - ); - } - } - - Ok::, ErrorObjectOwned>(items) - })?; - - module.register_async_method( - "get_cloud_config", - |params, _context, _extensions| async move { - if let Ok(request) = params.parse::() { - let mut cloud_rules = CloudRules::new(request.api_url); - if let Err(e) = cloud_rules.renew().await { - return Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Download cloud config failed", - Some(e.to_string()), - )); - } - Ok(cloud_rules.get_config_list().to_owned()) - } else { - Err(ErrorObjectOwned::owned( - ErrorCode::ParseError.code(), - "Parse params error", - Some(params.as_str().unwrap_or("None").to_string()), - )) - } - }, - )?; - - // ======================================================================== - // Downloader RPC Methods - // ======================================================================== - - // Create download task manager with HubDispatchDownloader - let download_config = DownloadConfig::from_env(); - let http_downloader = crate::downloader::create_downloader(&download_config); - let dispatcher = crate::downloader::HubDispatchDownloader::new(http_downloader); - - // Clone dispatcher for task manager (HubDispatchDownloader is cheap to clone via Arc internally) - let task_manager = Arc::new(DownloadTaskManager::new(Box::new(dispatcher.clone()))); - - // download_submit: Submit a single download task - let manager_clone = task_manager.clone(); - module.register_async_method("download_submit", move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - match manager.submit_task_with_options( - request.url, - request.dest_path, - request.headers, - request.cookies, - request.hub_uuid, - ) { - Ok(task_id) => Ok(RpcTaskIdResponse { task_id }), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Failed to submit download task", - Some(e.message), - )), - } - } - })?; - - // download_submit_batch: Submit multiple download tasks - let manager_clone = task_manager.clone(); - module.register_async_method( - "download_submit_batch", - move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - let tasks: Vec<(String, String)> = request - .tasks - .into_iter() - .map(|t| (t.url, t.dest_path)) - .collect(); - - match manager.submit_batch(tasks) { - Ok(task_ids) => Ok(RpcTaskIdsResponse { task_ids }), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Failed to submit batch download tasks", - Some(e.message), - )), - } - } - }, - )?; - - // download_get_status: Get status of a download task - let manager_clone = task_manager.clone(); - module.register_async_method( - "download_get_status", - move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - match manager.get_task(request.task_id) { - Ok(task_info) => Ok(task_info), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InvalidParams.code(), - "Task not found", - Some(e.message), - )), - } - } - }, - )?; - - // download_wait_for_change: Long-polling for task state change - let manager_clone = task_manager.clone(); - module.register_async_method( - "download_wait_for_change", - move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - let timeout = Duration::from_secs(request.timeout_seconds); - - match manager.wait_for_change(request.task_id, timeout).await { - Ok(task_info) => Ok(task_info), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InvalidParams.code(), - "Failed to wait for task change", - Some(e.message), - )), - } - } - }, - )?; - - // download_cancel: Cancel a download task - let manager_clone = task_manager.clone(); - module.register_async_method("download_cancel", move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - match manager.cancel_task(request.task_id) { - Ok(_) => Ok(true), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Failed to cancel task", - Some(e.message), - )), - } - } - })?; - - // download_pause: Pause a download task - let manager_clone = task_manager.clone(); - module.register_async_method("download_pause", move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - match manager.pause_task(request.task_id).await { - Ok(_) => Ok(true), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Failed to pause task", - Some(e.message), - )), - } - } - })?; - - // download_resume: Resume a paused download task - let manager_clone = task_manager.clone(); - module.register_async_method("download_resume", move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - match manager.resume_task(request.task_id).await { - Ok(_) => Ok(true), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Failed to resume task", - Some(e.message), - )), - } - } - })?; - - // download_get_capabilities: Get downloader capabilities - let manager_clone = task_manager.clone(); - module.register_method( - "download_get_capabilities", - move |_, _context, _extensions| { - let caps = manager_clone.get_capabilities(); - Ok::<_, ErrorObjectOwned>(caps.clone()) - }, - )?; - - // download_get_all_tasks: Get all tasks - let manager_clone = task_manager.clone(); - module.register_async_method("download_get_all_tasks", move |_, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - Ok::(RpcTasksResponse { - tasks: manager.get_all_tasks(), - }) - } - })?; - - // download_get_active_tasks: Get active tasks - let manager_clone = task_manager.clone(); - module.register_async_method( - "download_get_active_tasks", - move |_, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - Ok::(RpcTasksResponse { - tasks: manager.get_active_tasks(), - }) - } - }, - )?; - - // download_get_tasks_by_state: Get tasks by state - let manager_clone = task_manager.clone(); - module.register_async_method( - "download_get_tasks_by_state", - move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - Ok::(RpcTasksResponse { - tasks: manager.get_tasks_by_state(request.state), - }) - } - }, - )?; - - // register_downloader: Register an external downloader for a hub_uuid - let dispatcher_clone = dispatcher.clone(); - module.register_async_method( - "register_downloader", - move |params, _context, _extensions| { - let dispatcher = dispatcher_clone.clone(); - async move { - let request = params.parse::()?; - let external_downloader = Box::new(crate::downloader::ExternalRpcDownloader::new( - request.rpc_url.to_string(), - )); - dispatcher.register(request.hub_uuid, external_downloader); - Ok::(true) - } - }, - )?; - - // unregister_downloader: Unregister an external downloader for a hub_uuid - let dispatcher_clone = dispatcher.clone(); - module.register_async_method( - "unregister_downloader", - move |params, _context, _extensions| { - let dispatcher = dispatcher_clone.clone(); - async move { - let request = params.parse::()?; - dispatcher.unregister(request.hub_uuid); - Ok::(true) - } - }, - )?; - - // ======================================================================== - // App Manager RPC Methods - // ======================================================================== - - // manager_get_apps: Get all saved apps - module.register_async_method("manager_get_apps", |_, _, _| async move { - let mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let apps = mgr.read().await.get_saved_apps().await; - Ok::, ErrorObjectOwned>(apps) - })?; - - // manager_save_app: Insert or update an app record - module.register_async_method("manager_save_app", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let saved = mgr - .write() - .await - .save_app(request.record) - .await - .map_err(map_manager_err)?; - Ok::(saved) - })?; - - // manager_delete_app: Delete an app by record id - module.register_async_method("manager_delete_app", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let deleted = mgr - .write() - .await - .remove_app(&request.record_id) - .await - .map_err(map_manager_err)?; - Ok::(deleted) - })?; - - // manager_get_app_status: Get AppStatus for a specific app - module.register_async_method("manager_get_app_status", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let status = mgr.write().await.get_app_status(&request.record_id).await; - Ok::(status) - })?; - - // manager_set_virtual_apps: Set installed (virtual) apps from Android - module.register_async_method("manager_set_virtual_apps", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - mgr.read().await.set_virtual_apps(request.apps).await; - Ok::(true) - })?; - - // manager_renew_all: Trigger a full update check for all apps - module.register_async_method("manager_renew_all", |_, _, _| async move { - let app_mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let hub_mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let hubs = hub_mgr.read().await.get_hub_list().await; - app_mgr.read().await.renew_all(&hubs, None).await; - Ok::(true) - })?; - - // manager_check_invalid_applications: Return record IDs of apps whose configured - // hub UUIDs are all unknown (no valid hub found). Mirrors Kotlin's - // AppManager.check_invalid_applications logic. - module.register_async_method("manager_check_invalid_applications", |_, _, _| async move { - let app_mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let hub_mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let hubs = hub_mgr.read().await.get_hub_list().await; - let known_uuids: Vec = hubs.into_iter().map(|h| h.uuid).collect(); - let mgr = app_mgr.read().await; - let invalid_ids = mgr.check_invalid_applications(&known_uuids).await; - // Notify Kotlin UI about each invalid app so it can update status. - for record_id in &invalid_ids { - if let Some(app) = mgr.get_app(record_id).await { - notification::notify_if_registered(notification::ManagerEvent::AppStatusChanged { - record_id: record_id.clone(), - app_id: app.app_id.clone(), - old_status: crate::manager::app_status::AppStatus::AppLatest, - new_status: crate::manager::app_status::AppStatus::AppInactive, - }) - .await; - } - } - Ok::, ErrorObjectOwned>(invalid_ids) - })?; - - // ======================================================================== - // Hub Manager RPC Methods - // ======================================================================== - - // manager_get_hubs: Get all hubs - module.register_async_method("manager_get_hubs", |_, _, _| async move { - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let hubs = mgr.read().await.get_hub_list().await; - Ok::, ErrorObjectOwned>(hubs) - })?; - - // manager_save_hub: Insert or update a hub - module.register_async_method("manager_save_hub", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - mgr.write() - .await - .upsert_hub(request.record) - .await - .map_err(map_manager_err)?; - Ok::(true) - })?; - - // manager_update_hub_auth: Replace the auth map for a hub and persist. - module.register_async_method("manager_update_hub_auth", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let updated = mgr - .read() - .await - .update_auth(&request.hub_uuid, request.auth) - .await - .map_err(map_manager_err)?; - Ok::(updated) - })?; - - // manager_delete_hub: Delete a hub by UUID - module.register_async_method("manager_delete_hub", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let deleted = mgr - .write() - .await - .remove_hub(&request.hub_uuid) - .await - .map_err(map_manager_err)?; - Ok::(deleted) - })?; - - // manager_hub_ignore_app: Add or remove an app from a hub's ignore list - module.register_async_method("manager_hub_ignore_app", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let mut guard = mgr.write().await; - let mut hub = guard.get_hub(&request.hub_uuid).await.ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InvalidParams.code(), - "Hub not found", - None::, - ) - })?; - if request.ignore { - if !hub.user_ignore_app_id_list.contains(&request.app_id) { - hub.user_ignore_app_id_list.push(request.app_id); - } - } else { - hub.user_ignore_app_id_list - .retain(|id| id != &request.app_id); - } - guard.upsert_hub(hub).await.map_err(map_manager_err)?; - Ok::(true) - })?; - - // manager_set_applications_mode: Enable/disable auto app discovery for a hub - module.register_async_method("manager_set_applications_mode", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let mut guard = mgr.write().await; - let mut hub = guard.get_hub(&request.hub_uuid).await.ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InvalidParams.code(), - "Hub not found", - None::, - ) - })?; - hub.applications_mode = if request.enable { 1 } else { 0 }; - guard.upsert_hub(hub).await.map_err(map_manager_err)?; - Ok::(true) - })?; - - // ======================================================================== - // ExtraHub RPC Methods - // ======================================================================== - - // manager_get_extra_hubs: Get all extra hub configs - module.register_async_method("manager_get_extra_hubs", |_, _, _| async move { - let extra_hubs = crate::database::get_db() - .load_extra_hubs() - .map_err(map_manager_err)?; - Ok::, ErrorObjectOwned>(extra_hubs) - })?; - - // manager_save_extra_hub: Insert or update an extra hub config - module.register_async_method("manager_save_extra_hub", |params, _, _| async move { - let request = params.parse::()?; - crate::database::get_db() - .upsert_extra_hub(&request.record) - .map_err(map_manager_err)?; - Ok::(true) - })?; - - // manager_delete_extra_hub: Delete an extra hub by id - module.register_async_method("manager_delete_extra_hub", |params, _, _| async move { - let request = params.parse::()?; - let deleted = crate::database::get_db() - .delete_extra_hub(&request.id) - .map_err(map_manager_err)?; - Ok::(deleted) - })?; - - // ======================================================================== - // Android API / Notification Registration RPC Methods - // ======================================================================== - - // register_android_api: Register Kotlin's Android API callback URL - module.register_async_method("register_android_api", |params, _, _| async move { - let request = params.parse::()?; - android_api::set_android_api(request.url); - Ok::(true) - })?; - - // register_notification: Register Kotlin's notification callback URL - module.register_async_method("register_notification", |params, _, _| async move { - let request = params.parse::()?; - notification::set_notification(request.url); - Ok::(true) - })?; - - // ======================================================================== - // ExtraApp RPC Methods - // ======================================================================== - - // manager_get_extra_app_by_app_id: Get ExtraApp record by app_id map - module.register_async_method( - "manager_get_extra_app_by_app_id", - |params, _, _| async move { - let request = params.parse::()?; - let record = crate::database::get_db() - .get_extra_app_by_app_id(&request.app_id) - .map_err(map_manager_err)?; - Ok::, ErrorObjectOwned>( - record, - ) - }, - )?; - - // manager_save_extra_app: Insert or update an ExtraApp record - module.register_async_method("manager_save_extra_app", |params, _, _| async move { - let request = params.parse::()?; - crate::database::get_db() - .upsert_extra_app(&request.record) - .map_err(map_manager_err)?; - Ok::(true) - })?; - - // manager_delete_extra_app: Delete an ExtraApp by database id - module.register_async_method("manager_delete_extra_app", |params, _, _| async move { - let request = params.parse::()?; - let deleted = crate::database::get_db() - .delete_extra_app(&request.id) - .map_err(map_manager_err)?; - Ok::(deleted) - })?; - - // ======================================================================== - // Cloud Config Manager RPC Methods - // ======================================================================== - - // cloud_config_init: Initialise or re-initialise the CloudConfigGetter with an API URL - module.register_async_method("cloud_config_init", |params, _, _| async move { - let request = params.parse::()?; - let getter = CloudConfigGetter::new(request.api_url); - let _ = CLOUD_CONFIG_GETTER.set(Arc::new(RwLock::new(getter))); - Ok::(true) - })?; - - // cloud_config_renew: Download and cache the latest cloud config - module.register_async_method("cloud_config_renew", |_, _, _| async move { - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised. Call cloud_config_init first.", - None::, - ) - })?; - getter.read().await.renew().await.map_err(map_manager_err)?; - Ok::(true) - })?; - - // cloud_config_get_app_list: Return all available app configs from cache - module.register_async_method("cloud_config_get_app_list", |_, _, _| async move { - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised.", - None::, - ) - })?; - let list = getter.read().await.app_config_list().await; - Ok::, ErrorObjectOwned>(list) - })?; - - // cloud_config_get_hub_list: Return all available hub configs from cache - module.register_async_method("cloud_config_get_hub_list", |_, _, _| async move { - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised.", - None::, - ) - })?; - let list = getter.read().await.hub_config_list().await; - Ok::, ErrorObjectOwned>(list) - })?; - - // cloud_config_apply_app: Apply a cloud app config by UUID - module.register_async_method("cloud_config_apply_app", |params, _, _| async move { - let request = params.parse::()?; - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised.", - None::, - ) - })?; - let app_mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let hub_mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - getter - .read() - .await - .apply_app_config( - &request.uuid, - &mut *app_mgr.write().await, - &mut *hub_mgr.write().await, - ) - .await - .map_err(map_manager_err)?; - Ok::(true) - })?; - - // cloud_config_apply_hub: Apply a cloud hub config by UUID - module.register_async_method("cloud_config_apply_hub", |params, _, _| async move { - let request = params.parse::()?; - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised.", - None::, - ) - })?; - let hub_mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - getter - .read() - .await - .apply_hub_config(&request.uuid, &mut *hub_mgr.write().await) - .await - .map_err(map_manager_err)?; - Ok::(true) - })?; - - // cloud_config_renew_all: Bulk-update all installed apps/hubs from cloud - module.register_async_method("cloud_config_renew_all", |_, _, _| async move { - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised.", - None::, - ) - })?; - let app_mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let hub_mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - getter - .read() - .await - .renew_all_from_cloud(&mut *app_mgr.write().await, &mut *hub_mgr.write().await) - .await - .map_err(map_manager_err)?; - Ok::(true) - })?; - - let addr = server.local_addr()?; - let handle = server.start(module); - tokio::spawn(handle.clone().stopped()); - Ok((format!("http://{}", addr), handle)) -} - -#[allow(dead_code)] -pub async fn run_server_hanging( - addr: &str, - callback: impl Fn(&str) -> Result>, -) -> Result> { - let is_running = Arc::new(AtomicBool::new(true)); - let (url, handle) = match run_server(addr, is_running.clone()).await { - Ok((url, handle)) => (url, handle), - Err(e) => { - eprintln!("Failed to start server: {}", e); - return Err(e); - } - }; - let result = callback(&url)?; - while is_running.load(Ordering::SeqCst) { - tokio::time::sleep(Duration::from_secs(1)).await; - } - handle.stop()?; - Ok(result) -} - -#[cfg(test)] -mod tests { - use crate::rpc::client::Client; - use crate::websdk::repo::provider::github; - use crate::websdk::{ - cloud_rules::data::config_list::ConfigList, repo::data::release::ReleaseData, - }; - - use super::*; - use jsonrpsee::{core::client::ClientT, http_client::HttpClientBuilder, rpc_params}; - use mockito::Server; - use std::collections::BTreeMap; - use std::fs; - use tokio::time::timeout; - - #[tokio::test] - async fn test_server_start() { - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - assert!(url.starts_with("http://")); - assert!(url.split(":").last().unwrap().parse::().unwrap() > 0); - handle.stop().unwrap(); - let port = 33333; - let addr = format!("127.0.0.1:{}", port); - let (url, handle) = run_server(&addr, Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - assert!(url.starts_with("http://")); - assert!(url.split(":").last().unwrap().parse::().unwrap() == port); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_ping() { - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let client = HttpClientBuilder::default().build(url).unwrap(); - let response: Result = client.request("ping", rpc_params![]).await; - assert_eq!(response.unwrap(), "pong"); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_init() { - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/DUpdateSystem/UpgradeAll") - .with_status(200) - .create_async() - .await; - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let client = HttpClientBuilder::default().build(url).unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); - let temp_dir_path = temp_dir.path().to_str().unwrap(); - let params = RpcInitRequest { - data_path: &format!("{}/data", temp_dir_path), - cache_path: &format!("{}/cache", temp_dir_path), - global_expire_time: 3600, - }; - println!("{:?}", params); - let response: Result = client.request("init", params).await; - assert!(response.unwrap()); - handle.stop().unwrap(); - } - #[tokio::test] - async fn test_check_app_available() { - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/DUpdateSystem/UpgradeAll") - .with_status(200) - .create_async() - .await; - - let id_map = BTreeMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let proxy_url = format!("{} -> {}", github::GITHUB_API_URL, server.url()); - let hub_data = BTreeMap::from([("reverse_proxy", proxy_url.as_str())]); - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let params = RpcAppRequest { - hub_uuid: "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - app_data: id_map, - hub_data, - }; - println!("{:?}", params); - let client = Client::new(url).unwrap(); - let response: Result = client - .check_app_available(params.hub_uuid, params.app_data, params.hub_data) - .await; - assert!(response.unwrap()); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_get_latest_release() { - let body = fs::read_to_string("tests/files/web/github_api_release.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repos/DUpdateSystem/UpgradeAll/releases") - .with_status(200) - .with_body(body) - .create(); - - let id_map = BTreeMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let proxy_url = format!("{} -> {}", github::GITHUB_API_URL, server.url()); - let hub_data = BTreeMap::from([("reverse_proxy", proxy_url.as_str())]); - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let params = RpcAppRequest { - hub_uuid: "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - app_data: id_map, - hub_data, - }; - println!("{:?}", params); - let client = Client::new(url).unwrap(); - let response: Result = client - .get_latest_release(params.hub_uuid, params.app_data, params.hub_data) - .await; - let release = response.unwrap(); - assert!(!release.version_number.is_empty()); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_get_releases() { - let body = fs::read_to_string("tests/files/web/github_api_release.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repos/DUpdateSystem/UpgradeAll/releases") - .with_status(200) - .with_body(body) - .create(); - - let id_map = BTreeMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let proxy_url = format!("{} -> {}", github::GITHUB_API_URL, server.url()); - let hub_data = BTreeMap::from([("reverse_proxy", proxy_url.as_str())]); - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let params = RpcAppRequest { - hub_uuid: "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - app_data: id_map, - hub_data, - }; - println!("{:?}", params); - let client = Client::new(url).unwrap(); - let response: Result, _> = client - .get_releases(params.hub_uuid, params.app_data, params.hub_data) - .await; - let releases = response.unwrap(); - assert!(!releases.is_empty()); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_run_server_hanging() { - let addr = "127.0.0.1:33334"; - let server_task = tokio::spawn(async move { - // This should run the server and wait for the shutdown command - run_server_hanging(addr, |url| { - println!("Server started at {}", url); - Ok(()) - }) - .await - .expect("Server failed to run"); - }); - - // Allow some time for the server to start up - tokio::time::sleep(Duration::from_millis(500)).await; - - // The callback should print the URL, but since we cannot capture that output easily in a test, - // we assume the server starts correctly if no error happens till now. - // Here, manually create a client and send a shutdown request - let client = HttpClientBuilder::default() - .build(format!("http://{}", addr)) - .expect("Failed to build client"); - - let response: Result<(), _> = client.request("shutdown", rpc_params![]).await; - assert!(response.is_ok(), "Failed to shutdown server"); - - // Allow some time for the server to shut down - tokio::time::sleep(Duration::from_millis(500)).await; - - // Check if the shutdown was successful by confirming the server task is done - if timeout(Duration::from_secs(1), server_task).await.is_err() { - panic!("The server did not shut down within the expected time"); - } - - let response: Result<(), _> = client.request("shutdown", rpc_params![]).await; - assert!(response.is_err(), "Server should not be running"); - } - - #[tokio::test] - async fn test_get_cloud_config() { - let body = fs::read_to_string("tests/files/web/cloud_config.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/cloud_config.json") - .with_status(200) - .with_body(body) - .create(); - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let client = HttpClientBuilder::default().build(url).unwrap(); - let url = format!("{}/cloud_config.json", server.url()); - let params = RpcCloudConfigRequest { api_url: &url }; - println!("{:?}", params); - let response: Result = client.request("get_cloud_config", params).await; - let config = response.unwrap(); - assert!(!config.app_config_list.is_empty()); - assert!(!config.hub_config_list.is_empty()); - handle.stop().unwrap(); - } - - // ======================================================================== - // WebSocket and message size tests - // ======================================================================== - - use jsonrpsee::ws_client::WsClientBuilder; - use serial_test::serial; - - /// Generate a random ASCII string of given byte length (not compressible). - /// Uses printable ASCII range (0x21-0x7e) to avoid JSON escape overhead. - fn generate_random_string(size: usize) -> String { - use rand::RngExt; - let mut rng = rand::rng(); - let mut buf = vec![0u8; size]; - rng.fill(&mut buf[..]); - // Map each byte to printable ASCII (0x21..=0x7e, 94 chars), avoid '"' and '\\' - for b in buf.iter_mut() { - *b = match (*b % 92) + 0x21 { - b'"' => b'a', - b'\\' => b'b', - v => v, - }; - } - // SAFETY: all bytes are valid ASCII - unsafe { String::from_utf8_unchecked(buf) } - } - - /// Helper: start a minimal RPC server with "echo_data" method for size testing. - async fn start_test_server_with_config(max_size: u32) -> (String, ServerHandle) { - let config = ServerConfig::builder() - .max_request_body_size(max_size) - .max_response_body_size(max_size) - .build(); - let server = jsonrpsee::server::Server::builder() - .set_config(config) - .build("127.0.0.1:0".parse::().unwrap()) - .await - .unwrap(); - let mut module = RpcModule::new(()); - module - .register_method("echo_data", |params, _, _| { - let data: String = params.one()?; - Ok::(data) - }) - .unwrap(); - module.register_method("ping", |_, _, _| "pong").unwrap(); - let addr = server.local_addr().unwrap(); - let handle = server.start(module); - (format!("ws://{}", addr), handle) - } - - #[tokio::test] - async fn test_ws_client_connection() { - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - let ws_url = url.replace("http://", "ws://"); - let max_size = get_max_message_size(); - let client = WsClientBuilder::default() - .max_request_size(max_size) - .max_response_size(max_size) - .build(&ws_url) - .await - .unwrap(); - let response: String = client.request("ping", rpc_params![]).await.unwrap(); - assert_eq!(response, "pong"); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_ws_large_message_50mb() { - const SIZE: usize = 50 * 1024 * 1024; // 50MB - let max_size = DEFAULT_MAX_SIZE; - let (ws_url, handle) = start_test_server_with_config(max_size).await; - let client = WsClientBuilder::default() - .max_request_size(max_size) - .max_response_size(max_size) - .build(&ws_url) - .await - .unwrap(); - let data = generate_random_string(SIZE); - let response: String = client - .request("echo_data", rpc_params![&data]) - .await - .unwrap(); - assert_eq!(response.len(), data.len()); - handle.stop().unwrap(); - } - - #[tokio::test] - #[serial] - async fn test_ws_env_var_limits_message_size() { - const ONE_MB: u32 = 1024 * 1024; - const TWO_MB: usize = 2 * 1024 * 1024; - - // Set env var to 1MB limit - // SAFETY: This test is marked #[serial] so no other tests run concurrently - unsafe { std::env::set_var("GETTER_WS_MAX_MESSAGE_SIZE", ONE_MB.to_string()) }; - assert_eq!(get_max_message_size(), ONE_MB); - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - let ws_url = url.replace("http://", "ws://"); - - // Client allows large messages, but server should reject - let client = WsClientBuilder::default() - .max_request_size(u32::MAX) - .max_response_size(u32::MAX) - .build(&ws_url) - .await - .unwrap(); - - // Verify ping still works (small message) - let response: String = client.request("ping", rpc_params![]).await.unwrap(); - assert_eq!(response, "pong"); - - // Send 2MB data via init request, should be rejected by 1MB server limit - let large_data = generate_random_string(TWO_MB); - let params = RpcInitRequest { - data_path: &large_data, - cache_path: "/tmp/cache", - global_expire_time: 3600, - }; - let response: Result = client.request("init", params).await; - assert!( - response.is_err(), - "2MB request should be rejected by 1MB server limit" - ); - - handle.stop().unwrap(); - // SAFETY: This test is marked #[serial] so no other tests run concurrently - unsafe { std::env::remove_var("GETTER_WS_MAX_MESSAGE_SIZE") }; - } -} diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index d940e27..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod convert; -pub mod http; -pub mod instance; -pub mod json; -pub mod time; -pub mod versioning; diff --git a/src/utils/convert.rs b/src/utils/convert.rs deleted file mode 100644 index 73be4df..0000000 --- a/src/utils/convert.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::collections::{BTreeMap, HashMap}; - -pub fn convert_hashmap_to_btreemap(hash_map: HashMap) -> BTreeMap -where - K: Ord + Eq + std::hash::Hash, - V: Clone, -{ - let mut btree_map: BTreeMap = BTreeMap::new(); - for (key, value) in hash_map { - btree_map.insert(key, value); - } - btree_map -} - -pub fn convert_btreemap(original: &BTreeMap) -> BTreeMap<&str, &str> { - let mut new_map: BTreeMap<&str, &str> = BTreeMap::new(); - for (key, value) in original.iter() { - new_map.insert(key.as_str(), value.as_str()); - } - new_map -} diff --git a/src/utils/http.rs b/src/utils/http.rs deleted file mode 100644 index a1894fb..0000000 --- a/src/utils/http.rs +++ /dev/null @@ -1,327 +0,0 @@ -use bytes::{Bytes, BytesMut}; -use http_body_util::{BodyExt, Empty}; -use hyper::{StatusCode, Uri}; -#[cfg(not(feature = "rustls-platform-verifier"))] -use hyper_rustls::ConfigBuilderExt; -use hyper_util::{ - client::legacy::{connect::HttpConnector, Client}, - rt::TokioExecutor, -}; -use once_cell::sync::Lazy; -use rustls::ClientConfig; -#[cfg(feature = "rustls-platform-verifier")] -use rustls_platform_verifier::BuilderVerifierExt; -use std::{collections::HashMap, fmt}; - -// Custom http response Error -#[derive(Debug)] -pub struct ResponseData { - pub status: u16, - pub body: Option, -} - -impl fmt::Display for ResponseData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "Response status: {}, body: {}", - self.status, - self.body.as_ref().map_or_else( - || "".to_string(), - |body| String::from_utf8_lossy(body).to_string(), - ) - ) - } -} - -pub async fn get( - url: Uri, - header_map: &HashMap, -) -> Result> { - if url.scheme_str() == Some("https") { - https_get(url, header_map).await - } else { - http_get(url, header_map).await - } -} - -pub async fn head( - url: Uri, - header_map: &HashMap, -) -> Result> { - if url.scheme_str() == Some("https") { - https_head(url, header_map).await - } else { - http_head(url, header_map).await - } -} - -pub async fn http_get( - url: Uri, - header_map: &HashMap, -) -> Result> { - _http_get(url, header_map, false).await -} - -pub async fn http_head( - url: Uri, - header_map: &HashMap, -) -> Result> { - _http_get(url, header_map, true).await -} - -async fn _http_get( - url: Uri, - header_map: &HashMap, - only_status: bool, -) -> Result> { - let http = HttpConnector::new(); - let client = Client::builder(TokioExecutor::new()).build(http); - - let mut req = hyper::Request::builder().method("GET").uri(url.clone()); - for (key, value) in header_map { - req = req.header(key, value); - } - let req = req.body(Empty::::new())?; - let mut res = client.request(req).await?; - let status = res.status(); - if only_status { - Ok(ResponseData { - status: status.as_u16(), - body: None, - }) - } else { - let mut body = BytesMut::new(); - while let Some(next) = res.frame().await { - let frame = next?; - if let Some(chunk) = frame.data_ref() { - body.extend_from_slice(chunk); - } - } - Ok(ResponseData { - status: status.as_u16(), - body: Some(body.freeze()), - }) - } -} - -pub async fn https_get( - url: Uri, - header_map: &HashMap, -) -> Result> { - _https_get(url, header_map, false).await -} - -pub async fn https_head( - url: Uri, - header_map: &HashMap, -) -> Result> { - _https_get(url, header_map, true).await -} - -// Global https provider with lazy initialization -static PROVIDER: Lazy> = - Lazy::new(|| std::sync::Arc::new(rustls::crypto::ring::default_provider())); - -// Https config error wrapper error -struct HttpsConfigError { - error: Box, -} -impl fmt::Display for HttpsConfigError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "HttpsConfigError: {}", self.error) - } -} -impl fmt::Debug for HttpsConfigError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "HttpsConfigError: {:?}", self.error) - } -} -impl std::error::Error for HttpsConfigError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None - } -} - -fn https_config() -> Result, HttpsConfigError> { - let provider = PROVIDER.clone(); - let tls: rustls::ClientConfig; - #[cfg(feature = "rustls-platform-verifier")] - { - tls = ClientConfig::builder_with_provider(provider) - .with_safe_default_protocol_versions() - .map_err(|e| HttpsConfigError { error: Box::new(e) })? - .with_platform_verifier() - .map_err(|e| HttpsConfigError { error: Box::new(e) })? - .with_no_client_auth(); - } - #[cfg(all(feature = "webpki-roots", not(feature = "rustls-platform-verifier")))] - { - tls = rustls::ClientConfig::builder_with_provider(provider) - .with_safe_default_protocol_versions() - .map_err(|e| HttpsConfigError { error: Box::new(e) })? - .with_webpki_roots() - .with_no_client_auth(); - } - #[cfg(all( - feature = "native-tokio", - not(feature = "webpki-roots"), - not(feature = "rustls-platform-verifier") - ))] - { - tls = rustls::ClientConfig::builder_with_provider(provider) - .with_safe_default_protocol_versions() - .map_err(|e| HttpsConfigError { error: Box::new(e) })? - .with_native_roots() - .map_err(|e| HttpsConfigError { error: Box::new(e) })? - .with_no_client_auth(); - } - #[cfg(all( - not(feature = "native-tokio"), - not(feature = "webpki-roots"), - not(feature = "rustls-platform-verifier") - ))] - { - compile_error!("No TLS backend enabled"); - } - Ok(hyper_rustls::HttpsConnectorBuilder::new() - .with_tls_config(tls) - .https_or_http() - .enable_http1() - .enable_http2() - .build()) -} - -async fn _https_get( - url: Uri, - header_map: &HashMap, - only_status: bool, -) -> Result> { - let https = https_config()?; - let client = Client::builder(TokioExecutor::new()).build(https); - let mut req = hyper::Request::builder().method("GET").uri(url.clone()); - for (key, value) in header_map { - req = req.header(key, value); - } - let req = req.body(Empty::::new())?; - - let mut res = client.request(req).await?; - let status = res.status(); - if only_status { - Ok(ResponseData { - status: status.as_u16(), - body: None, - }) - } else { - let mut body = BytesMut::new(); - while let Some(next) = res.frame().await { - let frame = next?; - if let Some(chunk) = frame.data_ref() { - body.extend_from_slice(chunk); - } - } - Ok(ResponseData { - status: status.as_u16(), - body: Some(body.freeze()), - }) - } -} - -pub fn http_status_is_ok(status: u16) -> bool { - if let Ok(status) = StatusCode::from_u16(status) { - !(status.is_client_error() || status.is_server_error()) - } else { - false - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_https_get() { - let url = "https://github.com".parse().unwrap(); - let result = https_get(url, &HashMap::new()).await; - assert!(result.is_ok()); - assert!(!result.unwrap().body.unwrap().is_empty()); - } - - #[tokio::test] - async fn test_https_get_invalid() { - let url = "https://123123".parse().unwrap(); - let result = https_get(url, &HashMap::new()).await; - assert!(result.is_err()); - } - - #[tokio::test] - async fn test_https_get_status() { - let url = "https://mockhttp.org/status/418".parse().unwrap(); - let result = https_get(url, &HashMap::new()).await; - assert_eq!(result.unwrap().status, 418); - } - - #[tokio::test] - async fn test_https_head() { - let url = "https://github.com".parse().unwrap(); - let result = https_head(url, &HashMap::new()).await; - assert!(result.is_ok()); - assert!(result.unwrap().body.is_none()); - } - - #[tokio::test] - async fn test_https_get_header() { - let url = "https://postman-echo.com/get".parse().unwrap(); - let header_map = { - let mut map = HashMap::new(); - map.insert("X-Test".to_string(), "test000".to_string()); - map.insert("Test-Header".to_string(), "test001".to_string()); - map - }; - let result = https_get(url, &header_map).await; - assert!(result.is_ok()); - let body = result.unwrap().body.expect("Response body was empty"); - let json: serde_json::Value = serde_json::from_slice(&body).expect("Failed to parse JSON"); - for (key, value) in header_map { - // key to lowercase, for fix podman-echo - // Due https://stackoverflow.com/questions/5258977/are-http-headers-case-sensitive - let key = key.to_lowercase(); - assert_eq!(json["headers"][key], value); - } - } - - #[tokio::test] - async fn test_http_get() { - let url = "http://example.com".parse().unwrap(); - let result = http_get(url, &HashMap::new()).await; - assert!(result.is_ok()); - assert!(!result.unwrap().body.unwrap().is_empty()); - } - - #[tokio::test] - async fn test_http_head() { - let url = "http://example.com".parse().unwrap(); - let result = http_head(url, &HashMap::new()).await; - assert!(result.is_ok()); - assert!(result.unwrap().body.is_none()); - } - - #[tokio::test] - async fn test_http_get_header() { - let url = "http://postman-echo.com/get".parse().unwrap(); - let header_map = { - let mut map = HashMap::new(); - map.insert("X-Test".to_string(), "test000".to_string()); - map.insert("Test-Header".to_string(), "test001".to_string()); - map - }; - let result = http_get(url, &header_map).await; - assert!(result.is_ok()); - let body = result.unwrap().body.expect("Response body was empty"); - let json: serde_json::Value = serde_json::from_slice(&body).expect("Failed to parse JSON"); - for (key, value) in header_map { - let key = key.to_lowercase(); - assert_eq!(json["headers"][key], value); - } - } -} diff --git a/src/utils/instance.rs b/src/utils/instance.rs deleted file mode 100644 index d4acd9c..0000000 --- a/src/utils/instance.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::sync::Arc; -use tokio::sync::Mutex; - -pub struct InstanceContainer { - instance: Arc>, -} - -impl InstanceContainer { - pub fn new(value: T) -> Self { - Self { - instance: Arc::new(Mutex::new(value)), - } - } - - pub async fn get(&self) -> Arc> { - self.instance.clone() - } -} diff --git a/src/utils/json.rs b/src/utils/json.rs deleted file mode 100644 index 54c9710..0000000 --- a/src/utils/json.rs +++ /dev/null @@ -1,31 +0,0 @@ -use bytes::Bytes; -use serde::de::Deserialize; -use serde::ser::Serialize; - -pub fn json_to_bytes(json: &T) -> Result -where - T: ?Sized + Serialize, -{ - serde_json::to_vec(json).map(Bytes::from) -} - -pub fn bytes_to_json<'a, T>(bytes: &'a Bytes) -> Result -where - T: Deserialize<'a>, -{ - serde_json::from_slice(bytes) -} - -pub fn json_to_string(json: &T) -> Result -where - T: ?Sized + Serialize, -{ - serde_json::to_string(json) -} - -pub fn string_to_json<'a, T>(string: &'a str) -> Result -where - T: Deserialize<'a>, -{ - serde_json::from_str(string) -} diff --git a/src/utils/time.rs b/src/utils/time.rs deleted file mode 100644 index 98f3039..0000000 --- a/src/utils/time.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - -pub fn get_now_unix() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_else(|_| Duration::from_secs(0)) - .as_secs() -} diff --git a/src/utils/versioning.rs b/src/utils/versioning.rs deleted file mode 100644 index c25d819..0000000 --- a/src/utils/versioning.rs +++ /dev/null @@ -1,170 +0,0 @@ -use std::cmp::Ordering; - -use libversion_sys; - -use once_cell::sync::Lazy; -use regex::Regex; - -static VERSION_NUMBER_STRICT_MATCH_REGEX: Lazy = - Lazy::new(|| Regex::new(r"\d+(\.\d+)+([.|\-|+|_| ]*[A-Za-z0-9]+)*").unwrap()); - -static VERSION_NUMBER_MATCH_REGEX: Lazy = - Lazy::new(|| Regex::new(r"\d+(\.\d+)*([.|\-|+|_| ]*[A-Za-z0-9]+)*").unwrap()); - -#[derive(Debug, Clone)] -pub struct Version { - string: String, -} - -impl Version { - pub fn new(string: String) -> Self { - Version { string } - } - - pub fn is_valid(&self) -> bool { - self.get_valid_version().is_some() - } - - pub fn get_valid_version(&self) -> Option { - VERSION_NUMBER_STRICT_MATCH_REGEX - .find(&self.string) - .or_else(|| VERSION_NUMBER_MATCH_REGEX.find(&self.string)) - .map(|match_str| match_str.as_str().to_string()) - } -} - -impl PartialEq for Version { - fn eq(&self, other: &Self) -> bool { - match (self.get_valid_version(), other.get_valid_version()) { - (Some(v1), Some(v2)) => libversion_sys::compare(&v1, &v2) == Ordering::Equal, - _ => false, - } - } -} - -impl PartialOrd for Version { - fn partial_cmp(&self, other: &Self) -> Option { - match (self.get_valid_version(), other.get_valid_version()) { - (Some(v1), Some(v2)) => Some(libversion_sys::compare(&v1, &v2)), - _ => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_version_is_valid() { - let version = Version { - string: "1.0.0".to_string(), - }; - assert!(version.is_valid()); - let version = Version { - string: "1.0.0-alpha".to_string(), - }; - assert!(version.is_valid()); - let version = Version { - string: "版本1.0.0".to_string(), - }; - assert!(version.is_valid()); - let chinese_suffix_version = Version { - string: "版本1.0.0 天行健".to_string(), - }; - assert!(chinese_suffix_version.is_valid()); - } - - #[test] - fn test_version_is_invalid() { - let version = Version { - string: "xxx".to_string(), - }; - assert!(!version.is_valid()); - } - - #[test] - fn test_version_eq() { - let version = Version { - string: "1.0.0".to_string(), - }; - let other_version = Version { - string: "1.0".to_string(), - }; - assert_eq!(version, other_version); - - let chinese_version = Version { - string: "版本1.0.0".to_string(), - }; - assert_eq!(version, chinese_version); - } - - #[test] - fn test_version_ne() { - let version = Version { - string: "1.0.0".to_string(), - }; - let other_version = Version { - string: "1.0.1".to_string(), - }; - assert_ne!(version, other_version); - } - - #[test] - fn test_version_lt() { - let version = Version { - string: "1.0".to_string(), - }; - let other_version = Version { - string: "1.0.1".to_string(), - }; - assert!(version < other_version); - } - - #[test] - fn test_version_gt() { - let version = Version { - string: "1.0.1".to_string(), - }; - let other_version = Version { - string: "1.0.1-alpha".to_string(), - }; - assert!(version > other_version); - } - - #[test] - fn test_version_get_valid_version() { - let version = Version { - string: "1.0.0 123123".to_string(), - }; - assert_eq!( - version.get_valid_version(), - Some("1.0.0 123123".to_string()) - ); - let version = Version { - string: "1.0.0-alpha 版本".to_string(), - }; - assert_eq!(version.get_valid_version(), Some("1.0.0-alpha".to_string())); - let version = Version { - string: "版本1.0.0".to_string(), - }; - assert_eq!(version.get_valid_version(), Some("1.0.0".to_string())); - let chinese_suffix_version = Version { - string: "版本1.0.0 天行健".to_string(), - }; - assert_eq!( - chinese_suffix_version.get_valid_version(), - Some("1.0.0".to_string()) - ); - - let version = Version { - string: "xxx".to_string(), - }; - assert_eq!(version.get_valid_version(), None); - - let version = Version { - string: "1.0-alpha 版本 123123".to_string(), - }; - assert_eq!(version.get_valid_version(), Some("1.0-alpha".to_string())); - } -} diff --git a/src/websdk.rs b/src/websdk.rs deleted file mode 100644 index 6100512..0000000 --- a/src/websdk.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod cloud_rules; -pub mod repo; diff --git a/src/websdk/cloud_rules.rs b/src/websdk/cloud_rules.rs deleted file mode 100644 index 5952fe8..0000000 --- a/src/websdk/cloud_rules.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod cloud_rules_manager; -pub mod cloud_rules_wrapper; -pub mod data; diff --git a/src/websdk/cloud_rules/cloud_rules_manager.rs b/src/websdk/cloud_rules/cloud_rules_manager.rs deleted file mode 100644 index 17aaebf..0000000 --- a/src/websdk/cloud_rules/cloud_rules_manager.rs +++ /dev/null @@ -1,112 +0,0 @@ -use hyper::Uri; -use std::collections::HashMap; - -use super::data::config_list::{ConfigList, ConfigListViewer}; -use crate::utils::http::get; - -pub struct CloudRules { - pub api_url: String, - - _config_list: Option, -} - -#[derive(Debug)] -pub struct DownloadError { - pub url: Uri, - pub error: Box, -} - -impl std::fmt::Display for DownloadError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "DownloadError: {} ({})", self.error, self.url) - } -} - -impl CloudRules { - pub fn new(api_url: &str) -> CloudRules { - CloudRules { - api_url: api_url.to_string(), - _config_list: None, - } - } - - pub fn get_config_list(&self) -> ConfigListViewer<'_> { - self._config_list - .as_ref() - .map_or_else(ConfigListViewer::default, |config_list| { - config_list.viewer() - }) - } - - pub async fn renew(&mut self) -> Result<(), DownloadError> { - let config_list = self.download_config_list(&self.api_url).await?; - self._config_list = Some(config_list); - Ok(()) - } - - async fn download_config_list(&self, url: &str) -> Result { - Self::_download_config_list_impl(url) - .await - .map_err(|e| DownloadError { - url: url.parse().unwrap(), - error: e, - }) - } - async fn _download_config_list_impl( - url: &str, - ) -> Result> { - let map = HashMap::new(); - let resp = get(url.parse()?, &map).await?; - if let Some(body) = resp.body { - let config_list: ConfigList = serde_json::from_slice(&body)?; - Ok(config_list) - } else { - Err("No body".into()) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_download_config_list() { - let json = fs::read_to_string("tests/files/data/UpgradeAll-rules_rules.json").unwrap(); - let path = "/DUpdateSystem/UpgradeAll-rules/master/rules.json"; - let mut server = Server::new_async().await; - server.mock("GET", path).with_body(json).create(); - let url = server.url() + path; - - let mut cloud_rules = CloudRules::new(&url); - cloud_rules.renew().await.unwrap(); - let config_list = cloud_rules.get_config_list(); - assert_eq!(config_list.app_config_list.len(), 219); - assert_eq!(config_list.app_config_list[0].info.name, "UpgradeAll"); - assert_eq!( - config_list.app_config_list.last().unwrap().info.name, - "黑阈" - ); - assert_eq!(config_list.hub_config_list.len(), 11); - assert_eq!(config_list.hub_config_list[0].info.hub_name, "GitHub"); - assert_eq!( - config_list.hub_config_list.last().unwrap().info.hub_name, - "Xposed Module Repository" - ); - } - - #[tokio::test] - async fn test_download_config_list_invalid() { - let path = "/DUpdateSystem/UpgradeAll-rules/master/rules.json"; - let mut server = Server::new_async().await; - server.mock("GET", path).with_status(404).create(); - let url = server.url() + path; - - let mut cloud_rules = CloudRules::new(&url); - let result = cloud_rules.renew().await; - assert!(result.is_err()); - assert_eq!(result.unwrap_err().url.to_string(), url); - } -} diff --git a/src/websdk/cloud_rules/cloud_rules_wrapper.rs b/src/websdk/cloud_rules/cloud_rules_wrapper.rs deleted file mode 100644 index 24a718a..0000000 --- a/src/websdk/cloud_rules/cloud_rules_wrapper.rs +++ /dev/null @@ -1,109 +0,0 @@ -pub use super::cloud_rules_manager::CloudRules; -use super::data::app_item::AppItem; -use super::data::hub_item::HubItem; - -impl CloudRules { - pub fn get_cloud_app_rules(&self, filter: F) -> Option - where - F: Fn(&AppItem) -> bool, - { - self._get_cloud_app_rules(filter, true) - .first() - .map(|x| x.to_owned()) - } - - pub fn get_cloud_app_rules_list(&self, filter: F) -> Vec - where - F: Fn(&AppItem) -> bool, - { - self._get_cloud_app_rules(filter, false) - } - - pub fn _get_cloud_app_rules(&self, filter: F, only_first: bool) -> Vec - where - F: Fn(&AppItem) -> bool, - { - let mut list = Vec::new(); - for config in self.get_config_list().app_config_list { - if filter(config) { - list.push(config.to_owned()); - if only_first { - break; - } - } - } - list - } - - pub fn get_cloud_hub_rules(&self, filter: F) -> Option - where - F: Fn(&HubItem) -> bool, - { - for config in self.get_config_list().hub_config_list { - if filter(config) { - return Some(config.to_owned()); - } - } - None - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_get_cloud_app_rules() { - let json = fs::read_to_string("tests/files/data/UpgradeAll-rules_rules.json").unwrap(); - let path = "/DUpdateSystem/UpgradeAll-rules/master/rules.json"; - let mut server = Server::new_async().await; - server.mock("GET", path).with_body(json).create(); - let url = server.url() + path; - - let mut cloud_rules = CloudRules::new(&url); - cloud_rules.renew().await.unwrap(); - let list = cloud_rules.get_cloud_app_rules(|x| x.info.name == "UpgradeAll"); - assert!(list.is_some()); - let list = cloud_rules.get_cloud_app_rules(|x| x.info.name.is_empty()); - assert!(list.is_none()); - } - - #[tokio::test] - async fn test_get_cloud_app_rules_list() { - let json = fs::read_to_string("tests/files/data/UpgradeAll-rules_rules.json").unwrap(); - let path = "/DUpdateSystem/UpgradeAll-rules/master/rules.json"; - let mut server = Server::new_async().await; - server.mock("GET", path).with_body(json).create(); - let url = server.url() + path; - - let mut cloud_rules = CloudRules::new(&url); - cloud_rules.renew().await.unwrap(); - let list = cloud_rules.get_cloud_app_rules_list(|x| x.info.name == "UpgradeAll"); - assert_eq!(list.len(), 1); - let list = cloud_rules.get_cloud_app_rules_list(|x| x.info.name.is_empty()); - assert_eq!(list.len(), 0); - let list = cloud_rules.get_cloud_app_rules_list(|x| !x.info.name.is_empty()); - assert_eq!( - list.len(), - cloud_rules.get_config_list().app_config_list.len() - ); - } - - #[tokio::test] - async fn test_get_cloud_hub_rules() { - let json = fs::read_to_string("tests/files/data/UpgradeAll-rules_rules.json").unwrap(); - let path = "/DUpdateSystem/UpgradeAll-rules/master/rules.json"; - let mut server = Server::new_async().await; - server.mock("GET", path).with_body(json).create(); - let url = server.url() + path; - - let mut cloud_rules = CloudRules::new(&url); - cloud_rules.renew().await.unwrap(); - let list = cloud_rules.get_cloud_hub_rules(|x| x.info.hub_name == "GitHub"); - assert!(list.is_some()); - let list = cloud_rules.get_cloud_hub_rules(|x| x.info.hub_name.is_empty()); - assert!(list.is_none()); - } -} diff --git a/src/websdk/cloud_rules/data.rs b/src/websdk/cloud_rules/data.rs deleted file mode 100644 index 07f8d80..0000000 --- a/src/websdk/cloud_rules/data.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod app_item; -pub mod config_list; -pub mod hub_item; diff --git a/src/websdk/cloud_rules/data/app_item.rs b/src/websdk/cloud_rules/data/app_item.rs deleted file mode 100644 index dca31c9..0000000 --- a/src/websdk/cloud_rules/data/app_item.rs +++ /dev/null @@ -1,94 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -/// AppConfig -/// -/// JSON Schema: -/// ```json -/// { -/// "base_version": 2, -/// "config_version": 1, -/// "uuid": "", -/// "base_hub_uuid": "", -/// "info": { -/// "name": "", -/// "url": "", -/// "extra_map": { -/// "android_app_package": "" -/// } -/// } -/// } -/// ``` - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct AppItem { - #[serde(rename = "base_version")] - pub base_version: i32, - - #[serde(rename = "config_version")] - pub config_version: i32, - - #[serde(rename = "uuid")] - pub uuid: String, - - #[serde(rename = "base_hub_uuid")] - pub base_hub_uuid: String, - - #[serde(rename = "info")] - pub info: AppInfo, -} - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct AppInfo { - #[serde(rename = "name")] - pub name: String, - - #[serde(rename = "url")] - pub url: String, - - #[serde(rename = "extra_map")] - pub extra_map: HashMap, // Use HashMap to store arbitrary key/value pairs -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json; - - #[test] - fn test_app_config() { - let json = r#" -{ - "base_version": 2, - "config_version": 1, - "uuid": "f27f71e1-d7a1-4fd1-bbcc-9744380611a1", - "base_hub_uuid": "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - "info": { - "name": "UpgradeAll", - "url": "https://github.com/xz-dev/UpgradeAll", - "extra_map": { - "android_app_package": "net.xzos.upgradeall" - } - } -} -"#; - - let app_item: AppItem = serde_json::from_str(json).unwrap(); - assert_eq!(app_item.base_version, 2); - assert_eq!(app_item.config_version, 1); - assert_eq!(app_item.uuid, "f27f71e1-d7a1-4fd1-bbcc-9744380611a1"); - assert_eq!( - app_item.base_hub_uuid, - "fd9b2602-62c5-4d55-bd1e-0d6537714ca0" - ); - assert_eq!(app_item.info.name, "UpgradeAll"); - assert_eq!(app_item.info.url, "https://github.com/xz-dev/UpgradeAll"); - assert_eq!( - app_item.info.extra_map, - HashMap::from([( - "android_app_package".to_string(), - "net.xzos.upgradeall".to_string() - )]) - ); - } -} diff --git a/src/websdk/cloud_rules/data/config_list.rs b/src/websdk/cloud_rules/data/config_list.rs deleted file mode 100644 index 886fcfe..0000000 --- a/src/websdk/cloud_rules/data/config_list.rs +++ /dev/null @@ -1,80 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use super::app_item::AppItem; -use super::hub_item::HubItem; - -/// Configuration lists -/// -/// JSON Schema: -/// ```json -/// { -/// "app_config_list": [], -/// "hub_config_list": [] -/// } -/// ``` - -#[derive(Default, Serialize, Debug, Clone)] -pub struct ConfigListViewer<'a> { - #[serde(rename = "app_config_list")] - pub app_config_list: Vec<&'a AppItem>, - - #[serde(rename = "hub_config_list")] - pub hub_config_list: Vec<&'a HubItem>, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ConfigList { - #[serde(rename = "app_config_list")] - pub app_config_list: Vec, - - #[serde(rename = "hub_config_list")] - pub hub_config_list: Vec, -} - -impl ConfigList { - pub fn viewer(&self) -> ConfigListViewer<'_> { - ConfigListViewer { - app_config_list: self.app_config_list.iter().collect(), - hub_config_list: self.hub_config_list.iter().collect(), - } - } -} - -impl ConfigListViewer<'_> { - pub fn to_owned(&self) -> ConfigList { - ConfigList { - app_config_list: self.app_config_list.iter().map(|&x| x.clone()).collect(), - hub_config_list: self.hub_config_list.iter().map(|&x| x.clone()).collect(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json; - use std::fs; - - #[test] - fn test_config_list() { - // read json from file - let json = fs::read_to_string("tests/files/data/UpgradeAll-rules_rules.json").unwrap(); - - let config_list: ConfigList = serde_json::from_str(&json).unwrap(); - - // check app_config_list - assert_eq!(config_list.app_config_list.len(), 219); - assert_eq!(config_list.app_config_list[0].info.name, "UpgradeAll"); - assert_eq!( - config_list.app_config_list.last().unwrap().info.name, - "黑阈" - ); - // check hub_config_list - assert_eq!(config_list.hub_config_list.len(), 11); - assert_eq!(config_list.hub_config_list[0].info.hub_name, "GitHub"); - assert_eq!( - config_list.hub_config_list.last().unwrap().info.hub_name, - "Xposed Module Repository" - ); - } -} diff --git a/src/websdk/cloud_rules/data/hub_item.rs b/src/websdk/cloud_rules/data/hub_item.rs deleted file mode 100644 index c8c43b7..0000000 --- a/src/websdk/cloud_rules/data/hub_item.rs +++ /dev/null @@ -1,123 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// HubConfig -/// -/// JSON Schema: -/// ```json -/// { -/// base_version: 6 -/// config_version: 1 -/// uuid: "" -/// info: { -/// "hub_name": "", -/// "hub_icon_url": "" -/// } -/// target_check_api: "" -/// api_keywords: [] -/// auth_keywords: [] -/// app_url_templates": [] -/// } -/// ``` - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct HubItem { - #[serde(rename = "base_version", default)] - pub base_version: i32, - - #[serde(rename = "config_version", default)] - pub config_version: i32, - - #[serde(rename = "uuid", default)] - pub uuid: String, - - #[serde(rename = "info")] - pub info: Info, - - #[serde(rename = "api_keywords", default)] - pub api_keywords: Vec, - - /// Auth parameter keys required by this hub (e.g. ["token"] for GitHub). - /// Used by the UI to provide autocomplete suggestions when editing hub auth. - #[serde(rename = "auth_keywords", default)] - pub auth_keywords: Vec, - - #[serde(rename = "app_url_templates", default)] - pub app_url_templates: Vec, - - #[serde(rename = "target_check_api")] - pub target_check_api: Option, -} - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct Info { - #[serde(rename = "hub_name", default)] - pub hub_name: String, - - #[serde(rename = "hub_icon_url", default)] - pub hub_icon_url: Option, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_hub_item() { - let json = r#" -{ - "base_version": 6, - "config_version": 3, - "uuid": "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - "info": { - "hub_name": "GitHub", - "hub_icon_url": "" - }, - "target_check_api": "", - "api_keywords": [ - "owner", - "repo" - ], - "app_url_templates": [ - "https://github.com/%owner/%repo/" - ] -} - "#; - - let hub_item: HubItem = serde_json::from_str(json).unwrap(); - assert_eq!(hub_item.base_version, 6); - assert_eq!(hub_item.config_version, 3); - assert_eq!(hub_item.uuid, "fd9b2602-62c5-4d55-bd1e-0d6537714ca0"); - assert_eq!(hub_item.info.hub_name, "GitHub"); - assert_eq!(hub_item.info.hub_icon_url, Some("".to_string())); - assert_eq!(hub_item.target_check_api, Some("".to_string())); - assert_eq!(hub_item.api_keywords, ["owner", "repo"]); - // Old config without auth_keywords deserializes to empty vec. - assert_eq!(hub_item.auth_keywords, Vec::::new()); - assert_eq!( - hub_item.app_url_templates[0], - "https://github.com/%owner/%repo/" - ); - } - - #[test] - fn test_hub_item_auth_keywords() { - let json = r#" -{ - "base_version": 5, - "config_version": 3, - "uuid": "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - "info": { "hub_name": "GitHub" }, - "api_keywords": ["owner", "repo"], - "auth_keywords": ["token"], - "app_url_templates": ["https://github.com/%owner/%repo/"] -} - "#; - let hub_item: HubItem = serde_json::from_str(json).unwrap(); - assert_eq!(hub_item.auth_keywords, ["token"]); - - // Round-trip serialization preserves auth_keywords. - let serialized = serde_json::to_string(&hub_item).unwrap(); - let hub_item2: HubItem = serde_json::from_str(&serialized).unwrap(); - assert_eq!(hub_item2.auth_keywords, ["token"]); - } -} diff --git a/src/websdk/repo.rs b/src/websdk/repo.rs deleted file mode 100644 index d10139d..0000000 --- a/src/websdk/repo.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod api; -pub mod data; -pub mod provider; diff --git a/src/websdk/repo/api.rs b/src/websdk/repo/api.rs deleted file mode 100644 index 044176a..0000000 --- a/src/websdk/repo/api.rs +++ /dev/null @@ -1,166 +0,0 @@ -use async_fn_traits::AsyncFnOnce2; -use serde::de::DeserializeOwned; -use serde::Serialize; - -use super::data::release::ReleaseData; -use super::provider::base_provider::{AppDataMap, DataMap, FIn, FOut, FunctionType, HubDataMap}; -use super::provider::outside_rpc::OutsideProvider; -use super::provider::{self, add_provider}; -use crate::cache::get_cache_manager; -use crate::cache::manager::GroupType; -use crate::rpc::data::DownloadItemData; -use crate::utils::json::{bytes_to_json, json_to_bytes}; -use std::collections::HashMap; - -#[derive(Debug, Clone)] -struct ErrorProviderNotFound; - -impl std::fmt::Display for ErrorProviderNotFound { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Provider not found for this request.") - } -} - -impl std::error::Error for ErrorProviderNotFound { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None - } -} - -async fn call_func( - uuid: &str, - app_data: &AppDataMap<'_>, - hub_data: &HubDataMap<'_>, - func_type: FunctionType, - provider_func: F, -) -> Result, ErrorProviderNotFound> -where - T: Send + DeserializeOwned + Serialize, - F: for<'b> AsyncFnOnce2<&'b str, &'b FIn<'b>, Output = Option>>, -{ - let cache_manager = get_cache_manager().await; - let data_map = DataMap { app_data, hub_data }; - let api_cache_key = data_map.get_hash(); - if let Some(bytes) = cache_manager - .lock() - .await - .get(&GroupType::Api, &api_cache_key.to_string(), None) - .await - { - if let Ok(value) = bytes_to_json::(&bytes) { - return Ok(Some(value)); - } - } - let cache_keys = provider::get_cache_request_key(uuid, &func_type, &data_map); - let mut cache_map = HashMap::new(); - if let Some(keys) = cache_keys { - for key in keys { - if let Some(value) = cache_manager - .lock() - .await - .get(&GroupType::RepoInside, &key, None) - .await - { - cache_map.insert(key, value); - } - } - } - - let fin = FIn::new(data_map, Some(cache_map)); - if let Some(fout) = provider_func(uuid, &fin).await { - if let Some(cached_map) = fout.cached_map { - for (key, value) in cached_map { - let _ = cache_manager - .lock() - .await - .save(&GroupType::RepoInside, &key, value) - .await; - } - } - if let Ok(data) = fout.result { - if let Ok(value) = json_to_bytes(&data) { - let _ = cache_manager - .lock() - .await - .save(&GroupType::Api, &api_cache_key.to_string(), value) - .await; - } - Ok(Some(data)) - } else { - Ok(None) - } - } else { - Err(ErrorProviderNotFound) - } -} - -pub async fn check_app_available<'a>( - uuid: &str, - app_data: &AppDataMap<'a>, - hub_data: &HubDataMap<'a>, -) -> Option { - call_func( - uuid, - app_data, - hub_data, - FunctionType::CheckAppAvailable, - provider::check_app_available, - ) - .await - .unwrap_or(None) -} - -pub async fn get_latest_release<'a>( - uuid: &str, - app_data: &AppDataMap<'a>, - hub_data: &HubDataMap<'a>, -) -> Option { - call_func( - uuid, - app_data, - hub_data, - FunctionType::GetLatestRelease, - provider::get_latest_release, - ) - .await - .unwrap_or(None) -} - -pub async fn get_releases<'a>( - uuid: &str, - app_data: &AppDataMap<'a>, - hub_data: &HubDataMap<'a>, -) -> Option> { - call_func( - uuid, - app_data, - hub_data, - FunctionType::GetReleases, - provider::get_releases, - ) - .await - .unwrap_or(None) -} - -pub async fn get_download<'a>( - uuid: &str, - app_data: &AppDataMap<'a>, - hub_data: &HubDataMap<'a>, - asset_index: &[i32], -) -> Option> { - let data_map = DataMap { app_data, hub_data }; - let fin = FIn::new(data_map, None); - if let Some(fout) = provider::get_download(uuid, &fin, asset_index).await { - fout.result.ok() - } else { - None - } -} - -pub fn add_outside_provider(uuid: &str, url: &str) { - let provider = OutsideProvider { - uuid: uuid.to_string(), - url: url.to_string(), - }; - add_provider(uuid, provider); -} diff --git a/src/websdk/repo/controller.rs b/src/websdk/repo/controller.rs deleted file mode 100644 index f9eae81..0000000 --- a/src/websdk/repo/controller.rs +++ /dev/null @@ -1,121 +0,0 @@ -use bytes::Bytes; -use std::future::Future; - -use crate::cache::convert::{bool_to_bytes, bytes_to_bool}; -use crate::utils::json::{bytes_to_json, json_to_bytes}; -use crate::cache::manager::GroupType::{API, REPO_INSIDE}; -use crate::get_cache_manager; - -use super::data::release::ReleaseData; -use super::provider; -use super::provider::base_provider::{FIn, FOut, FunctionType, IdMap}; - -pub async fn check_app_available<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option { - let key = format!("check_app_available_{}_{:?}", &uuid, id_map); - let expire_time = Some(60 * 60 * 24); - cache_future_return( - _check_app_available(uuid, id_map), - &key, - expire_time, - |data| Ok(bool_to_bytes(data)), - |bytes| Ok(bytes_to_bool(bytes)), - ) - .await -} - -pub async fn get_latest_release<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option { - let key = format!("get_latest_release_{}_{:?}", &uuid, id_map); - let expire_time = Some(60 * 60 * 24); - cache_future_return( - _get_latest_release(uuid, id_map), - &key, - expire_time, - |data| json_to_bytes(&data), - |bytes| bytes_to_json(bytes), - ) - .await -} - -pub async fn get_releases<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option> { - let key = format!("get_releases_{}_{:?}", &uuid, id_map); - let expire_time = Some(60 * 60 * 24); - cache_future_return( - _get_releases(uuid, id_map), - &key, - expire_time, - |data| json_to_bytes(&data), - |bytes| bytes_to_json(bytes), - ) - .await -} - -async fn _check_app_available<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option { - let fin = get_fin(uuid, id_map, &FunctionType::CheckAppAvailable).await; - let fout = provider::check_app_available(uuid, &fin).await; - detach_result(fout).await -} - -async fn _get_latest_release<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option { - let fin = get_fin(uuid, id_map, &FunctionType::GetLatestRelease).await; - let fout = provider::get_latest_release(uuid, &fin).await; - detach_result(fout).await -} - -async fn _get_releases<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option> { - let fin = get_fin(uuid, id_map, &FunctionType::GetReleases).await; - let fout = provider::get_releases(uuid, &fin).await; - detach_result(fout).await -} - -async fn get_fin<'a>(uuid: &str, id_map: &'a IdMap<'a>, function_type: &FunctionType) -> FIn<'a> { - let cache_map = if let Some(cache_key_list) = - provider::get_cache_request_key(uuid, function_type, id_map) - { - let map = get_cache_manager!() - .get_cache_map(&REPO_INSIDE, &cache_key_list, None) - .await; - if map.is_empty() { - None - } else { - Some(map) - } - } else { - None - }; - FIn::new(id_map, cache_map) -} - -async fn detach_result(fout: Option>) -> Option { - if let Some(fout) = fout { - if let Some(cache_map) = fout.cached_map { - for (key, value) in cache_map { - let _ = get_cache_manager!().save(&REPO_INSIDE, &key, value).await; - } - } - fout.result.ok() - } else { - None - } -} - -async fn cache_future_return( - f: impl Future>, - key: &str, - expire_time: Option, - encoder: fn(&T) -> Result, - decoder: fn(&Bytes) -> Result, -) -> Option { - if let Some(value) = get_cache_manager!().get(&API, key, expire_time).await { - decoder(&value).ok() - } else { - let result = f.await; - if let Some(result) = result { - if let Ok(value) = encoder(&result) { - let _ = get_cache_manager!().save(&API, key, value).await; - } - Some(result) - } else { - None - } - } -} diff --git a/src/websdk/repo/data.rs b/src/websdk/repo/data.rs deleted file mode 100644 index f35143a..0000000 --- a/src/websdk/repo/data.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod release; diff --git a/src/websdk/repo/data/release.rs b/src/websdk/repo/data/release.rs deleted file mode 100644 index 6d25119..0000000 --- a/src/websdk/repo/data/release.rs +++ /dev/null @@ -1,17 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct ReleaseData { - pub version_number: String, - pub changelog: String, - pub assets: Vec, - pub extra: Option>, -} - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct AssetData { - pub file_name: String, - pub file_type: String, - pub download_url: String, -} diff --git a/src/websdk/repo/provider.rs b/src/websdk/repo/provider.rs deleted file mode 100644 index 220367b..0000000 --- a/src/websdk/repo/provider.rs +++ /dev/null @@ -1,99 +0,0 @@ -pub mod base_provider; -pub mod fdroid; -pub mod github; -pub mod gitlab; -pub mod lsposed_repo; -pub mod outside_rpc; - -use once_cell::sync::Lazy; -use std::collections::HashMap; -use std::sync::{Arc, RwLock}; - -use self::base_provider::{BaseProvider, DataMap, FIn, FOut, FunctionType}; -use self::fdroid::FDroidProvider; -use self::github::GitHubProvider; -use self::gitlab::GitLabProvider; -use self::lsposed_repo::LsposedRepoProvider; -use super::data::release::ReleaseData; -use crate::rpc::data::DownloadItemData; - -type ProviderMap = HashMap<&'static str, Arc>; - -static PROVIDER_MAP: Lazy>> = Lazy::new(|| { - Arc::new(RwLock::new(HashMap::from([ - ( - "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - Arc::new(GitHubProvider::new()) as Arc, - ), - ( - "6a6d590b-1809-41bf-8ce3-7e3f6c8da945", - Arc::new(FDroidProvider::new()) as Arc, - ), - ( - "a84e2fbe-1478-4db5-80ae-75d00454c7eb", - Arc::new(GitLabProvider::new()) as Arc, - ), - ( - "401e6259-2eab-46f0-8e8a-d2bfafedf5bf", - Arc::new(LsposedRepoProvider::new()) as Arc, - ), - ]))) -}); - -fn get_provider(uuid: &str) -> Option> { - let map = PROVIDER_MAP.read().unwrap(); - map.get(uuid).cloned() -} - -pub fn add_provider(uuid: &str, provider: impl BaseProvider + Send + Sync + 'static) { - let mut map = PROVIDER_MAP.write().unwrap(); - let uuid: &'static str = Box::leak(Box::new(uuid.to_string())); - map.insert( - uuid, - Arc::new(provider) as Arc, - ); -} - -pub fn get_cache_request_key( - uuid: &str, - function_type: &FunctionType, - data_map: &DataMap, -) -> Option> { - get_provider(uuid).map(|provider| provider.get_cache_request_key(function_type, data_map)) -} - -pub async fn check_app_available<'a>(uuid: &str, fin: &FIn<'a>) -> Option> { - if let Some(provider) = get_provider(uuid) { - Some(provider.check_app_available(fin).await) - } else { - None - } -} - -pub async fn get_latest_release<'a>(uuid: &str, fin: &FIn<'a>) -> Option> { - if let Some(provider) = get_provider(uuid) { - Some(provider.get_latest_release(fin).await) - } else { - None - } -} - -pub async fn get_releases<'a>(uuid: &str, fin: &FIn<'a>) -> Option>> { - if let Some(provider) = get_provider(uuid) { - Some(provider.get_releases(fin).await) - } else { - None - } -} - -pub async fn get_download<'a>( - uuid: &str, - fin: &FIn<'a>, - asset_index: &[i32], -) -> Option>> { - if let Some(provider) = get_provider(uuid) { - Some(provider.get_download(fin, asset_index).await) - } else { - None - } -} diff --git a/src/websdk/repo/provider/base_provider.rs b/src/websdk/repo/provider/base_provider.rs deleted file mode 100644 index 2dcec20..0000000 --- a/src/websdk/repo/provider/base_provider.rs +++ /dev/null @@ -1,427 +0,0 @@ -use async_trait::async_trait; - -use bytes::Bytes; -use core::fmt; -use regex::Regex; -use std::hash::{DefaultHasher, Hash, Hasher}; -use std::{ - collections::{BTreeMap, HashMap}, - error::Error, -}; - -use super::super::data::release::*; -use crate::rpc::data::DownloadItemData; - -pub type HubDataMap<'a> = BTreeMap<&'a str, &'a str>; -pub type AppDataMap<'a> = BTreeMap<&'a str, &'a str>; - -#[derive(Hash)] -pub struct DataMap<'a> { - pub app_data: &'a AppDataMap<'a>, - pub hub_data: &'a HubDataMap<'a>, -} - -impl fmt::Display for DataMap<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "app_data: {:?}, hub_data: {:?}", - self.app_data, self.hub_data - ) - } -} - -impl DataMap<'_> { - pub fn get_hash(&self) -> u64 { - let mut hasher = DefaultHasher::new(); - self.hash(&mut hasher); - hasher.finish() - } -} - -pub type CacheMap = HashMap; - -pub enum FunctionType { - CheckAppAvailable, - GetLatestRelease, - GetReleases, - GetDownload, -} - -pub struct FIn<'a> { - pub data_map: DataMap<'a>, - cache_map: Option>, -} - -impl<'a> FIn<'a> { - pub fn new_with_frag( - app_data: &'a AppDataMap<'a>, - hub_data: &'a HubDataMap<'a>, - cache_map: Option>, - ) -> Self { - FIn { - data_map: DataMap { app_data, hub_data }, - cache_map, - } - } - pub fn new(data_map: DataMap<'a>, cache_map: Option>) -> Self { - FIn { - data_map, - cache_map, - } - } - - pub fn get_cache(&self, key: &str) -> Option<&Bytes> { - if let Some(cache_map) = &self.cache_map { - if let Some(value) = cache_map.get(key) { - return Some(value); - } - } - None - } -} - -#[derive(Debug)] -pub struct FOut { - pub result: Result>, - pub cached_map: Option>, -} - -impl FOut { - pub fn new(data: T) -> Self { - FOut { - result: Ok(data), - cached_map: None, - } - } - - pub fn new_empty() -> Self { - FOut { - result: Err(Box::new(std::io::Error::other("no data"))), - cached_map: None, - } - } - - pub fn set_cache(mut self, key: &str, value: Bytes) -> Self { - let cache_map = self.cached_map.get_or_insert_with(HashMap::new); - cache_map.insert(key.to_string(), value); - self - } - - pub fn set_error(mut self, error: Box) -> Self { - self.result = Err(error); - self - } - - pub fn set_data(mut self, data: T) -> Self { - self.result = Ok(data); - self - } - - pub fn set_cached_map(mut self, cached_map: HashMap) -> Self { - self.cached_map = Some(cached_map); - self - } -} - -#[async_trait] -pub trait BaseProvider { - fn get_cache_request_key( - &self, - function_type: &FunctionType, - data_map: &DataMap, - ) -> Vec; - - async fn check_app_available(&self, fin: &FIn) -> FOut; - - async fn get_latest_release(&self, fin: &FIn) -> FOut { - let result = self.get_releases(fin).await; - - let fout_result = match result.result { - Ok(releases) => { - let release = releases.first().cloned(); - if let Some(release) = release { - Ok(release) - } else { - Err( - Box::new(std::io::Error::new(std::io::ErrorKind::NotFound, "no data")) - as Box, - ) - } - } - Err(e) => Err(e), - }; - - FOut { - result: fout_result, - cached_map: result.cached_map, - } - } - - async fn get_releases(&self, fin: &FIn) -> FOut>; - - /// Get download info for an app's asset. Default returns empty (not supported). - /// Only OutsideProvider (Kotlin hubs) needs to implement this. - async fn get_download(&self, _fin: &FIn, _asset_index: &[i32]) -> FOut> { - FOut::new_empty() - } -} - -pub trait BaseProviderExt: BaseProvider { - fn url_proxy_map(&self, fin: &FIn) -> HashMap { - let hub_data = fin.data_map.hub_data; - if let Some(proxy_map) = hub_data.get(REVERSE_PROXY) { - proxy_map - .lines() - .map(|line| { - let mut parts = line.splitn(2, "->"); - let url_prefix = parts.next().unwrap_or_default().trim(); - let proxy_url = parts.next().unwrap_or_default().trim(); - (url_prefix.to_string(), proxy_url.to_string()) - }) - .filter(|v| !v.0.is_empty() && !v.1.is_empty()) - .collect::>() - } else { - HashMap::new() - } - } - - fn replace_proxy_url(&self, fin: &FIn, url: &str) -> String { - let mut result_url = url.to_string(); - for (url_prefix, proxy_url) in self.url_proxy_map(fin).iter() { - let regex_prefix = "regex:"; - if let Some(stripped) = url_prefix.strip_prefix(regex_prefix) { - let url_prefix = &stripped.trim(); - if let Ok(re) = Regex::new(url_prefix) { - result_url = re.replace_all(&result_url, proxy_url.clone()).to_string(); - } - } else { - result_url = result_url.replace(url_prefix, proxy_url); - } - } - result_url - } -} - -pub const ANDROID_APP_TYPE: &str = "android_app_package"; -pub const ANDROID_MAGISK_MODULE_TYPE: &str = "android_magisk_module"; -pub const ANDROID_CUSTOM_SHELL: &str = "android_custom_shell"; -pub const ANDROID_CUSTOM_SHELL_ROOT: &str = "android_custom_shell_root"; - -pub const KEY_REPO_URL: &str = "repo_url"; -pub const KEY_REPO_API_URL: &str = "repo_api_url"; - -pub const REVERSE_PROXY: &str = "reverse_proxy"; - -#[cfg(test)] -mod tests { - use super::*; - - pub struct MockProvider; - - impl MockProvider { - pub fn new() -> MockProvider { - MockProvider {} - } - } - - #[async_trait] - impl BaseProvider for MockProvider { - fn get_cache_request_key( - &self, - function_type: &FunctionType, - data_map: &DataMap, - ) -> Vec { - let key_name = match function_type { - FunctionType::CheckAppAvailable => "check_app_available", - FunctionType::GetLatestRelease => "get_latest_release", - FunctionType::GetReleases => "get_releases", - FunctionType::GetDownload => "get_download", - }; - let id_map = data_map.app_data; - vec![format!( - "{}:{}", - key_name, - id_map - .iter() - .map(|(k, v)| format!("{}={}", k, v)) - .collect::>() - .join(",") - )] - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - let id_map = fin.data_map.app_data; - let cache_map = fin.cache_map.clone(); - FOut::new(cache_map.unwrap_or_default().contains_key(id_map["id"])) - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - let id_map = fin.data_map.app_data; - let cache_map = fin.cache_map.clone(); - FOut::new( - cache_map - .unwrap_or_default() - .get(id_map["id"]) - .unwrap() - .iter() - .map(|i| ReleaseData { - version_number: i.to_string(), - changelog: "".to_string(), - assets: vec![], - extra: None, - }) - .collect::>(), - ) - } - } - - impl BaseProviderExt for MockProvider {} - - #[tokio::test] - async fn test_get_cache_request_key() { - let mock = MockProvider::new(); - let data_map = DataMap { - app_data: &AppDataMap::from([("id", "123")]), - hub_data: &HubDataMap::new(), - }; - - let key = mock.get_cache_request_key(&FunctionType::CheckAppAvailable, &data_map); - assert_eq!(key, vec!["check_app_available:id=123"]); - let key = mock.get_cache_request_key(&FunctionType::GetReleases, &data_map); - assert_eq!(key, vec!["get_releases:id=123"]); - } - - #[tokio::test] - async fn test_check_app_available() { - let mock = MockProvider::new(); - let id_map = AppDataMap::from([("id", "123")]); - let cache_map = CacheMap::from([("123".to_string(), Bytes::from(vec![1u8]))]); - - let fin = FIn { - data_map: DataMap { - app_data: &id_map, - hub_data: &BTreeMap::new(), - }, - cache_map: Some(cache_map), - }; - - let available = mock.check_app_available(&fin).await; - assert_eq!(available.result.ok(), Some(true)); - } - - #[tokio::test] - async fn test_get_releases() { - let mock = MockProvider::new(); - let cache_map = CacheMap::from([("123".to_string(), Bytes::from(vec![1u8, 2u8, 3u8]))]); - - let fin = FIn { - data_map: DataMap { - app_data: &AppDataMap::from([("id", "123")]), - hub_data: &BTreeMap::new(), - }, - cache_map: Some(cache_map), - }; - - let releases = mock.get_releases(&fin).await; - assert_eq!(releases.result.unwrap().len(), 3); - } - - #[tokio::test] - async fn test_get_latest_release() { - let mock = MockProvider::new(); - let cache_map = CacheMap::from([("123".to_string(), Bytes::from(vec![1u8, 2u8, 3u8]))]); - - let fin = FIn { - data_map: DataMap { - app_data: &AppDataMap::from([("id", "123")]), - hub_data: &BTreeMap::new(), - }, - cache_map: Some(cache_map), - }; - - let latest_release = mock.get_latest_release(&fin).await; - let latest_version = latest_release.result.unwrap().version_number; - assert_eq!(latest_version, "1"); - } - - #[test] - fn test_replace_proxy_url() { - let mock = MockProvider::new(); - let url = "https://github.com"; - let url_r = "https://github.com.proxy"; - let proxy_url = format!("{} -> {}", url, url_r); - let data_map = HubDataMap::from([("reverse_proxy", proxy_url.as_str())]); - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - url, - ); - assert_eq!(result, url_r); - - let proxy_url = format!("{}->{}", url, url_r); - let data_map = HubDataMap::from([("reverse_proxy", proxy_url.as_str())]); - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - url, - ); - assert_eq!(result, url_r); - - let url_r = format!("{} -> proxy", url); - let proxy_url = format!(" {} ->{} ", url, url_r); - let data_map = HubDataMap::from([("reverse_proxy", proxy_url.as_str())]); - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - url, - ); - assert_eq!(result, url_r); - } - - #[test] - fn test_replace_proxy_url_with_regex() { - let mock = MockProvider::new(); - - let regex_url = "regex:^https:.*/"; - let url_r = "https://github-proxy.com/"; - - let proxy_url = format!("{} -> {}", regex_url, url_r); - let data_map = HubDataMap::from([("reverse_proxy", proxy_url.as_str())]); - - let url = "https://github.com/GitHub"; - let expected_url = "https://github-proxy.com/GitHub"; - - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - url, - ); - - assert_eq!( - result, expected_url, - "URL should be rewritten to use the proxy domain." - ); - - let non_matching_url = "http://example.com"; - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - non_matching_url, - ); - - assert_eq!( - result, non_matching_url, - "Non-matching URL should not be rewritten." - ); - } - - #[test] - fn test_replace_proxy_url_multiple() { - let mock = MockProvider::new(); - let url = "https://github.com"; - let proxy_url = "https -> http\ngithub -> github-proxy"; - let url_r = "http://github-proxy.com"; - let data_map = HubDataMap::from([("reverse_proxy", proxy_url)]); - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - url, - ); - assert_eq!(result, url_r); - } -} diff --git a/src/websdk/repo/provider/fdroid.rs b/src/websdk/repo/provider/fdroid.rs deleted file mode 100644 index 9e2f276..0000000 --- a/src/websdk/repo/provider/fdroid.rs +++ /dev/null @@ -1,356 +0,0 @@ -use async_trait::async_trait; -use bytes::Bytes; -use quick_xml::events::Event; -use quick_xml::Reader; -use std::collections::HashMap; -use std::error::Error; - -use crate::utils::http::{get, head, http_status_is_ok}; - -use super::super::data::release::*; -use super::base_provider::*; - -const FDROID_URL: &str = "https://f-droid.org"; - -pub struct FDroidProvider; - -impl FDroidProvider { - pub fn new() -> FDroidProvider { - FDroidProvider {} - } - - pub fn get_api_url(url: &str) -> String { - format!("{}/repo/index.xml", url) - } - - fn get_urls(data_map: &DataMap) -> (String, String) { - let url = data_map.hub_data.get(KEY_REPO_URL).unwrap_or(&FDROID_URL); - let api_url = if let Some(api_url) = data_map.hub_data.get(KEY_REPO_API_URL) { - api_url.to_string() - } else { - FDroidProvider::get_api_url(url) - }; - (url.to_string(), api_url) - } -} - -impl BaseProviderExt for FDroidProvider {} - -#[async_trait] -impl BaseProvider for FDroidProvider { - fn get_cache_request_key( - &self, - function_type: &FunctionType, - data_map: &DataMap, - ) -> Vec { - let (url, api_url) = FDroidProvider::get_urls(data_map); - let id_map = data_map.app_data; - match function_type { - FunctionType::CheckAppAvailable => vec![format!( - "{}/packages/{}/HEAD", - url, id_map[ANDROID_APP_TYPE] - )], - FunctionType::GetLatestRelease | FunctionType::GetReleases => vec![api_url.to_string()], - FunctionType::GetDownload => vec![], - } - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - let (url, _) = FDroidProvider::get_urls(&fin.data_map); - let id_map = fin.data_map.app_data; - let package_id = id_map[ANDROID_APP_TYPE]; - let api_url = format!("{}/packages/{}", url, package_id); - let api_url = self.replace_proxy_url(fin, &api_url); - - if let Ok(parsed_url) = api_url.parse() { - if let Ok(rsp) = head(parsed_url, &HashMap::new()).await { - return FOut::new(http_status_is_ok(rsp.status)); - } - } - FOut::new_empty() - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - let (url, api_url) = FDroidProvider::get_urls(&fin.data_map); - let id_map = fin.data_map.app_data; - let package_id = id_map[ANDROID_APP_TYPE]; - let api_url = self.replace_proxy_url(fin, &api_url); - let cache_key = self - .get_cache_request_key(&FunctionType::GetReleases, &fin.data_map) - .first() - .unwrap() - .clone(); - let mut cache_map_fout = CacheMap::new(); - let index_cache = fin.get_cache(&cache_key); - let mut index: Option = None; - if let Some(i) = index_cache { - index = Some(i.clone()); - } else if let Ok(parsed_url) = api_url.parse() { - if let Ok(rsp) = get(parsed_url, &HashMap::new()).await { - index = rsp.body; - cache_map_fout.insert(cache_key.to_string(), index.clone().unwrap()); - } - }; - if index.is_none() { - return FOut::new_empty(); - } - let mut releases_fout = Vec::new(); - if let Ok(content) = std::str::from_utf8(&index.unwrap()) { - let mut reader = Reader::from_str(content.trim()); - loop { - let (xml_package_id, releases) = - FDroidProvider::get_releases_from_xml(&mut reader, &url) - .await - .unwrap(); - if xml_package_id == package_id { - releases_fout = releases; - } - if xml_package_id.is_empty() { - break; - } - } - } - let mut fout = FOut::new(releases_fout); - if !cache_map_fout.is_empty() { - fout = fout.set_cached_map(cache_map_fout); - } - fout - } -} - -type Result = std::result::Result>; - -impl FDroidProvider { - async fn decode_package_xml(reader: &mut Reader<&[u8]>, url: &str) -> Result { - let xml_key = b"package"; - let mut version_number = String::new(); - let mut changelog = String::new(); - let mut file_name = String::new(); - let mut extra = HashMap::new(); - - let mut current_tag = String::new(); - loop { - match reader.read_event() { - Err(e) => return Err(Box::new(e)), - Ok(Event::Eof) => break, - Ok(Event::Start(e)) => { - let name = e.name(); - current_tag = String::from_utf8_lossy(name.as_ref()).to_string(); - } - Ok(Event::End(e)) => { - if e.name().as_ref() == xml_key { - break; - } - } - Ok(Event::Text(e)) => { - if let Ok(text) = e.decode() { - match current_tag.as_str() { - "version" => version_number += text.as_ref(), - "changelog" => changelog += text.as_ref(), - "versionCode" | "nativecode" => { - extra.insert(current_tag.clone(), text.to_string()); - } - "apkname" => file_name += text.as_ref(), - _ => (), - } - } - } - _ => (), - }; - } - let download_url = format!("{}/{}", url, file_name); - let file_type = file_name.split('.').next_back().unwrap_or("").to_string(); - - let extra = if extra.is_empty() { None } else { Some(extra) }; - Ok(ReleaseData { - version_number, - changelog, - assets: vec![AssetData { - file_name, - file_type, - download_url, - }], - extra, - }) - } - async fn decode_release_xml(reader: &mut Reader<&[u8]>, url: &str) -> Result> { - let mut releases = Vec::new(); - let mut changelog = String::new(); - - let mut current_tag = String::new(); - loop { - match reader.read_event() { - Err(e) => return Err(Box::new(e)), - Ok(Event::Eof) => break, - Ok(Event::Start(e)) => { - let name = e.name(); - match name.as_ref() { - b"package" => { - releases.push(FDroidProvider::decode_package_xml(reader, url).await?); - } - _ => { - current_tag = String::from_utf8_lossy(name.as_ref()).to_string(); - } - } - } - Ok(Event::End(e)) => { - if e.name().as_ref() == b"application" { - break; - } - } - Ok(Event::Text(e)) => { - if let Ok(text) = e.decode() { - if current_tag.as_str() == "changelog" { - changelog += text.as_ref() - } - } - } - _ => (), - }; - } - if !changelog.is_empty() { - if let Some(release) = releases.first_mut() { - release.changelog = changelog.clone(); - } - } - Ok(releases) - } - - async fn get_releases_from_xml( - reader: &mut Reader<&[u8]>, - url: &str, - ) -> Result<(String, Vec)> { - let mut package_id = String::new(); - loop { - match reader.read_event() { - Err(e) => return Err(Box::new(e)), - Ok(Event::Eof) => return Ok((package_id, Vec::new())), - Ok(Event::Start(e)) => { - if e.name().as_ref() == b"application" { - for attr in e.attributes().filter_map(|id| id.ok()) { - if attr.key.as_ref() == b"id" { - package_id = - String::from_utf8_lossy(attr.value.as_ref()).to_string(); - let releases = - FDroidProvider::decode_release_xml(reader, url).await?; - return Ok((package_id, releases)); - } - } - } - } - _ => (), - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_check_app_available() { - let package_id = "com.termux"; - let mut server = Server::new_async().await; - let _m = server - .mock("GET", format!("/packages/{}", package_id).as_str()) - .with_status(200) - .create_async() - .await; - - let provider = FDroidProvider::new(); - let app_data = AppDataMap::from([(ANDROID_APP_TYPE, package_id)]); - let proxy_url = format!("{} -> {}", FDROID_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - let fin = FIn::new_with_frag(&app_data, &hub_data, None); - let fout = provider.check_app_available(&fin).await; - assert!(fout.result.unwrap()); - } - - #[tokio::test] - async fn test_check_app_available_nonexist() { - let package_id = "com.termux"; - let mut server = Server::new_async().await; - let _m = server - .mock("GET", format!("/packages/{}", package_id).as_str()) - .with_status(200) - .create_async() - .await; - - let provider = FDroidProvider::new(); - let nonexist_package_id = "nonexist"; - let app_data = AppDataMap::from([(ANDROID_APP_TYPE, nonexist_package_id)]); - let proxy_url = format!("{} -> {}", FDROID_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - let fin = FIn::new_with_frag(&app_data, &hub_data, None); - let fout = provider.check_app_available(&fin).await; - assert!(!fout.result.unwrap()); - } - - #[tokio::test] - async fn test_get_releases() { - let body = fs::read_to_string("tests/files/web/f-droid.xml").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repo/index.xml") - .with_status(200) - .with_body(body) - .create(); - - let package_id = "org.fdroid.fdroid.privileged"; - let provider = FDroidProvider::new(); - let app_data = AppDataMap::from([(ANDROID_APP_TYPE, package_id)]); - let proxy_url = format!("{} -> {}", FDROID_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - let fin = FIn::new_with_frag(&app_data, &hub_data, None); - let fout = provider.get_releases(&fin).await; - let releases = fout.result.unwrap(); - assert!(!releases.is_empty()); - assert_eq!(releases[0].assets[0].file_type, "apk"); - } - - #[tokio::test] - async fn test_get_releases_nonexist() { - let body = fs::read_to_string("tests/files/web/f-droid.xml").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repo/index.xml") - .with_status(200) - .with_body(body) - .create(); - - let package_id = "nonexist"; - let provider = FDroidProvider::new(); - let app_data = AppDataMap::from([(ANDROID_APP_TYPE, package_id)]); - let proxy_url = format!("{} -> {}", FDROID_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - let fin = FIn::new_with_frag(&app_data, &hub_data, None); - let fout = provider.get_releases(&fin).await; - let releases = fout.result.unwrap(); - assert!(releases.is_empty()); - } - - #[tokio::test] - async fn test_get_releases_assets_type() { - let body = fs::read_to_string("tests/files/web/f-droid.xml").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repo/index.xml") - .with_status(200) - .with_body(body) - .create(); - - let package_id = "org.fdroid.fdroid.privileged.ota"; - let provider = FDroidProvider::new(); - let app_data = AppDataMap::from([(ANDROID_APP_TYPE, package_id)]); - let proxy_url = format!("{} -> {}", FDROID_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - let fin = FIn::new_with_frag(&app_data, &hub_data, None); - let fout = provider.get_releases(&fin).await; - let releases = fout.result.unwrap(); - assert!(!releases.is_empty()); - assert_eq!(releases[0].assets[0].file_type, "zip"); - } -} diff --git a/src/websdk/repo/provider/github.rs b/src/websdk/repo/provider/github.rs deleted file mode 100644 index f7e8bc8..0000000 --- a/src/websdk/repo/provider/github.rs +++ /dev/null @@ -1,262 +0,0 @@ -use async_trait::async_trait; -use bytes::Bytes; -use serde_json::Value; -use std::collections::HashMap; - -use super::super::data::release::*; -use super::base_provider::*; - -use crate::utils::{ - http::{get, head, http_status_is_ok}, - versioning::Version, -}; - -pub const GITHUB_API_URL: &str = "https://api.github.com"; -const GITHUB_URL: &str = "https://github.com"; - -const VERSION_NUMBER_KEY: &str = "version_number_key"; -const VERSION_CODE_KEY: &str = "version_code_key"; - -pub struct GitHubProvider; - -impl GitHubProvider { - pub fn new() -> Self { - GitHubProvider {} - } - - fn get_token_header(&self, fin: &FIn) -> HashMap { - let mut map = HashMap::new(); - // check if token is empty or blank - let token = fin - .data_map - .hub_data - .get("token") - .filter(|t| !t.trim().is_empty()) - .or_else(|| fin.data_map.app_data.get("token")); - if let Some(token) = token { - map.insert("Authorization".to_string(), format!("Bearer {}", token)); - } - map - } -} - -impl BaseProviderExt for GitHubProvider {} - -#[async_trait] -impl BaseProvider for GitHubProvider { - fn get_cache_request_key( - &self, - function_type: &FunctionType, - data_map: &DataMap, - ) -> Vec { - let id_map = data_map.app_data; - match function_type { - FunctionType::CheckAppAvailable => vec![format!( - "{}/{}/{}/HEAD", - GITHUB_URL, id_map["owner"], id_map["repo"] - )], - FunctionType::GetLatestRelease | FunctionType::GetReleases => vec![format!( - "{}/repos/{}/{}/releases", - GITHUB_API_URL, id_map["owner"], id_map["repo"] - )], - FunctionType::GetDownload => vec![], - } - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - let id_map = fin.data_map.app_data; - let api_url = format!("{}/{}/{}", GITHUB_URL, id_map["owner"], id_map["repo"]); - let api_url = self.replace_proxy_url(fin, &api_url); - - if let Ok(parsed_url) = api_url.parse() { - if let Ok(rsp) = head(parsed_url, &HashMap::new()).await { - return FOut::new(http_status_is_ok(rsp.status)); - } - } - FOut::new_empty() - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - let id_map = fin.data_map.app_data; - let url = format!( - "{}/repos/{}/{}/releases", - GITHUB_API_URL, id_map["owner"], id_map["repo"] - ); - let url = self.replace_proxy_url(fin, &url); - let mut fout = FOut::new_empty(); - let cache_body = fin.get_cache(&url); - let mut rsp_body = None; - if cache_body.is_none() { - if let Ok(parsed_url) = url.parse() { - let header_map = { - let mut map = HashMap::new(); - map.insert("User-Agent".to_string(), "UpgradeAll-App".to_string()); - let token_map = self.get_token_header(fin); - map.extend(token_map); - map - }; - if let Ok(rsp) = get(parsed_url, &header_map).await { - if let Some(content) = rsp.body { - rsp_body = Some(content); - } - } - } - } - - let body: &Bytes; - if let Some(ref content) = rsp_body { - body = content; - } else if let Some(content) = cache_body { - body = content; - } else { - return fout; - } - - if let Ok(data) = serde_json::from_slice::>(body) { - let release_list = data - .iter() - .filter_map(|json| { - let assets_data = match json.get("assets") { - Some(assets) => assets - .as_array()? - .iter() - .filter_map(|asset| { - let file_name = asset.get("name")?.as_str()?.to_string(); - let file_type = asset.get("content_type")?.as_str()?.to_string(); - let download_url = - asset.get("browser_download_url")?.as_str()?.to_string(); - Some(AssetData { - file_name, - file_type, - download_url, - }) - }) - .collect(), - None => vec![], - }; - let mut keys_to_try = vec!["name", "tag_name"]; - if let Some(tag) = fin.data_map.hub_data.get(VERSION_NUMBER_KEY) { - keys_to_try.insert(0, tag); - } - let mut version_number: Option = None; - - for key in keys_to_try.iter() { - if let Some(value) = json.get(key).and_then(|v| v.as_str()) { - if Version::new(value.to_string()).is_valid() { - version_number = Some(value.to_string()); - break; - } - } - } - let changelog = json.get("body")?.as_str()?.to_string(); - - let mut extra = None; - if let Some(tag) = fin.data_map.hub_data.get(VERSION_CODE_KEY) { - if let Some(value) = json.get(tag) { - extra = Some(HashMap::from([(tag.to_string(), value.to_string())])); - } - } - Some(ReleaseData { - version_number: version_number?.to_string(), - changelog, - assets: assets_data, - extra, - }) - }) - .collect::>(); - fout = fout.set_data(release_list); - }; - - if let Some(content) = rsp_body { - fout.set_cached_map(HashMap::from([(url, content)])) - } else { - fout - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_check_app_available() { - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/DUpdateSystem/UpgradeAll") - .with_status(200) - .create_async() - .await; - - let id_map = AppDataMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let proxy_url = format!("{} -> {}", GITHUB_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - - let github_provider = GitHubProvider::new(); - assert!(github_provider - .check_app_available(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap()); - } - - #[tokio::test] - async fn test_get_releases() { - let body = fs::read_to_string("tests/files/web/github_api_release.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repos/DUpdateSystem/UpgradeAll/releases") - .with_status(200) - .with_body(body) - .create(); - - let id_map = AppDataMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let proxy_url = format!("{} -> {}", GITHUB_API_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - - let github_provider = GitHubProvider::new(); - let releases = github_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - let release_json = - fs::read_to_string("tests/files/data/provider_github_release.json").unwrap(); - let releases_saved = serde_json::from_str::>(&release_json).unwrap(); - assert_eq!(releases, releases_saved) - } - - #[tokio::test] - async fn test_get_releases_token() { - let mut id_map = AppDataMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let test_token = std::env::var("GITHUB_TOKEN"); - if test_token.is_err() { - return; - } - let test_token = test_token.unwrap(); - let hub_data = HubDataMap::from([("token", test_token.as_str())]); - - let github_provider = GitHubProvider::new(); - let releases = github_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - assert!(!releases.is_empty()); - - let hub_data = HubDataMap::from([("token", " ")]); - id_map.insert("token", &test_token); - - let releases = github_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - assert!(!releases.is_empty()); - } -} diff --git a/src/websdk/repo/provider/gitlab.rs b/src/websdk/repo/provider/gitlab.rs deleted file mode 100644 index d3a67ad..0000000 --- a/src/websdk/repo/provider/gitlab.rs +++ /dev/null @@ -1,327 +0,0 @@ -use async_trait::async_trait; -use bytes::Bytes; -use serde_json::Value; -use std::collections::HashMap; - -use super::super::data::release::*; -use super::base_provider::*; -use markdown::{mdast::Node, to_mdast, ParseOptions}; - -use crate::utils::{ - http::{get, head, http_status_is_ok}, - versioning::Version, -}; - -const GITLAB_URL: &str = "https://gitlab.com"; -const GITLAB_API_URL: &str = "https://gitlab.com/api/v4/projects"; - -const VERSION_NUMBER_KEY: &str = "version_number_key"; - -pub struct GitLabProvider; - -impl GitLabProvider { - pub fn new() -> GitLabProvider { - GitLabProvider {} - } -} - -impl BaseProviderExt for GitLabProvider {} - -impl GitLabProvider { - async fn get_project_id(&self, fin: &FIn<'_>) -> Option { - let id_map = fin.data_map.app_data; - let api_url = format!( - "{}/{}%2F{}", - GITLAB_API_URL, id_map["owner"], id_map["repo"] - ); - let api_url = self.replace_proxy_url(fin, &api_url); - - if let Ok(parsed_url) = api_url.parse() { - if let Ok(rsp) = get(parsed_url, &HashMap::new()).await { - if let Some(body) = rsp.body { - if let Ok(data) = serde_json::from_slice::>(&body) { - return Some(data.get("id")?.as_number()?.to_string()); - } - } - } - } - None - } - - fn try_get_download_url_from_changelog(&self, changelog: &str) -> Vec<(String, String)> { - let changelog_ast = to_mdast(changelog, &ParseOptions::default()); - if changelog_ast.is_err() { - return vec![]; - } - let changelog_ast = changelog_ast.unwrap(); - - fn get_link_text(nodes: &Vec) -> Vec<(String, String)> { - let mut url_map = vec![]; - for node in nodes { - let mut name = String::new(); - if let Node::Link(link) = node { - for c in &link.children { - if let Node::Text(text) = c { - name.push_str(&text.value); - } - } - url_map.push((name, link.url.to_string())); - } - if let Some(child) = node.children() { - url_map.extend(get_link_text(child)); - } - } - url_map - } - if let Some(children) = changelog_ast.children() { - return get_link_text(children); - } - vec![] - } - - fn fix_download_url(&self, download_url: &str, project_id: &str) -> String { - if download_url.starts_with("/uploads/") { - return format!("{}/-/project/{}{}", GITLAB_URL, project_id, download_url); - } - download_url.to_string() - } -} - -#[async_trait] -impl BaseProvider for GitLabProvider { - fn get_cache_request_key( - &self, - function_type: &FunctionType, - data_map: &DataMap, - ) -> Vec { - let id_map = data_map.app_data; - match function_type { - FunctionType::CheckAppAvailable => vec![format!( - "{}/{}/{}/HEAD", - GITLAB_URL, id_map["owner"], id_map["repo"] - )], - FunctionType::GetLatestRelease | FunctionType::GetReleases => vec![format!( - "{}/{}/{}/releases", - GITLAB_API_URL, id_map["owner"], id_map["repo"] - )], - FunctionType::GetDownload => vec![], - } - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - let id_map = fin.data_map.app_data; - let api_url = format!("{}/{}/{}", GITLAB_URL, id_map["owner"], id_map["repo"]); - let api_url = self.replace_proxy_url(fin, &api_url); - - if let Ok(parsed_url) = api_url.parse() { - if let Ok(rsp) = head(parsed_url, &HashMap::new()).await { - return FOut::new(http_status_is_ok(rsp.status)); - } - } - FOut::new_empty() - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - let id_map = fin.data_map.app_data; - let url = format!( - "{}/{}%2F{}/releases", - GITLAB_API_URL, id_map["owner"], id_map["repo"] - ); - let url = self.replace_proxy_url(fin, &url); - let mut fout = FOut::new_empty(); - let cache_body = fin.get_cache(&url); - let mut rsp_body = None; - if cache_body.is_none() { - if let Ok(parsed_url) = url.parse() { - let header_map = { - let mut map = HashMap::new(); - map.insert("User-Agent".to_string(), "Awesome-Octocat-App".to_string()); - map - }; - if let Ok(rsp) = get(parsed_url, &header_map).await { - if let Some(content) = rsp.body { - rsp_body = Some(content); - } - } - } - } - - let body: &Bytes; - if let Some(ref content) = rsp_body { - body = content; - } else if let Some(content) = cache_body { - body = content; - } else { - return fout; - } - - if let Ok(data) = serde_json::from_slice::>(body) { - let mut release_list = data - .iter() - .filter_map(|json| { - let assets_data = match json.get("assets")?.get("links") { - Some(links) => links - .as_array()? - .iter() - .filter_map(|asset| { - let file_name = asset.get("name")?.as_str()?.to_string(); - let file_type = asset.get("link_type")?.as_str()?.to_string(); - let download_url = asset.get("url")?.as_str()?.to_string(); - Some(AssetData { - file_name, - file_type, - download_url, - }) - }) - .collect(), - None => vec![], - }; - let mut keys_to_try = vec!["name", "tag_name"]; - if let Some(tag) = fin.data_map.hub_data.get(VERSION_NUMBER_KEY) { - keys_to_try.insert(0, tag); - } - let mut version_number: Option = None; - - for key in keys_to_try.iter() { - if let Some(value) = json.get(key).and_then(|v| v.as_str()) { - if Version::new(value.to_string()).is_valid() { - version_number = Some(value.to_string()); - break; - } - } - } - let changelog = json.get("description")?.as_str()?.to_string(); - let extra_download_url = self.try_get_download_url_from_changelog(&changelog); - let assets_data = assets_data - .into_iter() - .chain(extra_download_url.into_iter().map(|(k, v)| AssetData { - file_name: k, - file_type: "".to_string(), - download_url: v, - })) - .collect(); - Some(ReleaseData { - version_number: version_number?.to_string(), - changelog, - assets: assets_data, - extra: None, - }) - }) - .collect::>(); - let mut project_id = None; - for release in release_list.iter_mut() { - for asset in release.assets.iter_mut() { - if asset.download_url.starts_with("/uploads/") { - if project_id.is_none() { - project_id = self.get_project_id(fin).await; - } - if let Some(project_id) = &project_id { - asset.download_url = - self.fix_download_url(&asset.download_url, project_id); - } - } - } - } - fout = fout.set_data(release_list); - }; - - if let Some(content) = rsp_body { - fout.set_cached_map(HashMap::from([(url, content)])) - } else { - fout - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_check_app_available() { - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/fdroid/fdroidclient") - .with_status(200) - .create_async() - .await; - - let id_map = AppDataMap::from([("owner", "fdroid"), ("repo", "fdroidclient")]); - let proxy_url = format!("{} -> {}", GITLAB_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - - let gitlab_provider = GitLabProvider::new(); - assert!(gitlab_provider - .check_app_available(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap()); - } - - #[tokio::test] - async fn test_get_releases() { - let body = fs::read_to_string("tests/files/web/gitlab_api_release.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/fdroid%2Ffdroidclient/releases") - .with_status(200) - .with_body(body) - .create(); - - let id_map = AppDataMap::from([("owner", "fdroid"), ("repo", "fdroidclient")]); - let proxy_url = format!("{} -> {}", GITLAB_API_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - - let gitlab_provider = GitLabProvider::new(); - let releases = gitlab_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - let release_json = - fs::read_to_string("tests/files/data/provider_gitlab_release.json").unwrap(); - let releases_saved = serde_json::from_str::>(&release_json).unwrap(); - assert_eq!(releases, releases_saved) - } - - #[tokio::test] - async fn test_try_get_download_url_from_changelog_in_release() { - let body = - fs::read_to_string("tests/files/web/gitlab_api_release_AuroraStore.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/AuroraOSS%2FAuroraStore/releases") - .with_status(200) - .with_body(body) - .create(); - let project_body = - fs::read_to_string("tests/files/web/gitlab_api_project_AuroraStore.json").unwrap(); - let _m = server - .mock("GET", "/AuroraOSS%2FAuroraStore") - .with_status(200) - .with_body(project_body) - .create_async() - .await; - - let id_map = AppDataMap::from([("owner", "AuroraOSS"), ("repo", "AuroraStore")]); - let proxy_url = format!("{} -> {}", GITLAB_API_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - - let gitlab_provider = GitLabProvider::new(); - let releases = gitlab_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - let release_json = - fs::read_to_string("tests/files/data/provider_gitlab_release_AuroraStore.json") - .unwrap(); - let releases_saved = serde_json::from_str::>(&release_json).unwrap(); - - assert_eq!(releases, releases_saved) - } -} diff --git a/src/websdk/repo/provider/lsposed_repo.rs b/src/websdk/repo/provider/lsposed_repo.rs deleted file mode 100644 index c7dc50c..0000000 --- a/src/websdk/repo/provider/lsposed_repo.rs +++ /dev/null @@ -1,224 +0,0 @@ -use std::collections::HashMap; - -use async_trait::async_trait; -use bytes::Bytes; -use serde_json::Value; - -use crate::utils::http::get; -use crate::utils::versioning::Version; - -use super::super::data::release::*; -use super::base_provider::*; - -const LSPOSED_REPO_API_URL: &str = "https://modules.lsposed.org/modules.json"; - -pub struct LsposedRepoProvider; - -impl LsposedRepoProvider { - pub fn new() -> Self { - LsposedRepoProvider {} - } - - fn get_app_json(package_name: &str, body: &Bytes) -> Option { - if let Ok(json) = serde_json::from_slice::>(body) { - for i in json { - if let Some(name) = i.get("name") { - if let Some(name_str) = name.as_str() { - if name_str == package_name { - return Some(i); - } - } - } - } - } - None - } -} - -impl BaseProviderExt for LsposedRepoProvider {} - -#[async_trait] -impl BaseProvider for LsposedRepoProvider { - fn get_cache_request_key( - &self, - _function_type: &FunctionType, - _data_map: &DataMap, - ) -> Vec { - vec![LSPOSED_REPO_API_URL.to_string()] - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - let url = self.replace_proxy_url(fin, LSPOSED_REPO_API_URL); - let mut fout = FOut::new_empty(); - let cache_body = fin.get_cache(&url); - let mut rsp_body = None; - if cache_body.is_none() { - if let Ok(parsed_url) = url.parse() { - let map = HashMap::new(); - if let Ok(rsp) = get(parsed_url, &map).await { - if let Some(content) = rsp.body { - fout = fout.set_cache(&url, content.clone()); - rsp_body = Some(content); - } - } - } - } - let body: &Bytes; - if let Some(ref content) = rsp_body { - body = content; - } else if let Some(content) = cache_body { - body = content; - } else { - return fout; - } - let id_map = fin.data_map.app_data; - let package_id = id_map[ANDROID_APP_TYPE]; - let json = LsposedRepoProvider::get_app_json(package_id, body); - fout.set_data(json.is_some()) - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - let url = self.replace_proxy_url(fin, LSPOSED_REPO_API_URL); - let mut fout = FOut::new_empty(); - let cache_body = fin.get_cache(&url); - let mut rsp_body = None; - if cache_body.is_none() { - if let Ok(parsed_url) = url.parse() { - let map = HashMap::new(); - if let Ok(rsp) = get(parsed_url, &map).await { - if let Some(content) = rsp.body { - fout = fout.set_cache(&url, content.clone()); - rsp_body = Some(content); - } - } - } - } - let body: &Bytes; - if let Some(ref content) = rsp_body { - body = content; - } else if let Some(content) = cache_body { - body = content; - } else { - return fout; - } - let id_map = fin.data_map.app_data; - let package_id = id_map[ANDROID_APP_TYPE]; - let json = LsposedRepoProvider::get_app_json(package_id, body); - if let Some(json) = json { - if let Some(releases_block) = json.get("releases") { - if let Some(release_json) = releases_block.as_array() { - let release_list = release_json - .iter() - .filter_map(|json| { - if let Some(assets_block) = json.get("releaseAssets") { - if let Some(assets) = assets_block.as_array() { - let assets_data = - assets - .iter() - .filter_map(|asset| { - Some(AssetData { - file_name: asset.get("name")?.as_str()?.to_string(), - file_type: asset - .get("contentType")? - .as_str() - .unwrap_or("application/vnd.android.package-archive") - .to_string(), - download_url: asset - .get("downloadUrl")? - .as_str()? - .to_string(), - }) - }) - .collect(); - let mut version_number: Option = None; - - for key in &["name", "tagName"] { - if let Some(value) = json.get(key).and_then(|v| v.as_str()) - { - if Version::new(value.to_string()).is_valid() { - version_number = Some(value.to_string()); - break; - } - } - } - let changelog = json - .get("descriptionHTML") - .and_then(|v| v.as_str()) - .unwrap_or("") - .to_string(); - return Some(ReleaseData { - version_number: version_number?.to_string(), - changelog, - assets: assets_data, - extra: None, - }); - } - } - None - }) - .collect(); - fout = fout.set_data(release_list); - } - } - } - fout - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_check_app_available() { - let body = fs::read_to_string("tests/files/web/lsposed_modules.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/modules.json") - .with_status(200) - .with_body(body) - .create_async() - .await; - - let id_map = AppDataMap::from([(ANDROID_APP_TYPE, "com.agoines.relaxhelp")]); - let proxy_url = format!("{} -> {}", LSPOSED_REPO_API_URL, server.url()); - let hub_data = HubDataMap::from([("proxy_url", proxy_url.as_str())]); - - let lsposed_provider = LsposedRepoProvider::new(); - assert!(lsposed_provider - .check_app_available(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap()); - } - - #[tokio::test] - async fn test_get_releases() { - let body = fs::read_to_string("tests/files/web/lsposed_modules.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/modules.json") - .with_status(200) - .with_body(body) - .create_async() - .await; - - let id_map = AppDataMap::from([(ANDROID_APP_TYPE, "com.agoines.relaxhelp")]); - let proxy_url = format!("{} -> {}", LSPOSED_REPO_API_URL, server.url()); - let hub_data = HubDataMap::from([("proxy_url", proxy_url.as_str())]); - - let lsposed_provider = LsposedRepoProvider::new(); - let releases = lsposed_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - let release_json = - fs::read_to_string("tests/files/data/provider_lsposed_releases.json").unwrap(); - let releases_saved = serde_json::from_str::>(&release_json).unwrap(); - assert_eq!(releases, releases_saved) - } -} diff --git a/src/websdk/repo/provider/outside_rpc.rs b/src/websdk/repo/provider/outside_rpc.rs deleted file mode 100644 index ddfcc79..0000000 --- a/src/websdk/repo/provider/outside_rpc.rs +++ /dev/null @@ -1,115 +0,0 @@ -use super::super::data::release::*; -use super::base_provider::*; -use crate::rpc::client::*; -use crate::rpc::data::DownloadItemData; -use async_trait::async_trait; - -pub struct OutsideProvider { - pub uuid: String, - pub url: String, -} - -impl OutsideProvider { - pub fn new(uuid: String, url: String) -> Self { - OutsideProvider { uuid, url } - } -} - -#[async_trait] -impl BaseProvider for OutsideProvider { - fn get_cache_request_key( - &self, - _function_type: &FunctionType, - _data_map: &DataMap, - ) -> Vec { - vec![] - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - FOut { - result: match Client::new(&self.url).map(|client| async move { - client - .check_app_available( - &self.uuid, - fin.data_map.app_data.to_owned(), - fin.data_map.hub_data.to_owned(), - ) - .await - }) { - Ok(result) => match result.await { - Ok(result) => Ok(result), - Err(e) => Err(Box::new(e)), - }, - Err(e) => Err(Box::new(e)), - }, - cached_map: None, - } - } - - async fn get_latest_release(&self, fin: &FIn) -> FOut { - FOut { - result: match Client::new(&self.url).map(|client| async move { - client - .get_latest_release( - &self.uuid, - fin.data_map.app_data.to_owned(), - fin.data_map.hub_data.to_owned(), - ) - .await - }) { - Ok(result) => match result.await { - Ok(result) => Ok(result), - Err(e) => Err(Box::new(e)), - }, - Err(e) => Err(Box::new(e)), - }, - cached_map: None, - } - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - FOut { - result: match Client::new(&self.url).map(|client| async move { - client - .get_releases( - &self.uuid, - fin.data_map.app_data.to_owned(), - fin.data_map.hub_data.to_owned(), - ) - .await - }) { - Ok(result) => match result.await { - Ok(result) => Ok(result), - Err(e) => Err(Box::new(e)), - }, - Err(e) => Err(Box::new(e)), - }, - cached_map: None, - } - } - - async fn get_download(&self, fin: &FIn, asset_index: &[i32]) -> FOut> { - FOut { - result: match Client::new(&self.url).map(|client| async move { - client - .get_download( - &self.uuid, - fin.data_map.app_data.to_owned(), - fin.data_map.hub_data.to_owned(), - asset_index, - ) - .await - }) { - Ok(result) => match result.await { - Ok(result) => Ok(result), - Err(e) => Err(Box::new(e)), - }, - Err(e) => Err(Box::new(e)), - }, - cached_map: None, - } - } -} - -#[cfg(test)] -mod tests {} From 475c3b2823fb9fb56cde7430711b82c2cbe79097 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 21 Jun 2026 23:53:58 +0800 Subject: [PATCH 02/72] feat(update): add deterministic update selection Add tokenized version comparison and selection policy that skips ignored versions while choosing the highest newer candidate. --- crates/getter-core/src/lib.rs | 1 + crates/getter-core/src/update.rs | 272 +++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 crates/getter-core/src/update.rs diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 45a464d..da959a3 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -11,6 +11,7 @@ use std::str::FromStr; pub mod lua; pub mod repository; +pub mod update; /// Error returned when parsing or constructing a [`PackageId`]. #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs new file mode 100644 index 0000000..ab3256c --- /dev/null +++ b/crates/getter-core/src/update.rs @@ -0,0 +1,272 @@ +//! Update selection helpers owned by getter core. + +use crate::{PackageId, SelectedUpdate, UpdateArtifact, UpdateCandidate}; +use std::cmp::Ordering; + +/// User state that affects update selection. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct UpdateSelectionPolicy { + /// Candidate version the user chose to ignore/mark as skipped. + pub ignored_version: Option, +} + +/// Compare human-facing version strings using a deterministic token ordering. +/// +/// This intentionally starts small: digit runs compare numerically, ASCII text +/// runs compare case-insensitively, common separators are ignored, and a text +/// suffix such as `beta` or `rc` sorts before the final release with the same +/// numeric prefix. +/// +/// Examples: +/// +/// - `1.10` > `1.2` +/// - `1.0.0` == `1.0` +/// - `1.0.0-beta` < `1.0.0` +pub fn compare_versions(left: &str, right: &str) -> Ordering { + let left = tokenize_version(left); + let right = tokenize_version(right); + let max_len = left.len().max(right.len()); + + for index in 0..max_len { + match (left.get(index), right.get(index)) { + (Some(left), Some(right)) => { + let ordering = left.cmp(right); + if ordering != Ordering::Equal { + return ordering; + } + } + (Some(VersionToken::Number(value)), None) if *value == 0 => continue, + (None, Some(VersionToken::Number(value))) if *value == 0 => continue, + (Some(VersionToken::Text(_)), None) => return Ordering::Less, + (None, Some(VersionToken::Text(_))) => return Ordering::Greater, + (Some(_), None) => return Ordering::Greater, + (None, Some(_)) => return Ordering::Less, + (None, None) => break, + } + } + + Ordering::Equal +} + +/// Select the best update candidate for a package. +/// +/// The selected candidate is the highest version that is newer than the +/// installed version and not equal to the user's ignored version. If no +/// installed version is known, the highest non-ignored candidate is selected. +pub fn select_update( + package_id: PackageId, + installed_version: Option<&str>, + candidates: &[UpdateCandidate], + policy: &UpdateSelectionPolicy, +) -> Option { + candidates + .iter() + .filter(|candidate| is_selectable(candidate, installed_version, policy)) + .max_by(|left, right| compare_versions(&left.version, &right.version)) + .cloned() + .map(|candidate| SelectedUpdate { + package_id, + artifact: first_artifact(&candidate), + candidate, + }) +} + +fn is_selectable( + candidate: &UpdateCandidate, + installed_version: Option<&str>, + policy: &UpdateSelectionPolicy, +) -> bool { + if policy + .ignored_version + .as_deref() + .is_some_and(|ignored| compare_versions(&candidate.version, ignored) == Ordering::Equal) + { + return false; + } + + match installed_version { + Some(installed) => compare_versions(&candidate.version, installed) == Ordering::Greater, + None => true, + } +} + +fn first_artifact(candidate: &UpdateCandidate) -> Option { + candidate.artifacts.first().cloned() +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum VersionToken { + Number(u128), + Text(String), +} + +impl Ord for VersionToken { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Self::Number(left), Self::Number(right)) => left.cmp(right), + (Self::Text(left), Self::Text(right)) => left.cmp(right), + (Self::Number(_), Self::Text(_)) => Ordering::Greater, + (Self::Text(_), Self::Number(_)) => Ordering::Less, + } + } +} + +impl PartialOrd for VersionToken { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +fn tokenize_version(version: &str) -> Vec { + let mut tokens = Vec::new(); + let mut current = String::new(); + let mut current_kind: Option = None; + + for ch in version.chars() { + let kind = TokenKind::from_char(ch); + match (kind, current_kind) { + (Some(kind), Some(existing)) if kind == existing => current.push(ch), + (Some(kind), Some(_)) => { + push_token(&mut tokens, ¤t, current_kind.expect("kind exists")); + current.clear(); + current.push(ch); + current_kind = Some(kind); + } + (Some(kind), None) => { + current.push(ch); + current_kind = Some(kind); + } + (None, Some(existing)) => { + push_token(&mut tokens, ¤t, existing); + current.clear(); + current_kind = None; + } + (None, None) => {} + } + } + + if let Some(kind) = current_kind { + push_token(&mut tokens, ¤t, kind); + } + + tokens +} + +fn push_token(tokens: &mut Vec, value: &str, kind: TokenKind) { + match kind { + TokenKind::Number => tokens.push(VersionToken::Number(parse_u128_lossy(value))), + TokenKind::Text => tokens.push(VersionToken::Text(value.to_ascii_lowercase())), + } +} + +fn parse_u128_lossy(value: &str) -> u128 { + value.parse::().unwrap_or(u128::MAX) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum TokenKind { + Number, + Text, +} + +impl TokenKind { + fn from_char(ch: char) -> Option { + if ch.is_ascii_digit() { + Some(Self::Number) + } else if ch.is_ascii_alphabetic() { + Some(Self::Text) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn compares_numeric_segments_numerically() { + assert_eq!(compare_versions("1.10", "1.2"), Ordering::Greater); + assert_eq!(compare_versions("1.0.0", "1.0"), Ordering::Equal); + assert_eq!(compare_versions("2", "10"), Ordering::Less); + } + + #[test] + fn treats_prerelease_suffix_as_older_than_release() { + assert_eq!(compare_versions("1.0.0-beta", "1.0.0"), Ordering::Less); + assert_eq!( + compare_versions("1.0.0-rc1", "1.0.0-beta2"), + Ordering::Greater + ); + } + + #[test] + fn selects_highest_newer_candidate() { + let selected = select_update( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0"), + &[candidate("1.0.1"), candidate("1.2.0"), candidate("0.9.0")], + &UpdateSelectionPolicy::default(), + ) + .unwrap(); + + assert_eq!(selected.candidate.version, "1.2.0"); + } + + #[test] + fn respects_ignored_version() { + let selected = select_update( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0"), + &[candidate("1.1.0"), candidate("1.2.0")], + &UpdateSelectionPolicy { + ignored_version: Some("1.2.0".to_owned()), + }, + ) + .unwrap(); + + assert_eq!(selected.candidate.version, "1.1.0"); + } + + #[test] + fn returns_none_when_no_candidate_is_newer() { + assert!(select_update( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("2.0.0"), + &[candidate("1.9.0"), candidate("2.0.0")], + &UpdateSelectionPolicy::default(), + ) + .is_none()); + } + + #[test] + fn unknown_installed_version_selects_highest_candidate() { + let selected = select_update( + "android/org.fdroid.fdroid".parse().unwrap(), + None, + &[candidate("1.0.0-beta"), candidate("1.0.0")], + &UpdateSelectionPolicy::default(), + ) + .unwrap(); + + assert_eq!(selected.candidate.version, "1.0.0"); + assert_eq!( + selected.artifact.as_ref().unwrap().file_name.as_deref(), + Some("app.apk") + ); + } + + fn candidate(version: &str) -> UpdateCandidate { + UpdateCandidate { + version: version.to_owned(), + channel: None, + source: None, + artifacts: vec![UpdateArtifact { + name: "APK".to_owned(), + url: format!("https://example.invalid/{version}.apk"), + file_name: Some("app.apk".to_owned()), + }], + } + } +} From 2ca92b80cdc89ac9c18f45ba812ed52098d4c8f7 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 08:55:46 +0800 Subject: [PATCH 03/72] fix: satisfy clippy on stable Use sort_by_key for repository package ordering and elide needless lifetimes in highest_priority. --- crates/getter-core/src/repository.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index 0bacf47..fe9fa21 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -105,7 +105,7 @@ impl RepositoryLayout { let mut packages = Vec::new(); collect_package_files(&packages_dir, &packages_dir, &mut packages)?; - packages.sort_by(|a, b| a.id.to_string().cmp(&b.id.to_string())); + packages.sort_by_key(|package| package.id.to_string()); Ok(Self { root, @@ -224,7 +224,7 @@ pub fn package_id_from_path( }) } -pub fn highest_priority<'a, T, F>(items: &'a [T], priority: F) -> Option<&'a T> +pub fn highest_priority(items: &[T], priority: F) -> Option<&T> where F: Fn(&T) -> RepositoryPriority, { From 90c02bac7442e57d422ab0e2fd7e783c15bc11fe Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 10:04:20 +0800 Subject: [PATCH 04/72] fix(lua): support lib-prefixed repository modules Preserve the documented require("lib.xxx") contract by extending the Lua package path while keeping unprefixed lib_dir lookups for compatibility. --- crates/getter-core/src/lua.rs | 44 ++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index fd6348e..0583664 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -85,10 +85,14 @@ pub fn evaluate_package_source( fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Result<()> { let package: Table = lua.globals().get("package")?; let current_path: String = package.get("path")?; + let root_lib_pattern = repository.root.join("?.lua"); + let root_nested_lib_pattern = repository.root.join("?/init.lua"); let lib_pattern = repository.lib_dir.join("?.lua"); let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); let new_path = format!( - "{};{};{}", + "{};{};{};{};{}", + root_lib_pattern.to_string_lossy(), + root_nested_lib_pattern.to_string_lossy(), lib_pattern.to_string_lossy(), nested_lib_pattern.to_string_lossy(), current_path @@ -457,4 +461,42 @@ return android.local_app { assert_eq!(package.name, "F-Droid"); assert_eq!(package.installed.len(), 1); } + + #[test] + fn require_can_load_repository_lib_modules_with_lib_prefix() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + layout.lib_dir.join("android.lua"), + r#" +return { + local_app = function(input) + return { + id = input.id, + name = input.name, + installed = { + { kind = "android_package", package_name = input.package_name }, + }, + } + end +} +"#, + ) + .unwrap(); + fs::write( + &package_path, + r#" +local android = require("lib.android") +return android.local_app { + id = "android/org.fdroid.fdroid", + name = "F-Droid", + package_name = "org.fdroid.fdroid", +} +"#, + ) + .unwrap(); + + let package = evaluate_package_file(&layout, &package_path).unwrap(); + assert_eq!(package.name, "F-Droid"); + assert_eq!(package.installed.len(), 1); + } } From bf932d655727ad2a9fd1c856ac2a047db3c95d54 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 10:31:26 +0800 Subject: [PATCH 05/72] fix(lua): constrain lib-prefixed module loading Support the documented require("lib.*") contract with a constrained searcher instead of exposing the repository root through package.path. --- crates/getter-core/src/lua.rs | 97 ++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 6 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 0583664..b8e905b 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -85,19 +85,80 @@ pub fn evaluate_package_source( fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Result<()> { let package: Table = lua.globals().get("package")?; let current_path: String = package.get("path")?; - let root_lib_pattern = repository.root.join("?.lua"); - let root_nested_lib_pattern = repository.root.join("?/init.lua"); let lib_pattern = repository.lib_dir.join("?.lua"); let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); let new_path = format!( - "{};{};{};{};{}", - root_lib_pattern.to_string_lossy(), - root_nested_lib_pattern.to_string_lossy(), + "{};{};{}", lib_pattern.to_string_lossy(), nested_lib_pattern.to_string_lossy(), current_path ); - package.set("path", new_path) + package.set("path", new_path)?; + install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone()) +} + +fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> mlua::Result<()> { + let searchers: Table = package.get("searchers")?; + let searcher = lua.create_function(move |lua, module: String| { + let Some(module) = module.strip_prefix("lib.") else { + return lua + .create_string("\n\tconstrained repository lib searcher only handles lib.* modules") + .map(Value::String); + }; + + let Some(relative_module) = module_to_relative_path(module) else { + return lua + .create_string(format!( + "\n\tinvalid repository lib module name 'lib.{module}'" + )) + .map(Value::String); + }; + + let module_path = lib_dir.join(&relative_module).with_extension("lua"); + let init_path = lib_dir.join(&relative_module).join("init.lua"); + for candidate in [&module_path, &init_path] { + if candidate.is_file() { + let source = fs::read_to_string(candidate).map_err(mlua::Error::external)?; + let chunk = lua + .load(&source) + .set_name(candidate.to_string_lossy().as_ref()); + return chunk.into_function().map(Value::Function); + } + } + + lua.create_string(format!( + "\n\tno repository lib module 'lib.{module}' in {}", + lib_dir.display() + )) + .map(Value::String) + })?; + + let len = searchers.raw_len(); + for index in (2..=len).rev() { + let value: Value = searchers.raw_get(index)?; + searchers.raw_set(index + 1, value)?; + } + searchers.raw_set(2, searcher) +} + +fn module_to_relative_path(module: &str) -> Option { + let mut path = PathBuf::new(); + for part in module.split('.') { + if part.is_empty() + || part == ".." + || part.contains('/') + || part.contains('\\') + || part.contains(std::path::MAIN_SEPARATOR) + { + return None; + } + path.push(part); + } + if path.as_os_str().is_empty() { + None + } else { + Some(path) + } } fn install_helpers(lua: &Lua) -> mlua::Result<()> { @@ -499,4 +560,28 @@ return android.local_app { assert_eq!(package.name, "F-Droid"); assert_eq!(package.installed.len(), 1); } + + #[test] + fn lib_prefixed_searcher_does_not_expose_repository_templates() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + layout.templates_dir.join("android.lua"), + r#"return { leaked = true }"#, + ) + .unwrap(); + fs::write( + &package_path, + r#" +local ok = pcall(require, "templates.android") +return { + id = "android/org.fdroid.fdroid", + name = ok and "leaked" or "F-Droid", +} +"#, + ) + .unwrap(); + + let package = evaluate_package_file(&layout, &package_path).unwrap(); + assert_eq!(package.name, "F-Droid"); + } } From f746c96161de03548e2ebaccf57e7800a98becc7 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 11:01:13 +0800 Subject: [PATCH 06/72] fix(lua): avoid default cwd module leakage Do not retain Lua's default package.path for package evaluation. Keep repository lib loading explicit and add coverage for root/template leakage when cwd is the repository root. --- crates/getter-core/src/lua.rs | 41 +++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index b8e905b..c6b8f96 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -84,14 +84,12 @@ pub fn evaluate_package_source( fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Result<()> { let package: Table = lua.globals().get("package")?; - let current_path: String = package.get("path")?; let lib_pattern = repository.lib_dir.join("?.lua"); let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); let new_path = format!( - "{};{};{}", + "{};{}", lib_pattern.to_string_lossy(), - nested_lib_pattern.to_string_lossy(), - current_path + nested_lib_pattern.to_string_lossy() ); package.set("path", new_path)?; install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone()) @@ -584,4 +582,39 @@ return { let package = evaluate_package_file(&layout, &package_path).unwrap(); assert_eq!(package.name, "F-Droid"); } + + #[test] + fn repository_root_is_not_exposed_even_when_cwd_is_repository_root() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + layout.root.join("rootleak.lua"), + r#"return { leaked = true }"#, + ) + .unwrap(); + fs::write( + layout.templates_dir.join("android.lua"), + r#"return { leaked = true }"#, + ) + .unwrap(); + fs::write( + &package_path, + r#" +local root_ok = pcall(require, "rootleak") +local template_ok = pcall(require, "templates.android") +return { + id = "android/org.fdroid.fdroid", + name = (root_ok or template_ok) and "leaked" or "F-Droid", +} +"#, + ) + .unwrap(); + + let original_cwd = std::env::current_dir().unwrap(); + std::env::set_current_dir(&layout.root).unwrap(); + let result = evaluate_package_file(&layout, &package_path); + std::env::set_current_dir(original_cwd).unwrap(); + + let package = result.unwrap(); + assert_eq!(package.name, "F-Droid"); + } } From 3b7613d709b405cb7229f2fbbf546c2d29ee96e6 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 11:26:45 +0800 Subject: [PATCH 07/72] fix(android): keep JNI facade free of Lua domain deps Make getter domain crates an optional feature so the Android api_proxy facade can build without vendored Lua on armeabi-v7a. --- Cargo.toml | 7 ++++--- src/lib.rs | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 500b0f1..e45af61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,8 @@ version = "0.1.0" edition = "2021" [features] -default = ["cli"] +default = ["cli", "domain"] +domain = ["dep:getter-core", "dep:getter-storage"] cli = ["dep:getter-cli"] rustls-platform-verifier = ["dep:rustls-platform-verifier"] rustls-platform-verifier-android = ["rustls-platform-verifier", "rustls-platform-verifier/jni"] @@ -29,8 +30,8 @@ webpki-roots = [] native-tokio = [] [dependencies] -getter-core = { path = "crates/getter-core" } -getter-storage = { path = "crates/getter-storage" } +getter-core = { path = "crates/getter-core", optional = true } +getter-storage = { path = "crates/getter-storage", optional = true } getter-cli = { path = "crates/getter-cli", optional = true } thiserror = "1" tokio = { version = "1", features = ["net"] } diff --git a/src/lib.rs b/src/lib.rs index 4ffc0b5..607df46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,9 @@ //! behavior lives in split crates such as `getter-core` and `getter-storage`; //! Android/Flutter hosts embed this crate through a stable facade. +#[cfg(feature = "domain")] pub use getter_core as core; +#[cfg(feature = "domain")] pub use getter_storage as storage; #[cfg(feature = "rustls-platform-verifier")] From 7357980bc5972288db1817ede5610b5974b1546e Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 16:41:26 +0800 Subject: [PATCH 08/72] feat(cli): expose legacy migration reports Add a legacy report-list command so app and test adapters can consume sanitized migration reports through the getter JSON envelope instead of reading the data directory layout directly. --- README.md | 2 +- crates/getter-cli/src/lib.rs | 56 +++++++++++++++++++ crates/getter-cli/tests/bdd_cli.rs | 21 +++++++ .../legacy_import_room_bundle_failure.feature | 2 + 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 34543a0..6fc932d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This repository is intentionally usable outside the UpgradeAll UI. The UpgradeAl - Package IDs are readable, for example `android/org.fdroid.fdroid`. - Lua package repositories use `repo.toml` plus `packages/`, `lib/`, and `templates/` directories. - SQLite storage uses a main DB and a separate cache DB. -- `getter-cli` exposes JSON command contracts for init, app list, repository registration/evaluation, package evaluation, storage validation, and legacy bridge-bundle import. +- `getter-cli` exposes JSON command contracts for init, app list, repository registration/evaluation, package evaluation, storage validation, legacy bridge-bundle import, and sanitized legacy report listing. ## Verify diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 8e0155e..48d240d 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -51,6 +51,7 @@ pub enum CliCommand { LegacyImportRoomBundle { bundle: PathBuf, }, + LegacyReportList, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -262,6 +263,9 @@ where bundle: PathBuf::from(bundle), } } + [domain, command] if domain == "legacy" && command == "report-list" => { + CliCommand::LegacyReportList + } _ => { return Err(CliError::Usage(format!( "unsupported command: {}", @@ -404,6 +408,10 @@ fn execute(invocation: CliInvocation) -> Result { "apps": tracked_packages_json(records), })) } + CliCommand::LegacyReportList => { + open_initialized_storage(&invocation.data_dir)?; + Ok(json!({ "reports": list_migration_reports(&invocation.data_dir)? })) + } } } @@ -647,6 +655,53 @@ fn report_file_name(code: &str) -> String { format!("{}.json", code.replace('.', "-")) } +fn list_migration_reports(data_dir: &Path) -> Result, CliError> { + let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); + if !reports_dir.exists() { + return Ok(Vec::new()); + } + + let mut report_paths = fs::read_dir(&reports_dir) + .map_err(|source| CliError::Storage(format!("failed to read migration reports: {source}")))? + .map(|entry| entry.map(|entry| entry.path())) + .collect::, _>>() + .map_err(|source| { + CliError::Storage(format!("failed to read migration report entry: {source}")) + })?; + report_paths.retain(|path| { + path.extension() + .is_some_and(|extension| extension == "json") + }); + report_paths.sort(); + + report_paths + .into_iter() + .map(|path| { + let bytes = fs::read(&path).map_err(|source| { + CliError::Storage(format!( + "failed to read migration report '{}': {source}", + path.display() + )) + })?; + let report: Value = serde_json::from_slice(&bytes).map_err(|source| { + CliError::Storage(format!( + "failed to parse migration report '{}': {source}", + path.display() + )) + })?; + Ok(json!({ + "ok": report.get("ok").and_then(Value::as_bool).unwrap_or(false), + "code": report.get("code").and_then(Value::as_str).unwrap_or("migration.unknown"), + "message": report.get("message").and_then(Value::as_str).unwrap_or("Legacy migration report"), + "bundle_file_name": report.get("bundle_file_name").and_then(Value::as_str), + "imported_records": report.get("imported_records").and_then(Value::as_u64).unwrap_or(0), + "tracked_records": report.get("tracked_records").and_then(Value::as_u64).unwrap_or(0), + "report_path": path, + })) + }) + .collect() +} + #[derive(Debug, Serialize)] struct MigrationReport<'a> { ok: bool, @@ -746,6 +801,7 @@ impl CliCommand { Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", + Self::LegacyReportList => "legacy report-list", } } } diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 807aaed..28ed839 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -190,6 +190,13 @@ fn run_getter_legacy_import(world: &mut CliWorld) { world.json = None; } +#[when("I run getter legacy report-list for that directory")] +fn run_getter_legacy_report_list(world: &mut CliWorld) { + let output = run_getter(world, ["legacy".to_owned(), "report-list".to_owned()]); + world.output = Some(output); + world.json = None; +} + #[then("the command succeeds")] fn command_succeeds(world: &mut CliWorld) { let output = world.output.as_ref().expect("command output exists"); @@ -356,6 +363,20 @@ fn import_reports_one_tracked_app(world: &mut CliWorld) { ); } +#[then(expr = "the output lists migration report {string}")] +fn output_lists_migration_report(world: &mut CliWorld, code: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "legacy report-list"); + let reports = json["data"]["reports"].as_array().expect("reports array"); + assert!( + reports + .iter() + .any(|report| report["code"].as_str() == Some(code.as_str())), + "reports should contain {code}: {reports:?}" + ); +} + #[then(expr = "the app list contains imported package {string}")] fn app_list_contains_imported_package(world: &mut CliWorld, package_id: String) { let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); diff --git a/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature b/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature index 864417f..02b6e8b 100644 --- a/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature +++ b/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature @@ -16,3 +16,5 @@ Feature: Legacy import failure recovery And the output is valid JSON And the import reports one tracked app And the app list contains imported package "android/org.fdroid.fdroid" + When I run getter legacy report-list for that directory + Then the output lists migration report "migration.imported" From 6be047ac13d6dc4dd94dc29fbe6c4f622acd640f Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 16:58:33 +0800 Subject: [PATCH 09/72] fix(cli): keep migration report list sanitized Do not expose getter data-directory report paths in legacy report-list output; callers only need the sanitized report fields. --- crates/getter-cli/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 48d240d..b13bd02 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -696,7 +696,6 @@ fn list_migration_reports(data_dir: &Path) -> Result, CliError> { "bundle_file_name": report.get("bundle_file_name").and_then(Value::as_str), "imported_records": report.get("imported_records").and_then(Value::as_u64).unwrap_or(0), "tracked_records": report.get("tracked_records").and_then(Value::as_u64).unwrap_or(0), - "report_path": path, })) }) .collect() From 13816863b20744beadab7bf10153afa2156e3010 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 20:19:23 +0800 Subject: [PATCH 10/72] feat(cli): add offline repo validation Expose repo validate through the getter JSON envelope with structured getter-owned diagnostics for repository layout, Lua runtime, schema, and domain errors. Add BDD coverage and cache-key hashing for repository changes. --- crates/getter-cli/src/lib.rs | 18 +- crates/getter-cli/tests/bdd_cli.rs | 134 ++++++++ .../tests/features/cli/repo_validate.feature | 35 ++ crates/getter-core/src/diagnostics.rs | 309 ++++++++++++++++++ crates/getter-core/src/lib.rs | 1 + crates/getter-core/src/repository.rs | 86 ++++- 6 files changed, 581 insertions(+), 2 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/repo_validate.feature create mode 100644 crates/getter-core/src/diagnostics.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index b13bd02..22fb675 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -4,6 +4,7 @@ //! while durable state is initialized through `getter-storage` so the command //! surface exercises the same Rust-owned SQLite direction used by embedders. +use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; @@ -43,6 +44,9 @@ pub enum CliCommand { RepoEval { id: RepositoryId, }, + RepoValidate { + path: PathBuf, + }, PackageEval { package_id: PackageId, repo_id: Option, @@ -241,6 +245,11 @@ where [domain, command, id] if domain == "repo" && command == "eval" => CliCommand::RepoEval { id: parse_repository_id(id)?, }, + [domain, command, path] if domain == "repo" && command == "validate" => { + CliCommand::RepoValidate { + path: PathBuf::from(path), + } + } [domain, command, package_id] if domain == "package" && command == "eval" => { CliCommand::PackageEval { package_id: parse_package_id(package_id)?, @@ -331,6 +340,12 @@ fn execute(invocation: CliInvocation) -> Result { "packages": packages, })) } + CliCommand::RepoValidate { path } => { + open_initialized_storage(&invocation.data_dir)?; + serde_json::to_value(validate_repository_path(path)).map_err(|source| { + CliError::Repository(format!("failed to serialize validation report: {source}")) + }) + } CliCommand::PackageEval { package_id, repo_id, @@ -747,7 +762,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle >\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -797,6 +812,7 @@ impl CliCommand { Self::RepoList => "repo list", Self::RepoAdd { .. } => "repo add", Self::RepoEval { .. } => "repo eval", + Self::RepoValidate { .. } => "repo validate", Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 28ed839..4f084c7 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -80,6 +80,61 @@ fn fixture_lua_repository_named( create_fixture_lua_repository(world, repo_id, package_id, package_name); } +#[given(expr = "a fixture Lua repository {string} with invalid Lua package {string}")] +fn fixture_lua_repository_invalid_lua(world: &mut CliWorld, repo_id: String, package_id: String) { + create_custom_fixture_lua_repository( + world, + repo_id, + package_id.clone(), + format!("return package_def {{ id = \"{package_id}\", name = "), + ); +} + +#[given(expr = "a fixture Lua repository {string} with schema-invalid package {string}")] +fn fixture_lua_repository_invalid_schema( + world: &mut CliWorld, + repo_id: String, + package_id: String, +) { + create_custom_fixture_lua_repository( + world, + repo_id, + package_id.clone(), + format!("return {{ id = \"{package_id}\" }}"), + ); +} + +#[given(expr = "a fixture Lua repository {string} with mismatched package path {string}")] +fn fixture_lua_repository_mismatched_path( + world: &mut CliWorld, + repo_id: String, + package_id: String, +) { + create_custom_fixture_lua_repository( + world, + repo_id, + package_id, + "return { id = \"android/com.termux\", name = \"Termux\" }".to_owned(), + ); +} + +#[given(expr = "an incomplete Lua repository {string}")] +fn incomplete_lua_repository(world: &mut CliWorld, repo_id: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let repo_path = temp.path().join(format!("repo-{repo_id}")); + fs::create_dir_all(&repo_path).expect("create incomplete repo dir"); + fs::write( + repo_path.join("repo.toml"), + format!( + "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" + ), + ) + .expect("write repo.toml"); + world.fixture_repo_id = Some(repo_id); + world.fixture_repo_path = Some(repo_path); + world.fixture_package_id = None; +} + #[when("I run getter init for that directory")] fn run_getter_init(world: &mut CliWorld) { let output = run_getter(world, ["init".to_owned()]); @@ -144,6 +199,24 @@ fn run_getter_repo_eval(world: &mut CliWorld) { world.json = None; } +#[when("I run getter repo validate for that repository")] +fn run_getter_repo_validate(world: &mut CliWorld) { + let repo_path = world + .fixture_repo_path + .as_ref() + .expect("fixture repo path exists"); + let output = run_getter( + world, + [ + "repo".to_owned(), + "validate".to_owned(), + repo_path.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter package eval for that fixture package")] fn run_getter_package_eval(world: &mut CliWorld) { let repo_id = world @@ -282,6 +355,35 @@ fn output_contains_added_repository(world: &mut CliWorld) { assert_eq!(json["data"]["repository"]["id"], repo_id); } +#[then("the output reports a valid repository without network")] +fn output_reports_valid_repository_without_network(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "repo validate"); + assert_eq!(json["data"]["valid"], true); + assert_eq!(json["data"]["network_required"], false); + assert_eq!(json["data"]["diagnostics"], Value::Array(Vec::new())); + assert_eq!(json["data"]["package_count"], 1); +} + +#[then(expr = "the output reports repository diagnostic {string}")] +fn output_reports_repository_diagnostic(world: &mut CliWorld, code: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "repo validate"); + assert_eq!(json["data"]["valid"], false); + assert_eq!(json["data"]["network_required"], false); + let diagnostics = json["data"]["diagnostics"] + .as_array() + .expect("diagnostics array"); + assert!( + diagnostics + .iter() + .any(|diagnostic| diagnostic["code"].as_str() == Some(code.as_str())), + "diagnostics should contain {code}: {diagnostics:?}" + ); +} + #[then("the output contains the evaluated fixture package")] fn output_contains_evaluated_fixture_package(world: &mut CliWorld) { let package_id = world @@ -442,6 +544,38 @@ return package_def {{ world.fixture_package_id = Some(package_id); } +fn create_custom_fixture_lua_repository( + world: &mut CliWorld, + repo_id: String, + package_id: String, + package_source: String, +) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let repo_path = temp.path().join(format!("repo-{repo_id}")); + let package_name_path = package_id + .strip_prefix("android/") + .expect("fixture package id should be android package id"); + fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); + fs::create_dir(repo_path.join("lib")).expect("create lib dir"); + fs::create_dir(repo_path.join("templates")).expect("create templates dir"); + fs::write( + repo_path.join("repo.toml"), + format!( + "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" + ), + ) + .expect("write repo.toml"); + fs::write( + repo_path.join(format!("packages/android/{package_name_path}.lua")), + package_source, + ) + .expect("write package Lua"); + + world.fixture_repo_id = Some(repo_id); + world.fixture_repo_path = Some(repo_path); + world.fixture_package_id = Some(package_id); +} + fn run_getter_repo_add_with_priority(world: &mut CliWorld, repo_id: &str, priority: i32) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); diff --git a/crates/getter-cli/tests/features/cli/repo_validate.feature b/crates/getter-cli/tests/features/cli/repo_validate.feature new file mode 100644 index 0000000..592d4bf --- /dev/null +++ b/crates/getter-cli/tests/features/cli/repo_validate.feature @@ -0,0 +1,35 @@ +Feature: Getter CLI repository validation + Scenario: User validates a fixture Lua repository offline + Given an initialized getter data directory + And a fixture Lua repository "official" with package "android/org.fdroid.fdroid" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports a valid repository without network + + Scenario: User receives diagnostics for invalid Lua + Given an initialized getter data directory + And a fixture Lua repository "broken" with invalid Lua package "android/org.fdroid.fdroid" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports repository diagnostic "package.lua_runtime" + + Scenario: User receives diagnostics for invalid package schema + Given an initialized getter data directory + And a fixture Lua repository "broken" with schema-invalid package "android/org.fdroid.fdroid" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports repository diagnostic "package.schema" + + Scenario: User receives diagnostics for package path mismatch + Given an initialized getter data directory + And a fixture Lua repository "broken" with mismatched package path "android/org.fdroid.fdroid" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports repository diagnostic "package.domain" + + Scenario: User receives diagnostics for an incomplete repository layout + Given an initialized getter data directory + And an incomplete Lua repository "broken" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports repository diagnostic "repository.missing_directory" diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs new file mode 100644 index 0000000..745446f --- /dev/null +++ b/crates/getter-core/src/diagnostics.rs @@ -0,0 +1,309 @@ +//! Structured repository/package diagnostics for offline validation. +//! +//! Diagnostics are getter-owned DTOs used by CLI and future app bridges. They +//! describe what getter observed; Flutter should only render them. + +use crate::lua::{evaluate_package_file, LuaPackageError}; +use crate::repository::{RepositoryLayout, RepositoryLoadError}; +use crate::PackageId; +use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DiagnosticSeverity { + Error, + Warning, + Info, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SourceLocation { + pub path: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub field: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PackageValidationDiagnostic { + pub severity: DiagnosticSeverity, + pub code: String, + pub message: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub package_id: Option, + pub location: SourceLocation, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RepositoryValidationReport { + pub valid: bool, + pub diagnostics: Vec, + pub package_count: usize, + pub network_required: bool, +} + +impl RepositoryValidationReport { + pub fn new(package_count: usize, diagnostics: Vec) -> Self { + let valid = !diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == DiagnosticSeverity::Error); + Self { + valid, + diagnostics, + package_count, + network_required: false, + } + } +} + +/// Validate a repository path offline. +/// +/// This intentionally loads and evaluates local Lua package files only. It must +/// not perform provider/network checks. +pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationReport { + let root = path.as_ref(); + let layout = match RepositoryLayout::load(root) { + Ok(layout) => layout, + Err(error) => { + return RepositoryValidationReport::new(0, vec![repository_load_diagnostic(error)]) + } + }; + + let mut diagnostics = Vec::new(); + let mut package_count = 0usize; + for package_file in &layout.packages { + match evaluate_package_file(&layout, &package_file.path) { + Ok(_) => package_count += 1, + Err(error) => diagnostics.push(lua_diagnostic(error, Some(package_file.id.clone()))), + } + } + + RepositoryValidationReport::new(package_count, diagnostics) +} + +fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDiagnostic { + let (code, path, message) = match error { + RepositoryLoadError::ReadRepoToml { path, source } => ( + "repository.read_repo_toml", + path, + format!("failed to read repo.toml: {source}"), + ), + RepositoryLoadError::ParseRepoToml { path, source } => ( + "repository.parse_repo_toml", + path, + format!("failed to parse repo.toml: {source}"), + ), + RepositoryLoadError::RepositoryId(source) => ( + "repository.invalid_id", + PathBuf::from("repo.toml"), + source.to_string(), + ), + RepositoryLoadError::UnsupportedApiVersion(version) => ( + "repository.unsupported_api_version", + PathBuf::from("repo.toml"), + format!("unsupported repository api_version '{version}'"), + ), + RepositoryLoadError::MissingDirectory { path, directory } => ( + "repository.missing_directory", + path, + format!("missing required directory '{directory}'"), + ), + RepositoryLoadError::ReadPackagesDir { path, source } => ( + "repository.read_packages_dir", + path, + format!("failed to read packages directory: {source}"), + ), + RepositoryLoadError::InvalidPackagePath { path, reason } => { + ("repository.invalid_package_path", path, reason) + } + RepositoryLoadError::PackageId { path, source } => ( + "repository.invalid_package_id", + path, + format!("invalid package id derived from path: {source}"), + ), + RepositoryLoadError::HashPackageFile { path, source } => ( + "repository.hash_package_file", + path, + format!("failed to hash package file: {source}"), + ), + }; + diagnostic(code, message, path, None, None) +} + +fn lua_diagnostic( + error: LuaPackageError, + fallback_package_id: Option, +) -> PackageValidationDiagnostic { + match error { + LuaPackageError::ReadFile { path, source } => diagnostic( + "package.read_file", + format!("failed to read Lua package file: {source}"), + path, + None, + fallback_package_id, + ), + LuaPackageError::Runtime { path, source } => diagnostic( + "package.lua_runtime", + format!("Lua runtime error: {source}"), + path, + None, + fallback_package_id, + ), + LuaPackageError::NotATable { path } => diagnostic( + "package.not_a_table", + "Lua package did not return a table".to_owned(), + path, + None, + fallback_package_id, + ), + LuaPackageError::UnsupportedValue { + path, + location, + value_type, + } => diagnostic( + "package.unsupported_value", + format!("unsupported Lua value at {location}: {value_type}"), + path, + Some(location), + fallback_package_id, + ), + LuaPackageError::Schema { path, message } => { + diagnostic("package.schema", message, path, None, fallback_package_id) + } + LuaPackageError::Domain { path, message } => { + diagnostic("package.domain", message, path, None, fallback_package_id) + } + } +} + +fn diagnostic( + code: impl Into, + message: impl Into, + path: PathBuf, + field: Option, + package_id: Option, +) -> PackageValidationDiagnostic { + PackageValidationDiagnostic { + severity: DiagnosticSeverity::Error, + code: code.into(), + message: message.into(), + package_id, + location: SourceLocation { + path: path.to_string_lossy().to_string(), + field, + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + + fn fixture_repo() -> tempfile::TempDir { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::write( + root.join("repo.toml"), + r#"id = "official" +name = "Official" +priority = 0 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::create_dir_all(root.join("packages/android")).unwrap(); + fs::create_dir(root.join("lib")).unwrap(); + fs::create_dir(root.join("templates")).unwrap(); + temp + } + + #[test] + fn valid_repository_has_no_diagnostics() { + let temp = fixture_repo(); + fs::write( + temp.path().join("packages/android/org.fdroid.fdroid.lua"), + r#"return package_def { id = "android/org.fdroid.fdroid", name = "F-Droid" }"#, + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + assert!(report.valid, "{report:?}"); + assert_eq!(report.package_count, 1); + assert!(report.diagnostics.is_empty()); + assert!(!report.network_required); + } + + #[test] + fn missing_directory_is_stable_repository_diagnostic() { + let temp = tempfile::tempdir().unwrap(); + fs::write( + temp.path().join("repo.toml"), + r#"id = "official" +name = "Official" +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + assert!(!report.valid); + assert_eq!(report.diagnostics[0].code, "repository.missing_directory"); + } + + #[test] + fn lua_schema_error_is_stable_package_diagnostic() { + let temp = fixture_repo(); + fs::write( + temp.path().join("packages/android/org.fdroid.fdroid.lua"), + r#"return { id = "android/org.fdroid.fdroid" }"#, + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + assert!(!report.valid); + assert_eq!(report.diagnostics[0].code, "package.schema"); + assert_eq!( + report.diagnostics[0] + .package_id + .as_ref() + .unwrap() + .to_string(), + "android/org.fdroid.fdroid" + ); + } + + #[test] + fn package_id_path_mismatch_is_domain_diagnostic() { + let temp = fixture_repo(); + fs::write( + temp.path().join("packages/android/org.fdroid.fdroid.lua"), + r#"return { id = "android/com.termux", name = "Termux" }"#, + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + assert!(!report.valid); + assert_eq!(report.diagnostics[0].code, "package.domain"); + } + + #[test] + fn unsupported_api_version_is_stable_repository_diagnostic() { + let temp = tempfile::tempdir().unwrap(); + fs::write( + temp.path().join("repo.toml"), + r#"id = "official" +name = "Official" +api_version = "getter.repo.v2" +"#, + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + assert!(!report.valid); + assert_eq!( + report.diagnostics[0].code, + "repository.unsupported_api_version" + ); + } +} diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index da959a3..32c110b 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -9,6 +9,7 @@ use std::cmp::Ordering; use std::fmt; use std::str::FromStr; +pub mod diagnostics; pub mod lua; pub mod repository; pub mod update; diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index fe9fa21..c383acc 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -1,7 +1,7 @@ //! Repository layout loading for Lua package repositories. use crate::{PackageId, PackageIdError, RepositoryId, RepositoryIdError, RepositoryPriority}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::fs; use std::path::{Path, PathBuf}; @@ -31,6 +31,14 @@ pub struct PackageFile { pub path: PathBuf, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RepositoryPackageCacheKey { + pub repository_id: RepositoryId, + pub package_id: PackageId, + pub api_version: String, + pub package_file_hash: String, +} + #[derive(Debug, thiserror::Error)] pub enum RepositoryLoadError { #[error("failed to read repo.toml at {path}: {source}")] @@ -68,6 +76,12 @@ pub enum RepositoryLoadError { #[source] source: PackageIdError, }, + #[error("failed to hash package file {path}: {source}")] + HashPackageFile { + path: PathBuf, + #[source] + source: std::io::Error, + }, } impl RepositoryLayout { @@ -224,6 +238,36 @@ pub fn package_id_from_path( }) } +pub fn package_cache_key( + repository: &RepositoryLayout, + package: &PackageFile, +) -> Result { + Ok(RepositoryPackageCacheKey { + repository_id: repository.metadata.id.clone(), + package_id: package.id.clone(), + api_version: repository.metadata.api_version.clone(), + package_file_hash: package_file_content_hash(&package.path)?, + }) +} + +pub fn package_file_content_hash(path: impl AsRef) -> Result { + let path = path.as_ref(); + let bytes = fs::read(path).map_err(|source| RepositoryLoadError::HashPackageFile { + path: path.to_path_buf(), + source, + })?; + Ok(format!("{:016x}", fnv1a64(&bytes))) +} + +fn fnv1a64(bytes: &[u8]) -> u64 { + let mut hash = 0xcbf29ce484222325u64; + for byte in bytes { + hash ^= u64::from(*byte); + hash = hash.wrapping_mul(0x100000001b3); + } + hash +} + pub fn highest_priority(items: &[T], priority: F) -> Option<&T> where F: Fn(&T) -> RepositoryPriority, @@ -275,6 +319,46 @@ api_version = "getter.repo.v1" ); } + #[test] + fn package_cache_key_changes_when_package_file_content_changes() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::write( + root.join("repo.toml"), + r#"id = "official" +name = "UpgradeAll Official" +priority = 0 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::create_dir(root.join("packages")).unwrap(); + fs::create_dir(root.join("packages/android")).unwrap(); + fs::create_dir(root.join("lib")).unwrap(); + fs::create_dir(root.join("templates")).unwrap(); + let package_path = root.join("packages/android/org.fdroid.fdroid.lua"); + fs::write( + &package_path, + r#"return { id = "android/org.fdroid.fdroid", name = "F-Droid" }"#, + ) + .unwrap(); + let layout = RepositoryLayout::load(root).unwrap(); + let first = package_cache_key(&layout, &layout.packages[0]).unwrap(); + + fs::write( + &package_path, + r#"return { id = "android/org.fdroid.fdroid", name = "F-Droid Nightly" }"#, + ) + .unwrap(); + let layout = RepositoryLayout::load(root).unwrap(); + let second = package_cache_key(&layout, &layout.packages[0]).unwrap(); + + assert_eq!(first.repository_id.as_str(), "official"); + assert_eq!(first.package_id.to_string(), "android/org.fdroid.fdroid"); + assert_eq!(first.api_version, REPO_API_VERSION_V1); + assert_ne!(first.package_file_hash, second.package_file_hash); + } + #[test] fn highest_priority_selects_larger_number() { let priorities = [ From f1035d3ae88198dfd0de872b6f9af010a0b0cd3f Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 20:30:12 +0800 Subject: [PATCH 11/72] fix(core): include schema field diagnostics Populate the diagnostic location field for schema errors when getter can infer the failing field from validation messages. --- crates/getter-core/src/diagnostics.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index 745446f..7c58881 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -168,7 +168,8 @@ fn lua_diagnostic( fallback_package_id, ), LuaPackageError::Schema { path, message } => { - diagnostic("package.schema", message, path, None, fallback_package_id) + let field = schema_field_from_message(&message); + diagnostic("package.schema", message, path, field, fallback_package_id) } LuaPackageError::Domain { path, message } => { diagnostic("package.domain", message, path, None, fallback_package_id) @@ -176,6 +177,13 @@ fn lua_diagnostic( } } +fn schema_field_from_message(message: &str) -> Option { + let marker = "field '"; + let start = message.find(marker)? + marker.len(); + let end = message[start..].find('\'')?; + Some(message[start..start + end].to_owned()) +} + fn diagnostic( code: impl Into, message: impl Into, @@ -263,6 +271,10 @@ api_version = "getter.repo.v1" let report = validate_repository_path(temp.path()); assert!(!report.valid); assert_eq!(report.diagnostics[0].code, "package.schema"); + assert_eq!( + report.diagnostics[0].location.field.as_deref(), + Some("name") + ); assert_eq!( report.diagnostics[0] .package_id From 255a9c7c25174355be24053c0e3876de7cf087db Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 20:41:12 +0800 Subject: [PATCH 12/72] fix(core): constrain Lua validation environment Remove process/filesystem-sensitive Lua globals during package evaluation and assert the repo validator exposes full diagnostic shape for schema errors. --- crates/getter-cli/tests/bdd_cli.rs | 17 +++++--- crates/getter-core/src/diagnostics.rs | 6 ++- crates/getter-core/src/lua.rs | 56 ++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 4f084c7..ddc42f2 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -376,12 +376,17 @@ fn output_reports_repository_diagnostic(world: &mut CliWorld, code: String) { let diagnostics = json["data"]["diagnostics"] .as_array() .expect("diagnostics array"); - assert!( - diagnostics - .iter() - .any(|diagnostic| diagnostic["code"].as_str() == Some(code.as_str())), - "diagnostics should contain {code}: {diagnostics:?}" - ); + let diagnostic = diagnostics + .iter() + .find(|diagnostic| diagnostic["code"].as_str() == Some(code.as_str())) + .unwrap_or_else(|| panic!("diagnostics should contain {code}: {diagnostics:?}")); + assert_eq!(diagnostic["severity"], "error"); + assert!(diagnostic["message"].as_str().is_some()); + assert!(diagnostic["location"]["path"].as_str().is_some()); + if code == "package.schema" { + assert_eq!(diagnostic["package_id"], "android/org.fdroid.fdroid"); + assert_eq!(diagnostic["location"]["field"], "name"); + } } #[then("the output contains the evaluated fixture package")] diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index 7c58881..62ed870 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -4,7 +4,7 @@ //! describe what getter observed; Flutter should only render them. use crate::lua::{evaluate_package_file, LuaPackageError}; -use crate::repository::{RepositoryLayout, RepositoryLoadError}; +use crate::repository::{package_cache_key, RepositoryLayout, RepositoryLoadError}; use crate::PackageId; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; @@ -72,6 +72,10 @@ pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationR let mut diagnostics = Vec::new(); let mut package_count = 0usize; for package_file in &layout.packages { + if let Err(error) = package_cache_key(&layout, package_file) { + diagnostics.push(repository_load_diagnostic(error)); + continue; + } match evaluate_package_file(&layout, &package_file.path) { Ok(_) => package_count += 1, Err(error) => diagnostics.push(lua_diagnostic(error, Some(package_file.id.clone()))), diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index c6b8f96..a31107d 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -61,6 +61,10 @@ pub fn evaluate_package_source( path: path.clone(), source, })?; + remove_unsafe_globals(&lua).map_err(|source| LuaPackageError::Runtime { + path: path.clone(), + source, + })?; install_helpers(&lua).map_err(|source| LuaPackageError::Runtime { path: path.clone(), source, @@ -92,7 +96,32 @@ fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Res nested_lib_pattern.to_string_lossy() ); package.set("path", new_path)?; - install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone()) + package.set("cpath", "")?; + package.set("loadlib", Value::Nil)?; + install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone())?; + disable_native_module_searchers(&package) +} + +fn disable_native_module_searchers(package: &Table) -> mlua::Result<()> { + let searchers: Table = package.get("searchers")?; + let len = searchers.raw_len(); + for index in 4..=len { + searchers.raw_set(index, Value::Nil)?; + } + Ok(()) +} + +fn remove_unsafe_globals(lua: &Lua) -> mlua::Result<()> { + let globals = lua.globals(); + let package: Table = globals.get("package")?; + let loaded: Table = package.get("loaded")?; + for name in ["os", "io", "debug"] { + loaded.set(name, Value::Nil)?; + } + for name in ["os", "io", "debug", "dofile", "loadfile", "package"] { + globals.set(name, Value::Nil)?; + } + Ok(()) } fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> mlua::Result<()> { @@ -583,6 +612,31 @@ return { assert_eq!(package.name, "F-Droid"); } + #[test] + fn lua_environment_does_not_expose_process_or_file_system_globals() { + let temp = tempfile::tempdir().unwrap(); + let side_effect = temp.path().join("side-effect"); + let (_repo_temp, layout, package_path) = fixture_repo(); + fs::write( + &package_path, + format!( + r#" +return {{ + id = "android/org.fdroid.fdroid", + name = (os == nil and io == nil and debug == nil and dofile == nil and loadfile == nil and package == nil) and "F-Droid" or "leaked", + side_effect = rawget(_G, "os") and os.execute("touch {}"), +}} +"#, + side_effect.display() + ), + ) + .unwrap(); + + let package = evaluate_package_file(&layout, &package_path).unwrap(); + assert_eq!(package.name, "F-Droid"); + assert!(!side_effect.exists()); + } + #[test] fn repository_root_is_not_exposed_even_when_cwd_is_repository_root() { let (_temp, layout, package_path) = fixture_repo(); From 17b0e281914d3c7d646aa0605bee283d31ffd60b Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 22:22:48 +0800 Subject: [PATCH 13/72] feat(cli): import legacy Room databases --- Cargo.lock | 2 + crates/getter-cli/Cargo.toml | 1 + crates/getter-cli/src/lib.rs | 277 +++++++++- crates/getter-cli/tests/bdd_cli.rs | 276 +++++++++- .../cli/legacy_import_room_db.feature | 70 +++ crates/getter-storage/Cargo.toml | 1 + crates/getter-storage/src/legacy_room.rs | 486 +++++++++++++++++- crates/getter-storage/src/lib.rs | 115 ++++- 8 files changed, 1169 insertions(+), 59 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/legacy_import_room_db.feature diff --git a/Cargo.lock b/Cargo.lock index 5d73f4c..6119744 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -521,6 +521,7 @@ dependencies = [ "cucumber", "getter-core", "getter-storage", + "rusqlite", "serde", "serde_json", "tempfile", @@ -581,6 +582,7 @@ version = "0.1.0" dependencies = [ "getter-core", "rusqlite", + "serde_json", "tempfile", "thiserror 1.0.69", ] diff --git a/crates/getter-cli/Cargo.toml b/crates/getter-cli/Cargo.toml index ca03088..ef7d105 100644 --- a/crates/getter-cli/Cargo.toml +++ b/crates/getter-cli/Cargo.toml @@ -12,6 +12,7 @@ thiserror = "1" [dev-dependencies] cucumber = "0.23" +rusqlite = { version = "0.32", features = ["bundled"] } serde_json = "1" tempfile = "3" tokio = { version = "1", features = ["macros", "rt-multi-thread"] } diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 22fb675..a15af83 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -9,11 +9,13 @@ use getter_core::lua::evaluate_package_file; use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_storage::legacy_room::{ - map_legacy_app, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, + map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, + LegacyExtraAppRecord, LegacyPackageResolution, LegacyRoomDbImport, LegacyRoomImportWarning, + LegacyRoomReadError, }; use getter_storage::{ - CacheDb, MainDb, StorageError, StoredPackageResolution, StoredRepository, StoredTrackedPackage, - TrackedPackageUpsert, + CacheDb, MainDb, MigrationRecordUpsert, StorageError, StoredPackageResolution, + StoredRepository, StoredTrackedPackage, TrackedPackageUpsert, }; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; @@ -23,6 +25,7 @@ use std::path::{Path, PathBuf}; const MAIN_DB_FILE: &str = "main.db"; const CACHE_DB_FILE: &str = "cache.db"; const MIGRATION_REPORTS_DIR: &str = "migration-reports"; +const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliInvocation { @@ -55,6 +58,9 @@ pub enum CliCommand { LegacyImportRoomBundle { bundle: PathBuf, }, + LegacyImportRoomDb { + db: PathBuf, + }, LegacyReportList, } @@ -87,6 +93,10 @@ pub enum CliError { InvalidLegacyBundle { report_path: PathBuf }, #[error("Legacy Room bundle import is not implemented yet")] UnsupportedLegacyBundle { report_path: PathBuf }, + #[error("Legacy Room database is invalid")] + InvalidLegacyDb { report_path: PathBuf }, + #[error("Legacy Room database version is not supported")] + UnsupportedLegacyDb { report_path: PathBuf }, } impl CliError { @@ -95,9 +105,10 @@ impl CliError { Self::Usage(_) => ExitCode::Usage, Self::Storage(_) => ExitCode::Storage, Self::Repository(_) | Self::PackageEval(_) => ExitCode::GenericFailure, - Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } => { - ExitCode::Migration - } + Self::InvalidLegacyBundle { .. } + | Self::UnsupportedLegacyBundle { .. } + | Self::InvalidLegacyDb { .. } + | Self::UnsupportedLegacyDb { .. } => ExitCode::Migration, } } @@ -109,6 +120,8 @@ impl CliError { Self::PackageEval(_) => "package.eval_error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", + Self::InvalidLegacyDb { .. } => "migration.invalid_db", + Self::UnsupportedLegacyDb { .. } => "migration.unsupported_db", } } @@ -122,6 +135,8 @@ impl CliError { Self::UnsupportedLegacyBundle { .. } => { "Legacy Room bundle import is not implemented yet" } + Self::InvalidLegacyDb { .. } => "Legacy Room database is invalid", + Self::UnsupportedLegacyDb { .. } => "Legacy Room database version is not supported", } } @@ -131,14 +146,19 @@ impl CliError { | Self::Storage(detail) | Self::Repository(detail) | Self::PackageEval(detail) => Some(detail.as_str()), - Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } => None, + Self::InvalidLegacyBundle { .. } + | Self::UnsupportedLegacyBundle { .. } + | Self::InvalidLegacyDb { .. } + | Self::UnsupportedLegacyDb { .. } => None, } } fn report_path(&self) -> Option<&Path> { match self { Self::InvalidLegacyBundle { report_path } - | Self::UnsupportedLegacyBundle { report_path } => Some(report_path.as_path()), + | Self::UnsupportedLegacyBundle { report_path } + | Self::InvalidLegacyDb { report_path } + | Self::UnsupportedLegacyDb { report_path } => Some(report_path.as_path()), Self::Usage(_) | Self::Storage(_) | Self::Repository(_) | Self::PackageEval(_) => None, } } @@ -272,6 +292,11 @@ where bundle: PathBuf::from(bundle), } } + [domain, command, db] if domain == "legacy" && command == "import-room-db" => { + CliCommand::LegacyImportRoomDb { + db: PathBuf::from(db), + } + } [domain, command] if domain == "legacy" && command == "report-list" => { CliCommand::LegacyReportList } @@ -367,6 +392,14 @@ fn execute(invocation: CliInvocation) -> Result { } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; + if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { + let records = db.tracked_packages()?; + return Ok(json!({ + "already_imported": true, + "imported_records": 0, + "apps": tracked_packages_json(records), + })); + } let bytes = fs::read(&bundle).map_err(|source| { match create_migration_report( &invocation.data_dir, @@ -375,6 +408,7 @@ fn execute(invocation: CliInvocation) -> Result { &format!("failed to read bundle: {source}"), 0, 0, + &[], ) { Ok(report_path) => CliError::InvalidLegacyBundle { report_path }, Err(error) => error, @@ -388,6 +422,7 @@ fn execute(invocation: CliInvocation) -> Result { &format!("failed to parse JSON bundle: {source}"), 0, 0, + &[], ) { Ok(report_path) => CliError::InvalidLegacyBundle { report_path }, Err(error) => error, @@ -404,6 +439,7 @@ fn execute(invocation: CliInvocation) -> Result { ), 0, 0, + &[], )?; return Err(CliError::UnsupportedLegacyBundle { report_path }); } @@ -415,6 +451,7 @@ fn execute(invocation: CliInvocation) -> Result { "Legacy Room bundle imported", parsed.apps.len() as u64, parsed.apps.len() as u64, + &[], )?; let records = db.tracked_packages()?; Ok(json!({ @@ -423,6 +460,62 @@ fn execute(invocation: CliInvocation) -> Result { "apps": tracked_packages_json(records), })) } + CliCommand::LegacyImportRoomDb { db: legacy_db } => { + let db = open_main_db(&invocation.data_dir)?; + if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { + let records = db.tracked_packages()?; + return Ok(json!({ + "already_imported": true, + "imported_records": 0, + "source_counts": { + "app_rows": 0, + "extra_app_rows": 0, + "hub_rows": 0, + "extra_hub_rows": 0, + }, + "warnings": [], + "apps": tracked_packages_json(records), + })); + } + let import = read_legacy_room_database(&legacy_db).map_err(|source| { + legacy_db_import_error(&invocation.data_dir, &legacy_db, source) + })?; + let imported_records = import.apps.len(); + let source_counts = source_counts_json(&import); + let warnings = import_warnings_json(&import.warnings); + if import.source_counts.app_rows > 0 && imported_records == 0 { + let report_path = create_migration_report_with_source_counts( + &invocation.data_dir, + &legacy_db, + "migration.invalid_db", + "Legacy Room database has app rows but no importable app rows", + 0, + 0, + &warnings, + Some(&source_counts), + )?; + return Err(CliError::InvalidLegacyDb { report_path }); + } + import_legacy_room_db(&db, &import)?; + let report_path = create_migration_report_with_source_counts( + &invocation.data_dir, + &legacy_db, + "migration.imported", + "Legacy Room database imported", + imported_records as u64, + imported_records as u64, + &warnings, + Some(&source_counts), + )?; + let records = db.tracked_packages()?; + Ok(json!({ + "report_path": report_path, + "imported_records": imported_records, + "source_counts": source_counts, + "warnings": warnings, + "apps": tracked_packages_json(records), + })) + } CliCommand::LegacyReportList => { open_initialized_storage(&invocation.data_dir)?; Ok(json!({ "reports": list_migration_reports(&invocation.data_dir)? })) @@ -574,10 +667,11 @@ fn tracked_packages_json(packages: Vec) -> Vec { } fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<(), CliError> { + let mut packages = Vec::new(); for app in &bundle.apps { let mapping = map_legacy_app( &LegacyAppRecord { - kind: app.kind.to_legacy_kind()?, + kind: app.kind.to_legacy_kind(), installed_id: app.installed_id.clone(), official_package_available: app.official_package_available, common_conversion_available: app.common_conversion_available, @@ -588,14 +682,7 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( }), ) .map_err(|source| CliError::Storage(source.to_string()))?; - db.upsert_tracked_package(&TrackedPackageUpsert { - package_id: mapping.package_id, - enabled: true, - favorite: mapping.user_state.favorite, - ignored_version: mapping.user_state.ignored_version, - repository_id: None, - package_resolution: stored_resolution(mapping.package_resolution), - })?; + packages.push(tracked_package_upsert(mapping)); } let report_json = json!({ @@ -604,10 +691,58 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( "imported_records": bundle.apps.len(), }) .to_string(); - db.insert_migration_record("legacy-room-v17", "legacy-room-bundle", &report_json)?; + db.import_tracked_packages_with_migration_record( + &packages, + &MigrationRecordUpsert { + id: LEGACY_ROOM_MIGRATION_ID, + source: "legacy-room-bundle", + report_json: &report_json, + }, + )?; Ok(()) } +fn import_legacy_room_db(db: &MainDb, import: &LegacyRoomDbImport) -> Result<(), CliError> { + let mut packages = Vec::new(); + for app in &import.apps { + let mapping = map_legacy_app(&app.app, Some(&app.user_state)) + .map_err(|source| CliError::Storage(source.to_string()))?; + packages.push(tracked_package_upsert(mapping)); + } + + let report_json = json!({ + "ok": true, + "source": "legacy-room-db", + "version": import.version, + "imported_records": import.apps.len(), + "source_counts": source_counts_json(import), + "warnings": import_warnings_json(&import.warnings), + }) + .to_string(); + db.import_tracked_packages_with_migration_record( + &packages, + &MigrationRecordUpsert { + id: LEGACY_ROOM_MIGRATION_ID, + source: "legacy-room-db", + report_json: &report_json, + }, + )?; + Ok(()) +} + +fn tracked_package_upsert( + mapping: getter_storage::legacy_room::LegacyAppMapping, +) -> TrackedPackageUpsert { + TrackedPackageUpsert { + package_id: mapping.package_id, + enabled: true, + favorite: mapping.user_state.favorite, + ignored_version: mapping.user_state.ignored_version, + repository_id: None, + package_resolution: stored_resolution(mapping.package_resolution), + } +} + fn stored_resolution(resolution: LegacyPackageResolution) -> StoredPackageResolution { match resolution { LegacyPackageResolution::OfficialRepositoryPackage => { @@ -622,6 +757,54 @@ fn stored_resolution(resolution: LegacyPackageResolution) -> StoredPackageResolu } } +fn legacy_db_import_error( + data_dir: &Path, + legacy_db: &Path, + source: LegacyRoomReadError, +) -> CliError { + let (code, unsupported) = match source { + LegacyRoomReadError::UnsupportedVersion { .. } => ("migration.unsupported_db", true), + LegacyRoomReadError::Sqlite(_) | LegacyRoomReadError::MissingRequiredTable(_) => { + ("migration.invalid_db", false) + } + }; + match create_migration_report_with_source_counts( + data_dir, + legacy_db, + code, + &source.to_string(), + 0, + 0, + &[], + None, + ) { + Ok(report_path) if unsupported => CliError::UnsupportedLegacyDb { report_path }, + Ok(report_path) => CliError::InvalidLegacyDb { report_path }, + Err(error) => error, + } +} + +fn source_counts_json(import: &LegacyRoomDbImport) -> Value { + json!({ + "app_rows": import.source_counts.app_rows, + "extra_app_rows": import.source_counts.extra_app_rows, + "hub_rows": import.source_counts.hub_rows, + "extra_hub_rows": import.source_counts.extra_hub_rows, + }) +} + +fn import_warnings_json(warnings: &[LegacyRoomImportWarning]) -> Vec { + warnings + .iter() + .map(|warning| { + json!({ + "code": warning.code(), + "message": warning.message(), + }) + }) + .collect() +} + fn main_db_path(data_dir: &Path) -> PathBuf { data_dir.join(MAIN_DB_FILE) } @@ -632,11 +815,35 @@ fn cache_db_path(data_dir: &Path) -> PathBuf { fn create_migration_report( data_dir: &Path, - bundle: &Path, + source_file: &Path, code: &str, detail: &str, imported_records: u64, tracked_records: u64, + warnings: &[Value], +) -> Result { + create_migration_report_with_source_counts( + data_dir, + source_file, + code, + detail, + imported_records, + tracked_records, + warnings, + None, + ) +} + +#[allow(clippy::too_many_arguments)] +fn create_migration_report_with_source_counts( + data_dir: &Path, + source_file: &Path, + code: &str, + detail: &str, + imported_records: u64, + tracked_records: u64, + warnings: &[Value], + source_counts: Option<&Value>, ) -> Result { let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); fs::create_dir_all(&reports_dir).map_err(|source| { @@ -651,13 +858,17 @@ fn create_migration_report( message: match code { "migration.invalid_bundle" => "Legacy Room export bundle is invalid", "migration.unsupported_bundle" => "Legacy Room bundle import is not implemented yet", - "migration.imported" => "Legacy Room bundle imported", + "migration.invalid_db" => "Legacy Room database is invalid", + "migration.unsupported_db" => "Legacy Room database version is not supported", + "migration.imported" => "Legacy Room data imported", _ => "Legacy migration failed", }, - bundle_file_name: bundle.file_name().and_then(|name| name.to_str()), + source_file_name: source_file.file_name().and_then(|name| name.to_str()), detail, imported_records, tracked_records, + warnings, + source_counts, }; let bytes = serde_json::to_vec_pretty(&report) .map_err(|source| CliError::Storage(format!("failed to serialize report: {source}")))?; @@ -708,9 +919,17 @@ fn list_migration_reports(data_dir: &Path) -> Result, CliError> { "ok": report.get("ok").and_then(Value::as_bool).unwrap_or(false), "code": report.get("code").and_then(Value::as_str).unwrap_or("migration.unknown"), "message": report.get("message").and_then(Value::as_str).unwrap_or("Legacy migration report"), - "bundle_file_name": report.get("bundle_file_name").and_then(Value::as_str), + "source_file_name": report + .get("source_file_name") + .or_else(|| report.get("bundle_file_name")) + .and_then(Value::as_str), "imported_records": report.get("imported_records").and_then(Value::as_u64).unwrap_or(0), "tracked_records": report.get("tracked_records").and_then(Value::as_u64).unwrap_or(0), + "warnings": report + .get("warnings") + .cloned() + .unwrap_or_else(|| Value::Array(Vec::new())), + "source_counts": report.get("source_counts").cloned().unwrap_or(Value::Null), })) }) .collect() @@ -721,10 +940,13 @@ struct MigrationReport<'a> { ok: bool, code: &'a str, message: &'a str, - bundle_file_name: Option<&'a str>, + source_file_name: Option<&'a str>, detail: &'a str, imported_records: u64, tracked_records: u64, + warnings: &'a [Value], + #[serde(skip_serializing_if = "Option::is_none")] + source_counts: Option<&'a Value>, } fn success_envelope(command: &str, data: Value) -> String { @@ -762,7 +984,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -795,11 +1017,11 @@ enum LegacyBundleAppKind { } impl LegacyBundleAppKind { - fn to_legacy_kind(&self) -> Result { - Ok(match self { + const fn to_legacy_kind(&self) -> LegacyAppKind { + match self { Self::Android => LegacyAppKind::Android, Self::Magisk => LegacyAppKind::Magisk, - }) + } } } @@ -816,6 +1038,7 @@ impl CliCommand { Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", + Self::LegacyImportRoomDb { .. } => "legacy import-room-db", Self::LegacyReportList => "legacy report-list", } } diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index ddc42f2..5d07d58 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -1,4 +1,5 @@ use cucumber::{given, then, when, World as _}; +use rusqlite::Connection; use serde_json::Value; use std::fs; use std::path::PathBuf; @@ -10,6 +11,7 @@ struct CliWorld { temp: Option, data_dir: Option, bundle: Option, + legacy_db: Option, fixture_repo_id: Option, fixture_repo_path: Option, fixture_package_id: Option, @@ -65,6 +67,48 @@ fn valid_legacy_export_bundle_with_android_app(world: &mut CliWorld) { world.bundle = Some(bundle); } +#[given("a legacy Room v17 database with an Android app and extra app state")] +fn legacy_room_v17_database_with_android_app(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let legacy_db = temp.path().join("app_metadata_database.db"); + create_fixture_legacy_room_db(&legacy_db, 17, true); + world.legacy_db = Some(legacy_db); +} + +#[given("an unsupported legacy Room database")] +fn unsupported_legacy_room_database(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let legacy_db = temp.path().join("app_metadata_database.db"); + create_fixture_legacy_room_db(&legacy_db, 16, true); + world.legacy_db = Some(legacy_db); +} + +#[given("a malformed legacy Room database")] +fn malformed_legacy_room_database(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let legacy_db = temp.path().join("app_metadata_database.db"); + create_fixture_legacy_room_db(&legacy_db, 17, false); + world.legacy_db = Some(legacy_db); +} + +#[given("a legacy Room v17 database with only unsupported app rows")] +fn legacy_room_v17_database_with_only_unsupported_app_rows(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let legacy_db = temp.path().join("app_metadata_database.db"); + create_fixture_legacy_room_db(&legacy_db, 17, true); + let conn = Connection::open(&legacy_db).expect("open legacy Room fixture"); + conn.execute("DELETE FROM extra_app", []) + .expect("delete fixture extra_app"); + conn.execute("DELETE FROM app", []) + .expect("delete fixture app"); + conn.execute( + "INSERT INTO app(id, name, app_id, ignore_version_number, star) VALUES (1, 'Unsupported', ?1, NULL, 0)", + [r#"{"unknown_provider":"com.example.unsupported"}"#], + ) + .expect("insert unsupported app row"); + world.legacy_db = Some(legacy_db); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -263,6 +307,21 @@ fn run_getter_legacy_import(world: &mut CliWorld) { world.json = None; } +#[when("I run getter legacy import-room-db for that database")] +fn run_getter_legacy_import_db(world: &mut CliWorld) { + let legacy_db = world.legacy_db.as_ref().expect("legacy db exists"); + let output = run_getter( + world, + [ + "legacy".to_owned(), + "import-room-db".to_owned(), + legacy_db.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter legacy report-list for that directory")] fn run_getter_legacy_report_list(world: &mut CliWorld) { let output = run_getter(world, ["legacy".to_owned(), "report-list".to_owned()]); @@ -282,8 +341,31 @@ fn command_fails_with_migration_error(world: &mut CliWorld) { assert_eq!(output.status.code(), Some(20)); let json = parse_stdout(output); assert_eq!(json["ok"], false); - assert_eq!(json["command"], "legacy import-room-bundle"); - assert_eq!(json["error"]["code"], "migration.invalid_bundle"); + assert!( + json["command"] == "legacy import-room-bundle" + || json["command"] == "legacy import-room-db" + ); + assert!(matches!( + json["error"]["code"].as_str(), + Some("migration.invalid_bundle" | "migration.invalid_db" | "migration.unsupported_db") + )); + world.json = Some(json); +} + +#[then(expr = "the command fails with direct DB migration error {string}")] +fn command_fails_with_direct_db_migration_error(world: &mut CliWorld, code: String) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(20)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["command"], "legacy import-room-db"); + assert_eq!(json["error"]["code"], code); + let report_path = json["error"]["report_path"] + .as_str() + .expect("report_path should be a string"); + let report = fs::read_to_string(report_path).expect("report should be readable"); + let report_json: Value = serde_json::from_str(&report).expect("report should be JSON"); + assert_eq!(report_json["code"], code); world.json = Some(json); } @@ -451,7 +533,10 @@ fn sanitized_migration_report_available(world: &mut CliWorld) { let report_json: Value = serde_json::from_str(&report).expect("report should be JSON"); assert_eq!(report_json["ok"], false); assert_eq!(report_json["imported_records"], 0); - assert!(report_json.get("bundle_file_name").is_some()); + assert!( + report_json.get("source_file_name").is_some() + || report_json.get("bundle_file_name").is_some() + ); assert!( report_json.get("raw_bundle").is_none(), "report must not include raw bundle content" @@ -462,7 +547,10 @@ fn sanitized_migration_report_available(world: &mut CliWorld) { fn import_reports_one_tracked_app(world: &mut CliWorld) { let json = current_json(world); assert_eq!(json["ok"], true); - assert_eq!(json["command"], "legacy import-room-bundle"); + assert!( + json["command"] == "legacy import-room-bundle" + || json["command"] == "legacy import-room-db" + ); assert_eq!(json["data"]["imported_records"], 1); assert_eq!( json["data"]["apps"].as_array().expect("apps array").len(), @@ -470,6 +558,64 @@ fn import_reports_one_tracked_app(world: &mut CliWorld) { ); } +#[then("the direct migration reports dropped legacy hub warnings")] +fn direct_migration_reports_dropped_hub_warnings(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "legacy import-room-db"); + assert_eq!(json["data"]["source_counts"]["hub_rows"], 1); + assert_eq!(json["data"]["source_counts"]["extra_hub_rows"], 1); + let warnings = json["data"]["warnings"].as_array().expect("warnings array"); + assert!(warnings + .iter() + .any(|warning| { warning["code"].as_str() == Some("legacy.dropped_hub_rows") })); + assert!(warnings + .iter() + .any(|warning| { warning["code"].as_str() == Some("legacy.dropped_extra_hub_rows") })); +} + +#[then("the direct migration report stays sanitized")] +fn direct_migration_report_stays_sanitized(world: &mut CliWorld) { + let json = current_json(world); + let stdout = serde_json::to_string(json).expect("JSON output serializes"); + assert_sanitized_direct_room_report_text(&stdout); + let report_path = json["data"]["report_path"] + .as_str() + .expect("report_path should be a string"); + let report = fs::read_to_string(report_path).expect("report should be readable"); + assert_sanitized_direct_room_report_text(&report); + let report_json: Value = serde_json::from_str(&report).expect("report should be JSON"); + assert_eq!(report_json["source_counts"]["hub_rows"], 1); + assert_eq!(report_json["source_counts"]["extra_hub_rows"], 1); + assert_report_has_drop_warnings(&report_json); +} + +#[then("the output reports the legacy Room migration was already completed")] +fn output_reports_legacy_room_migration_already_completed(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "legacy import-room-db"); + assert_eq!(json["data"]["already_imported"], true); + assert_eq!(json["data"]["imported_records"], 0); + assert_eq!( + json["data"]["apps"].as_array().expect("apps array").len(), + 1 + ); +} + +#[then("the bundle output reports the legacy Room migration was already completed")] +fn bundle_output_reports_legacy_room_migration_already_completed(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "legacy import-room-bundle"); + assert_eq!(json["data"]["already_imported"], true); + assert_eq!(json["data"]["imported_records"], 0); + assert_eq!( + json["data"]["apps"].as_array().expect("apps array").len(), + 1 + ); +} + #[then(expr = "the output lists migration report {string}")] fn output_lists_migration_report(world: &mut CliWorld, code: String) { let json = current_json(world); @@ -484,6 +630,21 @@ fn output_lists_migration_report(world: &mut CliWorld, code: String) { ); } +#[then("the direct migration report list stays sanitized")] +fn direct_migration_report_list_stays_sanitized(world: &mut CliWorld) { + let json = current_json(world); + let report_list = serde_json::to_string(json).expect("report list serializes"); + assert_sanitized_direct_room_report_text(&report_list); + let reports = json["data"]["reports"].as_array().expect("reports array"); + let imported = reports + .iter() + .find(|report| report["code"].as_str() == Some("migration.imported")) + .expect("imported report exists"); + assert_eq!(imported["source_counts"]["hub_rows"], 1); + assert_eq!(imported["source_counts"]["extra_hub_rows"], 1); + assert_report_has_drop_warnings(imported); +} + #[then(expr = "the app list contains imported package {string}")] fn app_list_contains_imported_package(world: &mut CliWorld, package_id: String) { let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); @@ -501,6 +662,23 @@ fn app_list_contains_imported_package(world: &mut CliWorld, package_id: String) world.json = Some(json); } +#[then(expr = "the app list contains directly imported package {string}")] +fn app_list_contains_directly_imported_package(world: &mut CliWorld, package_id: String) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); + let json = parse_stdout(&output); + let apps = json["data"]["apps"].as_array().expect("apps array"); + let app = apps + .iter() + .find(|app| app["id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); + assert_eq!(app["favorite"], true); + assert_eq!(app["ignored_version"], "1.20.0"); + assert_eq!(app["package_resolution"], "missing_package_definition"); + world.output = Some(output); + world.json = Some(json); +} + fn current_json(world: &mut CliWorld) -> &Value { world .json @@ -549,6 +727,96 @@ return package_def {{ world.fixture_package_id = Some(package_id); } +fn create_fixture_legacy_room_db(path: &PathBuf, version: u32, include_app_table: bool) { + let conn = Connection::open(path).expect("create legacy Room fixture"); + conn.pragma_update(None, "user_version", version) + .expect("set user_version"); + if include_app_table { + conn.execute_batch( + r#" +CREATE TABLE app ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + app_id TEXT NOT NULL, + invalid_version_number_field_regex TEXT, + include_version_number_field_regex TEXT, + ignore_version_number TEXT, + cloud_config TEXT, + enable_hub_list TEXT, + star INTEGER +); +CREATE TABLE extra_app ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + app_id TEXT NOT NULL, + mark_version_number TEXT +); +CREATE TABLE hub ( + uuid TEXT PRIMARY KEY, + hub_config TEXT NOT NULL, + auth TEXT NOT NULL, + ignore_app_id_list TEXT NOT NULL, + applications_mode INTEGER NOT NULL DEFAULT 0, + user_ignore_app_id_list TEXT NOT NULL, + sort_point INTEGER NOT NULL DEFAULT 0 +); +CREATE TABLE extra_hub ( + id TEXT PRIMARY KEY, + enable_global INTEGER NOT NULL DEFAULT 0, + url_replace_search TEXT, + url_replace_string TEXT +); +"#, + ) + .expect("create legacy tables"); + let app_id = r#"{"android_app_package":"org.fdroid.fdroid"}"#; + conn.execute( + "INSERT INTO app(id, name, app_id, ignore_version_number, star) VALUES (1, 'F-Droid', ?1, '1.10.0', 1)", + [app_id], + ) + .expect("insert app"); + conn.execute( + "INSERT INTO extra_app(id, app_id, mark_version_number) VALUES (1, ?1, '1.20.0')", + [app_id], + ) + .expect("insert extra app"); + conn.execute( + "INSERT INTO hub(uuid, hub_config, auth, ignore_app_id_list, user_ignore_app_id_list) VALUES ('legacy-hub', '{\"secret\":\"UA_DIRECT_DB_SENTINEL_SECRET\"}', '{\"token\":\"UA_DIRECT_DB_SENTINEL_TOKEN\"}', '[]', '[]')", + [], + ) + .expect("insert hub"); + conn.execute( + "INSERT INTO extra_hub(id, enable_global, url_replace_search, url_replace_string) VALUES ('GLOBAL', 1, 'UA_DIRECT_DB_SENTINEL_SEARCH', 'UA_DIRECT_DB_SENTINEL_REPLACE')", + [], + ) + .expect("insert extra hub"); + } +} + +fn assert_report_has_drop_warnings(report: &Value) { + let warnings = report["warnings"].as_array().expect("warnings array"); + assert!(warnings + .iter() + .any(|warning| warning["code"].as_str() == Some("legacy.dropped_hub_rows"))); + assert!(warnings + .iter() + .any(|warning| warning["code"].as_str() == Some("legacy.dropped_extra_hub_rows"))); +} + +fn assert_sanitized_direct_room_report_text(text: &str) { + for forbidden in [ + "UA_DIRECT_DB_SENTINEL_SECRET", + "UA_DIRECT_DB_SENTINEL_TOKEN", + "UA_DIRECT_DB_SENTINEL_SEARCH", + "UA_DIRECT_DB_SENTINEL_REPLACE", + "legacy-hub", + ] { + assert!( + !text.contains(forbidden), + "direct Room migration report must not expose {forbidden}: {text}" + ); + } +} + fn create_custom_fixture_lua_repository( world: &mut CliWorld, repo_id: String, diff --git a/crates/getter-cli/tests/features/cli/legacy_import_room_db.feature b/crates/getter-cli/tests/features/cli/legacy_import_room_db.feature new file mode 100644 index 0000000..8ddcf2b --- /dev/null +++ b/crates/getter-cli/tests/features/cli/legacy_import_room_db.feature @@ -0,0 +1,70 @@ +@getter-cli @migration +Feature: Direct legacy Room database import + Scenario: User imports a supported legacy Room database into tracked app state + Given an initialized getter data directory + And a legacy Room v17 database with an Android app and extra app state + When I run getter legacy import-room-db for that database + Then the command succeeds + And the output is valid JSON + And the import reports one tracked app + And the direct migration reports dropped legacy hub warnings + And the direct migration report stays sanitized + And the app list contains directly imported package "android/org.fdroid.fdroid" + When I run getter legacy report-list for that directory + Then the output lists migration report "migration.imported" + And the direct migration report list stays sanitized + + Scenario: User does not rerun a completed direct legacy Room migration + Given an initialized getter data directory + And a legacy Room v17 database with an Android app and extra app state + When I run getter legacy import-room-db for that database + Then the command succeeds + When I run getter legacy import-room-db for that database + Then the command succeeds + And the output reports the legacy Room migration was already completed + + Scenario: User cannot apply a bridge bundle after completed direct legacy Room migration + Given an initialized getter data directory + And a legacy Room v17 database with an Android app and extra app state + And a syntactically valid legacy export bundle with an Android app + When I run getter legacy import-room-db for that database + Then the command succeeds + When I run getter legacy import-room-bundle for that bundle + Then the command succeeds + And the bundle output reports the legacy Room migration was already completed + And the app list contains directly imported package "android/org.fdroid.fdroid" + + Scenario: User cannot apply a direct legacy Room database after completed bridge bundle migration + Given an initialized getter data directory + And a syntactically valid legacy export bundle with an Android app + And a legacy Room v17 database with an Android app and extra app state + When I run getter legacy import-room-bundle for that bundle + Then the command succeeds + When I run getter legacy import-room-db for that database + Then the command succeeds + And the output reports the legacy Room migration was already completed + And the app list contains imported package "android/org.fdroid.fdroid" + + Scenario: User receives a recovery report for an unsupported legacy Room database + Given an initialized getter data directory + And an unsupported legacy Room database + When I run getter legacy import-room-db for that database + Then the command fails with direct DB migration error "migration.unsupported_db" + And no partially usable imported state is created + And a sanitized migration report is available + + Scenario: User receives a recovery report for a malformed legacy Room database + Given an initialized getter data directory + And a malformed legacy Room database + When I run getter legacy import-room-db for that database + Then the command fails with direct DB migration error "migration.invalid_db" + And no partially usable imported state is created + And a sanitized migration report is available + + Scenario: User receives a recovery report when no legacy app rows can be mapped + Given an initialized getter data directory + And a legacy Room v17 database with only unsupported app rows + When I run getter legacy import-room-db for that database + Then the command fails with direct DB migration error "migration.invalid_db" + And no partially usable imported state is created + And a sanitized migration report is available diff --git a/crates/getter-storage/Cargo.toml b/crates/getter-storage/Cargo.toml index 6a561a4..bc0964b 100644 --- a/crates/getter-storage/Cargo.toml +++ b/crates/getter-storage/Cargo.toml @@ -6,6 +6,7 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core" } rusqlite = { version = "0.32", features = ["bundled"] } +serde_json = "1" thiserror = "1" [dev-dependencies] diff --git a/crates/getter-storage/src/legacy_room.rs b/crates/getter-storage/src/legacy_room.rs index 798809e..711ea1f 100644 --- a/crates/getter-storage/src/legacy_room.rs +++ b/crates/getter-storage/src/legacy_room.rs @@ -1,10 +1,18 @@ //! Legacy Room migration mapping tests and pure mapping helpers. //! -//! This module intentionally contains no Android Room/database reader. It is the -//! TDD boundary for source->target mapping rules before the full migration -//! implementation is added. +//! This module owns Rust-side interpretation of legacy Room data. Android and +//! Flutter adapters may locate/checkpoint/copy the old SQLite files, but package +//! identity, user-state mapping, warnings, and target semantics live here. use getter_core::PackageId; +use rusqlite::{Connection, OpenFlags}; +use serde_json::Value; +use std::collections::HashMap; +use std::path::Path; + +pub const LEGACY_ROOM_SUPPORTED_VERSION: u32 = 17; +const LEGACY_ANDROID_APP_TYPE: &str = "android_app_package"; +const LEGACY_ANDROID_MAGISK_MODULE_TYPE: &str = "android_magisk_module"; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LegacyAppKind { @@ -52,6 +60,64 @@ pub enum LegacyMigrationWarning { MissingPackageDefinition { package_id: PackageId }, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LegacyRoomDbImport { + pub version: u32, + pub source_counts: LegacyRoomSourceCounts, + pub apps: Vec, + pub warnings: Vec, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct LegacyRoomSourceCounts { + pub app_rows: u64, + pub extra_app_rows: u64, + pub hub_rows: u64, + pub extra_hub_rows: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LegacyRoomImportedApp { + pub app: LegacyAppRecord, + pub user_state: LegacyExtraAppRecord, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum LegacyRoomImportWarning { + SkippedApp { row_id: i64, reason: String }, + SkippedExtraApp { row_id: i64, reason: String }, + DroppedHubRows { rows: u64 }, + DroppedExtraHubRows { rows: u64 }, +} + +impl LegacyRoomImportWarning { + pub fn code(&self) -> &'static str { + match self { + Self::SkippedApp { .. } => "legacy.skipped_app", + Self::SkippedExtraApp { .. } => "legacy.skipped_extra_app", + Self::DroppedHubRows { .. } => "legacy.dropped_hub_rows", + Self::DroppedExtraHubRows { .. } => "legacy.dropped_extra_hub_rows", + } + } + + pub fn message(&self) -> String { + match self { + Self::SkippedApp { row_id, reason } => { + format!("Skipped legacy app row {row_id}: {reason}") + } + Self::SkippedExtraApp { row_id, reason } => { + format!("Skipped legacy extra_app row {row_id}: {reason}") + } + Self::DroppedHubRows { rows } => { + format!("Legacy hub rows are not imported in this slice: {rows}") + } + Self::DroppedExtraHubRows { rows } => { + format!("Legacy extra_hub rows are not imported in this slice: {rows}") + } + } + } +} + #[derive(Debug, thiserror::Error)] pub enum LegacyRoomMappingError { #[error("legacy installed id is empty")] @@ -60,6 +126,16 @@ pub enum LegacyRoomMappingError { PackageId(#[from] getter_core::PackageIdError), } +#[derive(Debug, thiserror::Error)] +pub enum LegacyRoomReadError { + #[error("sqlite error: {0}")] + Sqlite(#[from] rusqlite::Error), + #[error("unsupported legacy Room database version {found}; expected {expected}")] + UnsupportedVersion { found: u32, expected: u32 }, + #[error("legacy Room database is missing required table '{0}'")] + MissingRequiredTable(&'static str), +} + pub fn map_legacy_app( app: &LegacyAppRecord, extra: Option<&LegacyExtraAppRecord>, @@ -107,6 +183,227 @@ pub fn map_legacy_package_id( Ok(format!("{prefix}/{installed_id}").parse()?) } +pub fn read_legacy_room_database(path: &Path) -> Result { + let conn = Connection::open_with_flags(path, OpenFlags::SQLITE_OPEN_READ_ONLY)?; + let version = legacy_user_version(&conn)?; + if version != LEGACY_ROOM_SUPPORTED_VERSION { + return Err(LegacyRoomReadError::UnsupportedVersion { + found: version, + expected: LEGACY_ROOM_SUPPORTED_VERSION, + }); + } + if !table_exists(&conn, "app")? { + return Err(LegacyRoomReadError::MissingRequiredTable("app")); + } + + let mut warnings = Vec::new(); + let extra_apps = read_extra_apps(&conn, &mut warnings)?; + let apps = read_apps(&conn, &extra_apps, &mut warnings)?; + let source_counts = LegacyRoomSourceCounts { + app_rows: table_row_count(&conn, "app")?.unwrap_or(0), + extra_app_rows: table_row_count(&conn, "extra_app")?.unwrap_or(0), + hub_rows: table_row_count(&conn, "hub")?.unwrap_or(0), + extra_hub_rows: table_row_count(&conn, "extra_hub")?.unwrap_or(0), + }; + if source_counts.hub_rows > 0 { + warnings.push(LegacyRoomImportWarning::DroppedHubRows { + rows: source_counts.hub_rows, + }); + } + if source_counts.extra_hub_rows > 0 { + warnings.push(LegacyRoomImportWarning::DroppedExtraHubRows { + rows: source_counts.extra_hub_rows, + }); + } + + Ok(LegacyRoomDbImport { + version, + source_counts, + apps, + warnings, + }) +} + +fn legacy_user_version(conn: &Connection) -> Result { + let version: u32 = conn.query_row("PRAGMA user_version", [], |row| row.get(0))?; + Ok(version) +} + +fn read_apps( + conn: &Connection, + extra_apps: &HashMap, + warnings: &mut Vec, +) -> Result, LegacyRoomReadError> { + let mut stmt = conn.prepare( + r#" +SELECT id, app_id, ignore_version_number, star +FROM app +ORDER BY id ASC +"#, + )?; + let rows = stmt.query_map([], |row| { + Ok(( + row.get::<_, i64>(0)?, + row.get::<_, String>(1)?, + row.get::<_, Option>(2)?, + row.get::<_, Option>(3)?, + )) + })?; + + let mut apps = Vec::new(); + for row in rows { + let (row_id, app_id_json, ignored_version, star) = row?; + let app_id = match parse_app_id_map(&app_id_json) { + Ok(app_id) => app_id, + Err(reason) => { + warnings.push(LegacyRoomImportWarning::SkippedApp { row_id, reason }); + continue; + } + }; + let (kind, installed_id) = match installed_id_from_app_id(&app_id) { + Ok(target) => target, + Err(reason) => { + warnings.push(LegacyRoomImportWarning::SkippedApp { row_id, reason }); + continue; + } + }; + let package_key = match map_legacy_package_id(kind, &installed_id) { + Ok(package_id) => package_id.to_string(), + Err(error) => { + warnings.push(LegacyRoomImportWarning::SkippedApp { + row_id, + reason: error.to_string(), + }); + continue; + } + }; + let extra = extra_apps.get(&package_key); + apps.push(LegacyRoomImportedApp { + app: LegacyAppRecord { + kind, + installed_id, + official_package_available: false, + common_conversion_available: false, + }, + user_state: LegacyExtraAppRecord { + ignored_version: extra + .and_then(|extra| extra.ignored_version.clone()) + .or(ignored_version), + favorite: star.unwrap_or(0) != 0, + }, + }); + } + Ok(apps) +} + +fn read_extra_apps( + conn: &Connection, + warnings: &mut Vec, +) -> Result, LegacyRoomReadError> { + if !table_exists(conn, "extra_app")? { + return Ok(HashMap::new()); + } + let mut stmt = conn.prepare( + r#" +SELECT id, app_id, mark_version_number +FROM extra_app +ORDER BY id ASC +"#, + )?; + let rows = stmt.query_map([], |row| { + Ok(( + row.get::<_, i64>(0)?, + row.get::<_, String>(1)?, + row.get::<_, Option>(2)?, + )) + })?; + + let mut extras = HashMap::new(); + for row in rows { + let (row_id, app_id_json, ignored_version) = row?; + let app_id = match parse_app_id_map(&app_id_json) { + Ok(app_id) => app_id, + Err(reason) => { + warnings.push(LegacyRoomImportWarning::SkippedExtraApp { row_id, reason }); + continue; + } + }; + let (kind, installed_id) = match installed_id_from_app_id(&app_id) { + Ok(target) => target, + Err(reason) => { + warnings.push(LegacyRoomImportWarning::SkippedExtraApp { row_id, reason }); + continue; + } + }; + let package_id = match map_legacy_package_id(kind, &installed_id) { + Ok(package_id) => package_id, + Err(error) => { + warnings.push(LegacyRoomImportWarning::SkippedExtraApp { + row_id, + reason: error.to_string(), + }); + continue; + } + }; + extras.insert( + package_id.to_string(), + LegacyExtraAppRecord { + ignored_version, + favorite: false, + }, + ); + } + Ok(extras) +} + +fn parse_app_id_map(value: &str) -> Result, String> { + let value: Value = serde_json::from_str(value).map_err(|error| error.to_string())?; + let object = value + .as_object() + .ok_or_else(|| "app_id must be a JSON object".to_owned())?; + let mut app_id = HashMap::new(); + for (key, value) in object { + if let Some(value) = value.as_str().filter(|value| !value.is_empty()) { + app_id.insert(key.clone(), value.to_owned()); + } + } + Ok(app_id) +} + +fn installed_id_from_app_id( + app_id: &HashMap, +) -> Result<(LegacyAppKind, String), String> { + if let Some(module_id) = app_id.get(LEGACY_ANDROID_MAGISK_MODULE_TYPE) { + return Ok((LegacyAppKind::Magisk, module_id.clone())); + } + if let Some(package_name) = app_id.get(LEGACY_ANDROID_APP_TYPE) { + return Ok((LegacyAppKind::Android, package_name.clone())); + } + Err(format!( + "app_id has no supported '{}' or '{}' value", + LEGACY_ANDROID_APP_TYPE, LEGACY_ANDROID_MAGISK_MODULE_TYPE + )) +} + +fn table_exists(conn: &Connection, table: &str) -> Result { + let exists: i64 = conn.query_row( + "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = ?1", + [table], + |row| row.get(0), + )?; + Ok(exists != 0) +} + +fn table_row_count(conn: &Connection, table: &str) -> Result, LegacyRoomReadError> { + if !table_exists(conn, table)? { + return Ok(None); + } + let count: u64 = conn.query_row(&format!("SELECT COUNT(*) FROM {table}"), [], |row| { + row.get(0) + })?; + Ok(Some(count)) +} + #[cfg(test)] mod tests { use super::*; @@ -212,4 +509,187 @@ mod tests { assert_eq!(mapping.user_state.ignored_version.as_deref(), Some("1.2.3")); assert!(mapping.user_state.favorite); } + + #[test] + fn direct_room_reader_imports_app_rows_and_extra_app_state() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("legacy.db"); + create_fixture_legacy_db(&db_path, LEGACY_ROOM_SUPPORTED_VERSION); + + let import = read_legacy_room_database(&db_path).unwrap(); + + assert_eq!(import.version, LEGACY_ROOM_SUPPORTED_VERSION); + assert_eq!(import.apps.len(), 1); + assert_eq!(import.source_counts.app_rows, 1); + assert_eq!(import.source_counts.extra_app_rows, 1); + assert_eq!(import.source_counts.hub_rows, 1); + assert_eq!(import.source_counts.extra_hub_rows, 1); + assert_eq!(import.apps[0].app.kind, LegacyAppKind::Android); + assert_eq!(import.apps[0].app.installed_id, "org.fdroid.fdroid"); + assert_eq!( + import.apps[0].user_state.ignored_version.as_deref(), + Some("1.20.0") + ); + assert!(import.apps[0].user_state.favorite); + assert!(import + .warnings + .iter() + .any(|warning| matches!(warning, LegacyRoomImportWarning::DroppedHubRows { rows: 1 }))); + assert!(import.warnings.iter().any(|warning| matches!( + warning, + LegacyRoomImportWarning::DroppedExtraHubRows { rows: 1 } + ))); + } + + #[test] + fn direct_room_reader_maps_magisk_app_id_rows() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("legacy.db"); + create_fixture_legacy_db(&db_path, LEGACY_ROOM_SUPPORTED_VERSION); + let conn = Connection::open(&db_path).unwrap(); + conn.execute("DELETE FROM app", []).unwrap(); + conn.execute("DELETE FROM extra_app", []).unwrap(); + conn.execute( + "INSERT INTO app(id, name, app_id, ignore_version_number, star) VALUES (1, 'Zygisk Next', ?1, NULL, 0)", + [r#"{"android_magisk_module":"zygisk-next"}"#], + ) + .unwrap(); + drop(conn); + + let import = read_legacy_room_database(&db_path).unwrap(); + + assert_eq!(import.apps.len(), 1); + assert_eq!(import.apps[0].app.kind, LegacyAppKind::Magisk); + assert_eq!(import.apps[0].app.installed_id, "zygisk-next"); + let mapping = + map_legacy_app(&import.apps[0].app, Some(&import.apps[0].user_state)).unwrap(); + assert_eq!(mapping.package_id.to_string(), "magisk/zygisk-next"); + } + + #[test] + fn direct_room_reader_skips_invalid_app_rows_without_dropping_valid_apps() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("legacy.db"); + create_fixture_legacy_db(&db_path, LEGACY_ROOM_SUPPORTED_VERSION); + let conn = Connection::open(&db_path).unwrap(); + conn.execute( + "INSERT INTO app(id, name, app_id, ignore_version_number, star) VALUES (2, 'Broken', 'not-json', NULL, 0)", + [], + ) + .unwrap(); + drop(conn); + + let import = read_legacy_room_database(&db_path).unwrap(); + + assert_eq!(import.source_counts.app_rows, 2); + assert_eq!(import.apps.len(), 1); + assert_eq!(import.apps[0].app.installed_id, "org.fdroid.fdroid"); + assert!(import.warnings.iter().any(|warning| matches!( + warning, + LegacyRoomImportWarning::SkippedApp { row_id: 2, .. } + ))); + } + + #[test] + fn direct_room_reader_skips_malformed_optional_extra_app_rows() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("legacy.db"); + create_fixture_legacy_db(&db_path, LEGACY_ROOM_SUPPORTED_VERSION); + let conn = Connection::open(&db_path).unwrap(); + conn.execute( + "INSERT INTO extra_app(id, app_id, mark_version_number) VALUES (2, 'not-json', '9.9.9')", + [], + ) + .unwrap(); + drop(conn); + + let import = read_legacy_room_database(&db_path).unwrap(); + + assert_eq!(import.apps.len(), 1); + assert!(import.warnings.iter().any(|warning| { + matches!( + warning, + LegacyRoomImportWarning::SkippedExtraApp { row_id: 2, .. } + ) + })); + } + + #[test] + fn direct_room_reader_rejects_unsupported_schema_version() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("legacy.db"); + create_fixture_legacy_db(&db_path, 16); + + let error = read_legacy_room_database(&db_path).unwrap_err(); + + assert!(matches!( + error, + LegacyRoomReadError::UnsupportedVersion { + found: 16, + expected: LEGACY_ROOM_SUPPORTED_VERSION, + } + )); + } + + fn create_fixture_legacy_db(path: &Path, version: u32) { + let conn = Connection::open(path).unwrap(); + conn.pragma_update(None, "user_version", version).unwrap(); + conn.execute_batch( + r#" +CREATE TABLE app ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + app_id TEXT NOT NULL, + invalid_version_number_field_regex TEXT, + include_version_number_field_regex TEXT, + ignore_version_number TEXT, + cloud_config TEXT, + enable_hub_list TEXT, + star INTEGER +); +CREATE TABLE extra_app ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + app_id TEXT NOT NULL, + mark_version_number TEXT +); +CREATE TABLE hub ( + uuid TEXT PRIMARY KEY, + hub_config TEXT NOT NULL, + auth TEXT NOT NULL, + ignore_app_id_list TEXT NOT NULL, + applications_mode INTEGER NOT NULL DEFAULT 0, + user_ignore_app_id_list TEXT NOT NULL, + sort_point INTEGER NOT NULL DEFAULT 0 +); +CREATE TABLE extra_hub ( + id TEXT PRIMARY KEY, + enable_global INTEGER NOT NULL DEFAULT 0, + url_replace_search TEXT, + url_replace_string TEXT +); +"#, + ) + .unwrap(); + let app_id = r#"{"android_app_package":"org.fdroid.fdroid"}"#; + conn.execute( + "INSERT INTO app(id, name, app_id, ignore_version_number, star) VALUES (1, 'F-Droid', ?1, '1.10.0', 1)", + [app_id], + ) + .unwrap(); + conn.execute( + "INSERT INTO extra_app(id, app_id, mark_version_number) VALUES (1, ?1, '1.20.0')", + [app_id], + ) + .unwrap(); + conn.execute( + "INSERT INTO hub(uuid, hub_config, auth, ignore_app_id_list, user_ignore_app_id_list) VALUES ('legacy-hub', '{}', '{}', '[]', '[]')", + [], + ) + .unwrap(); + conn.execute( + "INSERT INTO extra_hub(id, enable_global, url_replace_search, url_replace_string) VALUES ('GLOBAL', 1, 'http://', 'https://')", + [], + ) + .unwrap(); + } } diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index 6eed591..a6f0e56 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -4,7 +4,7 @@ pub mod legacy_room; use getter_core::repository::RepositoryMetadata; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; -use rusqlite::{params, Connection}; +use rusqlite::{params, Connection, Params, Transaction}; use std::path::Path; use std::str::FromStr; @@ -180,33 +180,31 @@ ON CONFLICT(id) DO UPDATE SET &self, package: &TrackedPackageUpsert, ) -> Result<(), StorageError> { - self.conn.execute( + execute_tracked_package_upsert(&self.conn, package)?; + Ok(()) + } + + pub fn import_tracked_packages_with_migration_record( + &self, + packages: &[TrackedPackageUpsert], + record: &MigrationRecordUpsert<'_>, + ) -> Result<(), StorageError> { + let tx = self.conn.unchecked_transaction()?; + for package in packages { + execute_tracked_package_upsert(&tx, package)?; + } + tx.execute( r#" -INSERT INTO tracked_packages( - package_id, - enabled, - favorite, - ignored_version, - repository_id, - package_resolution -) -VALUES (?1, ?2, ?3, ?4, ?5, ?6) -ON CONFLICT(package_id) DO UPDATE SET - enabled = excluded.enabled, - favorite = excluded.favorite, - ignored_version = excluded.ignored_version, - repository_id = excluded.repository_id, - package_resolution = excluded.package_resolution +INSERT INTO migration_records(id, source, report_json) +VALUES (?1, ?2, ?3) +ON CONFLICT(id) DO UPDATE SET + source = excluded.source, + completed_at_unix = unixepoch(), + report_json = excluded.report_json "#, - params![ - package.package_id.to_string(), - bool_to_i64(package.enabled), - bool_to_i64(package.favorite), - package.ignored_version.as_deref(), - package.repository_id.as_ref().map(RepositoryId::as_str), - package.package_resolution.as_str(), - ], + params![record.id, record.source, record.report_json], )?; + tx.commit()?; Ok(()) } @@ -263,6 +261,15 @@ ON CONFLICT(id) DO UPDATE SET Ok(()) } + pub fn migration_record_exists(&self, id: &str) -> Result { + let count: i64 = self.conn.query_row( + "SELECT COUNT(*) FROM migration_records WHERE id = ?1", + [id], + |row| row.get(0), + )?; + Ok(count != 0) + } + pub fn migration_records(&self) -> Result, StorageError> { let mut stmt = self.conn.prepare( "SELECT id, source, report_json FROM migration_records ORDER BY completed_at_unix ASC, id ASC", @@ -370,6 +377,13 @@ pub struct StoredMigrationRecord { pub report_json: String, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MigrationRecordUpsert<'a> { + pub id: &'a str, + pub source: &'a str, + pub report_json: &'a str, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum StoredPackageResolution { OfficialRepositoryPackage, @@ -400,6 +414,55 @@ impl FromStr for StoredPackageResolution { } } +trait SqlExecutor { + fn execute_statement(&self, sql: &str, params: P) -> Result; +} + +impl SqlExecutor for Connection { + fn execute_statement(&self, sql: &str, params: P) -> Result { + self.execute(sql, params) + } +} + +impl SqlExecutor for Transaction<'_> { + fn execute_statement(&self, sql: &str, params: P) -> Result { + self.execute(sql, params) + } +} + +fn execute_tracked_package_upsert( + conn: &impl SqlExecutor, + package: &TrackedPackageUpsert, +) -> Result { + conn.execute_statement( + r#" +INSERT INTO tracked_packages( + package_id, + enabled, + favorite, + ignored_version, + repository_id, + package_resolution +) +VALUES (?1, ?2, ?3, ?4, ?5, ?6) +ON CONFLICT(package_id) DO UPDATE SET + enabled = excluded.enabled, + favorite = excluded.favorite, + ignored_version = excluded.ignored_version, + repository_id = excluded.repository_id, + package_resolution = excluded.package_resolution +"#, + params![ + package.package_id.to_string(), + bool_to_i64(package.enabled), + bool_to_i64(package.favorite), + package.ignored_version.as_deref(), + package.repository_id.as_ref().map(RepositoryId::as_str), + package.package_resolution.as_str(), + ], + ) +} + fn bool_to_i64(value: bool) -> i64 { if value { 1 @@ -475,6 +538,7 @@ mod tests { #[test] fn main_db_records_migration_completion() { let db = MainDb::open_in_memory().unwrap(); + assert!(!db.migration_record_exists("legacy-room-v17").unwrap()); db.insert_migration_record("legacy-room-v17", "legacy-room-bundle", r#"{"ok":true}"#) .unwrap(); @@ -482,6 +546,7 @@ mod tests { assert_eq!(records.len(), 1); assert_eq!(records[0].id, "legacy-room-v17"); assert_eq!(records[0].source, "legacy-room-bundle"); + assert!(db.migration_record_exists("legacy-room-v17").unwrap()); } #[test] From 05397e1446f19e728737b5f94b8f8d96036c83f4 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 23 Jun 2026 00:16:46 +0800 Subject: [PATCH 14/72] feat(cli): add installed app autogen --- crates/getter-cli/src/lib.rs | 716 +++++++++++++++++- crates/getter-cli/tests/bdd_cli.rs | 372 +++++++++ .../features/cli/autogen_installed.feature | 92 +++ crates/getter-core/src/autogen.rs | 536 +++++++++++++ crates/getter-core/src/lib.rs | 1 + crates/getter-storage/src/lib.rs | 171 +++++ 6 files changed, 1882 insertions(+), 6 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/autogen_installed.feature create mode 100644 crates/getter-core/src/autogen.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index a15af83..72d92fc 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -4,9 +4,15 @@ //! while durable state is initialized through `getter-storage` so the command //! surface exercises the same Rust-owned SQLite direction used by embedders. +use getter_core::autogen::{ + content_hash, local_autogen_repo_toml, local_repo_toml, plan_local_autogen, AutogenManifest, + AutogenManifestEntry, AutogenPlan, AutogenSkipReason, InstalledInventory, + LOCAL_AUTOGEN_REPOSITORY_ID, LOCAL_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, + LOCAL_REPOSITORY_NAME, +}; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; -use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; +use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_storage::legacy_room::{ map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, @@ -19,13 +25,15 @@ use getter_storage::{ }; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; +use std::collections::{BTreeSet, HashMap}; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::{Component, Path, PathBuf}; const MAIN_DB_FILE: &str = "main.db"; const CACHE_DB_FILE: &str = "cache.db"; const MIGRATION_REPORTS_DIR: &str = "migration-reports"; const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; +const AUTOGEN_MANIFEST_FILE: &str = "autogen-manifest.json"; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliInvocation { @@ -55,6 +63,20 @@ pub enum CliCommand { repo_id: Option, }, StorageValidate, + AutogenInstalledPreview { + inventory: PathBuf, + }, + AutogenInstalledApply { + preview: PathBuf, + acceptance: AutogenAcceptance, + }, + AutogenCleanupPreview { + inventory: PathBuf, + }, + AutogenCleanupApply { + preview: PathBuf, + acceptance: AutogenAcceptance, + }, LegacyImportRoomBundle { bundle: PathBuf, }, @@ -64,6 +86,12 @@ pub enum CliCommand { LegacyReportList, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AutogenAcceptance { + AcceptAll, + Accept(Vec), +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExitCode { Success = 0, @@ -89,6 +117,8 @@ pub enum CliError { Repository(String), #[error("package evaluation error: {0}")] PackageEval(String), + #[error("autogen error: {0}")] + Autogen(String), #[error("Legacy Room export bundle is invalid")] InvalidLegacyBundle { report_path: PathBuf }, #[error("Legacy Room bundle import is not implemented yet")] @@ -104,7 +134,9 @@ impl CliError { match self { Self::Usage(_) => ExitCode::Usage, Self::Storage(_) => ExitCode::Storage, - Self::Repository(_) | Self::PackageEval(_) => ExitCode::GenericFailure, + Self::Repository(_) | Self::PackageEval(_) | Self::Autogen(_) => { + ExitCode::GenericFailure + } Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } | Self::InvalidLegacyDb { .. } @@ -118,6 +150,7 @@ impl CliError { Self::Storage(_) => "storage.error", Self::Repository(_) => "repository.error", Self::PackageEval(_) => "package.eval_error", + Self::Autogen(_) => "autogen.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", Self::InvalidLegacyDb { .. } => "migration.invalid_db", @@ -131,6 +164,7 @@ impl CliError { Self::Storage(_) => "Getter storage operation failed", Self::Repository(_) => "Getter repository operation failed", Self::PackageEval(_) => "Getter package evaluation failed", + Self::Autogen(_) => "Getter autogen operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { "Legacy Room bundle import is not implemented yet" @@ -145,7 +179,8 @@ impl CliError { Self::Usage(detail) | Self::Storage(detail) | Self::Repository(detail) - | Self::PackageEval(detail) => Some(detail.as_str()), + | Self::PackageEval(detail) + | Self::Autogen(detail) => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } | Self::InvalidLegacyDb { .. } @@ -159,7 +194,11 @@ impl CliError { | Self::UnsupportedLegacyBundle { report_path } | Self::InvalidLegacyDb { report_path } | Self::UnsupportedLegacyDb { report_path } => Some(report_path.as_path()), - Self::Usage(_) | Self::Storage(_) | Self::Repository(_) | Self::PackageEval(_) => None, + Self::Usage(_) + | Self::Storage(_) + | Self::Repository(_) + | Self::PackageEval(_) + | Self::Autogen(_) => None, } } } @@ -287,6 +326,48 @@ where [domain, command] if domain == "storage" && command == "validate" => { CliCommand::StorageValidate } + [domain, subject, action, flag, inventory] + if domain == "autogen" + && subject == "installed" + && action == "preview" + && flag == "--inventory" => + { + CliCommand::AutogenInstalledPreview { + inventory: PathBuf::from(inventory), + } + } + [domain, subject, action, flag, preview, rest @ ..] + if domain == "autogen" + && subject == "installed" + && action == "apply" + && flag == "--preview" => + { + CliCommand::AutogenInstalledApply { + preview: PathBuf::from(preview), + acceptance: parse_autogen_acceptance(rest)?, + } + } + [domain, subject, action, flag, inventory] + if domain == "autogen" + && subject == "cleanup" + && action == "preview" + && flag == "--inventory" => + { + CliCommand::AutogenCleanupPreview { + inventory: PathBuf::from(inventory), + } + } + [domain, subject, action, flag, preview, rest @ ..] + if domain == "autogen" + && subject == "cleanup" + && action == "apply" + && flag == "--preview" => + { + CliCommand::AutogenCleanupApply { + preview: PathBuf::from(preview), + acceptance: parse_autogen_acceptance(rest)?, + } + } [domain, command, bundle] if domain == "legacy" && command == "import-room-bundle" => { CliCommand::LegacyImportRoomBundle { bundle: PathBuf::from(bundle), @@ -390,6 +471,33 @@ fn execute(invocation: CliInvocation) -> Result { "cache_db": cache_db_path(&invocation.data_dir), })) } + CliCommand::AutogenInstalledPreview { inventory } => { + let db = open_main_db(&invocation.data_dir)?; + let inventory = read_installed_inventory(&inventory)?; + let plan = build_local_autogen_plan(&db, &inventory)?; + Ok(autogen_installed_preview_json(&invocation.data_dir, &plan)) + } + CliCommand::AutogenInstalledApply { + preview, + acceptance, + } => { + let db = open_main_db(&invocation.data_dir)?; + let preview = read_autogen_preview(&preview, "installed.preview")?; + apply_autogen_installed_preview(&invocation.data_dir, &db, &preview, &acceptance) + } + CliCommand::AutogenCleanupPreview { inventory } => { + let db = open_main_db(&invocation.data_dir)?; + let inventory = read_installed_inventory(&inventory)?; + cleanup_preview_json(&invocation.data_dir, &db, &inventory) + } + CliCommand::AutogenCleanupApply { + preview, + acceptance, + } => { + let db = open_main_db(&invocation.data_dir)?; + let preview = read_autogen_preview(&preview, "cleanup.preview")?; + apply_autogen_cleanup_preview(&invocation.data_dir, &db, &preview, &acceptance) + } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { @@ -558,6 +666,33 @@ fn parse_priority(value: &str) -> Result { .map_err(|source| CliError::Usage(format!("invalid repository priority: {source}"))) } +fn parse_autogen_acceptance(args: &[String]) -> Result { + match args { + [flag] if flag == "--accept-all" => Ok(AutogenAcceptance::AcceptAll), + rest if !rest.is_empty() => { + let mut ids = Vec::new(); + let mut index = 0; + while index < rest.len() { + if rest[index] != "--accept" { + return Err(CliError::Usage( + "autogen apply requires --accept-all or repeated --accept " + .to_owned(), + )); + } + let package_id = rest + .get(index + 1) + .ok_or_else(|| CliError::Usage("--accept requires a package id".to_owned()))?; + ids.push(parse_package_id(package_id)?); + index += 2; + } + Ok(AutogenAcceptance::Accept(ids)) + } + _ => Err(CliError::Usage( + "autogen apply requires --accept-all or --accept ".to_owned(), + )), + } +} + fn load_repository_layout(path: &Path) -> Result { RepositoryLayout::load(path).map_err(|source| CliError::Repository(source.to_string())) } @@ -612,6 +747,571 @@ fn evaluate_highest_priority_package( ))) } +fn read_installed_inventory(path: &Path) -> Result { + let bytes = fs::read(path) + .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; + serde_json::from_slice(&bytes) + .map_err(|source| CliError::Autogen(format!("failed to parse inventory JSON: {source}"))) +} + +fn build_local_autogen_plan( + db: &MainDb, + inventory: &InstalledInventory, +) -> Result { + let covered = higher_priority_package_coverage(db)?; + plan_local_autogen(inventory, &covered).map_err(|source| CliError::Autogen(source.to_string())) +} + +fn higher_priority_package_coverage( + db: &MainDb, +) -> Result, CliError> { + let mut covered = HashMap::new(); + for repo in db.repositories()? { + if repo.id.as_str() == LOCAL_AUTOGEN_REPOSITORY_ID { + continue; + } + if repo.priority <= RepositoryPriority::LOCAL_AUTOGEN { + continue; + } + let Some(path) = repo.path.as_ref() else { + continue; + }; + let layout = load_repository_layout(Path::new(path))?; + for package in layout.packages { + covered.entry(package.id).or_insert_with(|| repo.id.clone()); + } + } + Ok(covered) +} + +fn autogen_installed_preview_json(data_dir: &Path, plan: &AutogenPlan) -> Value { + let candidates: Vec = plan.candidates.iter().map(autogen_candidate_json).collect(); + let skipped: Vec = plan.skipped.iter().map(autogen_skip_json).collect(); + json!({ + "operation": "installed.preview", + "target_repo_id": plan.repository_id.as_str(), + "target_repo_path": local_autogen_repo_path(data_dir), + "summary": { + "candidate_count": candidates.len(), + "skipped_count": skipped.len(), + "write_count": candidates.len(), + "delete_count": 0, + }, + "candidates": candidates, + "skipped": skipped, + "diagnostics": [], + }) +} + +fn autogen_candidate_json(candidate: &getter_core::autogen::AutogenCandidate) -> Value { + json!({ + "package_id": candidate.package_id.to_string(), + "kind": candidate.package_id.kind().as_str(), + "display_name": candidate.name, + "installed_target": candidate.installed, + "action": "create", + "output_relative_path": candidate.relative_path, + "content_hash": candidate.content_hash, + "content": candidate.content, + }) +} + +fn autogen_skip_json(skip: &getter_core::autogen::AutogenSkip) -> Value { + json!({ + "package_id": skip.package_id.to_string(), + "reason": match skip.reason { + AutogenSkipReason::DuplicateInventoryItem => "duplicate_inventory_item", + AutogenSkipReason::CoveredByHigherPriorityRepository => "covered_by_higher_priority_repo", + }, + "covering_repo_id": skip.repository_id.as_ref().map(RepositoryId::as_str), + }) +} + +fn read_autogen_preview(path: &Path, expected_operation: &str) -> Result { + let bytes = fs::read(path) + .map_err(|source| CliError::Autogen(format!("failed to read autogen preview: {source}")))?; + let raw: Value = serde_json::from_slice(&bytes).map_err(|source| { + CliError::Autogen(format!("failed to parse autogen preview JSON: {source}")) + })?; + let payload = if raw.get("ok").is_some() && raw.get("data").is_some() { + raw.get("data").cloned().unwrap_or(Value::Null) + } else { + raw + }; + if payload.get("operation").and_then(Value::as_str) != Some(expected_operation) { + return Err(CliError::Autogen(format!( + "autogen preview operation must be '{expected_operation}'" + ))); + } + Ok(payload) +} + +fn apply_autogen_installed_preview( + data_dir: &Path, + db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> Result { + let repo_path = local_autogen_repo_path(data_dir); + ensure_local_autogen_repository(data_dir, db)?; + let accepted = accepted_preview_candidates(preview, acceptance)?; + let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut applied = Vec::new(); + let mut preserved = Vec::new(); + + for candidate in accepted { + let package_id = preview_package_id(candidate)?; + let relative_path = preview_relative_path(candidate)?; + let content = candidate + .get("content") + .and_then(Value::as_str) + .ok_or_else(|| CliError::Autogen("preview candidate missing content".to_owned()))?; + let expected_hash = candidate + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + CliError::Autogen("preview candidate missing content_hash".to_owned()) + })?; + if content_hash(content) != expected_hash { + return Err(CliError::Autogen(format!( + "preview content hash mismatch for {package_id}" + ))); + } + let target = safe_join(&repo_path, &relative_path)?; + if target.exists() { + let current = fs::read_to_string(&target).map_err(|source| { + CliError::Autogen(format!( + "failed to read existing autogen file '{}': {source}", + target.display() + )) + })?; + let current_hash = content_hash(¤t); + let known_hash = manifest + .package(&package_id) + .map(|entry| entry.content_hash.as_str()); + if known_hash != Some(current_hash.as_str()) { + preserved.push(preserve_autogen_file_in_local( + data_dir, + db, + &package_id, + ¤t, + )?); + } + } + if let Some(parent) = target.parent() { + fs::create_dir_all(parent).map_err(|source| { + CliError::Autogen(format!( + "failed to create autogen package directory '{}': {source}", + parent.display() + )) + })?; + } + fs::write(&target, content).map_err(|source| { + CliError::Autogen(format!( + "failed to write autogen package '{}': {source}", + target.display() + )) + })?; + upsert_manifest_entry( + &mut manifest, + AutogenManifestEntry { + package_id: package_id.clone(), + relative_path: relative_path.clone(), + content_hash: expected_hash.to_owned(), + }, + ); + db.upsert_generated_tracked_package_preserving_user_state( + &package_id, + &RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + )?; + applied.push(json!({ + "package_id": package_id.to_string(), + "output_relative_path": relative_path, + })); + } + + write_autogen_manifest(&repo_path, &manifest)?; + Ok(json!({ + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "applied_count": applied.len(), + "applied": applied, + "preserved_to_local": preserved, + })) +} + +fn cleanup_preview_json( + data_dir: &Path, + db: &MainDb, + inventory: &InstalledInventory, +) -> Result { + let repo_path = local_autogen_repo_path(data_dir); + let Some(manifest) = read_autogen_manifest(&repo_path)? else { + return Ok(json!({ + "operation": "cleanup.preview", + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "summary": { "candidate_count": 0, "skipped_count": 0, "write_count": 0, "delete_count": 0 }, + "candidates": [], + "skipped": [], + "diagnostics": [], + })); + }; + let plan = build_local_autogen_plan(db, inventory)?; + let installed_ids: BTreeSet = plan + .candidates + .iter() + .map(|candidate| candidate.package_id.to_string()) + .chain(plan.skipped.iter().map(|skip| skip.package_id.to_string())) + .collect(); + let mut candidates = Vec::new(); + for entry in manifest.packages { + if !installed_ids.contains(&entry.package_id.to_string()) { + candidates.push(json!({ + "package_id": entry.package_id.to_string(), + "action": "delete", + "output_relative_path": entry.relative_path, + "content_hash": entry.content_hash, + "reason": "not_in_installed_inventory", + })); + } + } + candidates.sort_by_key(|candidate| { + candidate + .get("package_id") + .and_then(Value::as_str) + .unwrap_or_default() + .to_owned() + }); + Ok(json!({ + "operation": "cleanup.preview", + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "summary": { + "candidate_count": candidates.len(), + "skipped_count": 0, + "write_count": 0, + "delete_count": candidates.len(), + }, + "candidates": candidates, + "skipped": [], + "diagnostics": [], + })) +} + +fn apply_autogen_cleanup_preview( + data_dir: &Path, + db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> Result { + if preview.get("target_repo_id").and_then(Value::as_str) != Some(LOCAL_AUTOGEN_REPOSITORY_ID) { + return Err(CliError::Autogen(format!( + "cleanup preview target_repo_id must be '{LOCAL_AUTOGEN_REPOSITORY_ID}'" + ))); + } + let repo_path = local_autogen_repo_path(data_dir); + let accepted = accepted_preview_candidates(preview, acceptance)?; + let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut deleted = Vec::new(); + let mut preserved = Vec::new(); + let local_autogen_id = RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"); + for candidate in accepted { + let package_id = preview_package_id(candidate)?; + let relative_path = preview_relative_path(candidate)?; + let expected_hash = candidate + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + CliError::Autogen("cleanup preview candidate missing content_hash".to_owned()) + })?; + let manifest_entry = manifest.package(&package_id).ok_or_else(|| { + CliError::Autogen(format!( + "cleanup preview candidate {package_id} is not managed by local_autogen manifest" + )) + })?; + if manifest_entry.relative_path != relative_path + || manifest_entry.content_hash != expected_hash + { + return Err(CliError::Autogen(format!( + "cleanup preview candidate {package_id} does not match local_autogen manifest" + ))); + } + let target = safe_join(&repo_path, &relative_path)?; + if target.exists() { + let current = fs::read_to_string(&target).map_err(|source| { + CliError::Autogen(format!( + "failed to read existing autogen file '{}': {source}", + target.display() + )) + })?; + if content_hash(¤t) != expected_hash { + preserved.push(preserve_autogen_file_in_local( + data_dir, + db, + &package_id, + ¤t, + )?); + } + fs::remove_file(&target).map_err(|source| { + CliError::Autogen(format!( + "failed to delete autogen package '{}': {source}", + target.display() + )) + })?; + } + manifest + .packages + .retain(|entry| entry.package_id != package_id); + db.delete_generated_tracked_package(&package_id, &local_autogen_id)?; + deleted.push(json!({ + "package_id": package_id.to_string(), + "output_relative_path": relative_path, + })); + } + write_autogen_manifest(&repo_path, &manifest)?; + Ok(json!({ + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "deleted_count": deleted.len(), + "deleted": deleted, + "preserved_to_local": preserved, + })) +} + +fn accepted_preview_candidates<'a>( + preview: &'a Value, + acceptance: &AutogenAcceptance, +) -> Result, CliError> { + let candidates = preview + .get("candidates") + .and_then(Value::as_array) + .ok_or_else(|| CliError::Autogen("autogen preview missing candidates".to_owned()))?; + match acceptance { + AutogenAcceptance::AcceptAll => Ok(candidates.iter().collect()), + AutogenAcceptance::Accept(ids) => { + let accepted: BTreeSet = ids.iter().map(ToString::to_string).collect(); + Ok(candidates + .iter() + .filter(|candidate| { + candidate + .get("package_id") + .and_then(Value::as_str) + .is_some_and(|id| accepted.contains(id)) + }) + .collect()) + } + } +} + +fn preview_package_id(candidate: &Value) -> Result { + candidate + .get("package_id") + .and_then(Value::as_str) + .ok_or_else(|| CliError::Autogen("preview candidate missing package_id".to_owned()))? + .parse() + .map_err(|source: getter_core::PackageIdError| CliError::Autogen(source.to_string())) +} + +fn preview_relative_path(candidate: &Value) -> Result { + candidate + .get("output_relative_path") + .and_then(Value::as_str) + .map(PathBuf::from) + .ok_or_else(|| { + CliError::Autogen("preview candidate missing output_relative_path".to_owned()) + }) +} + +fn ensure_local_autogen_repository(data_dir: &Path, db: &MainDb) -> Result { + let repo_path = local_autogen_repo_path(data_dir); + ensure_repository_layout(&repo_path, &local_autogen_repo_toml())?; + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), + priority: RepositoryPriority::LOCAL_AUTOGEN, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + )?; + Ok(repo_path) +} + +fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> Result { + let local_id = RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id"); + if let Ok(existing) = find_repository(db, &local_id) { + let repo_path = repo_path(&existing)?; + ensure_repository_layout(&repo_path, &local_repo_toml())?; + return Ok(repo_path); + } + + let repo_path = data_dir.join("repositories").join(LOCAL_REPOSITORY_ID); + ensure_repository_layout(&repo_path, &local_repo_toml())?; + db.upsert_repository( + &RepositoryMetadata { + id: local_id, + name: LOCAL_REPOSITORY_NAME.to_owned(), + priority: RepositoryPriority::LOCAL, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + )?; + Ok(repo_path) +} + +fn ensure_repository_layout(repo_path: &Path, repo_toml: &str) -> Result<(), CliError> { + fs::create_dir_all(repo_path.join("packages")).map_err(|source| { + CliError::Autogen(format!( + "failed to create repository packages dir '{}': {source}", + repo_path.display() + )) + })?; + fs::create_dir_all(repo_path.join("lib")).map_err(|source| { + CliError::Autogen(format!( + "failed to create repository lib dir '{}': {source}", + repo_path.display() + )) + })?; + fs::create_dir_all(repo_path.join("templates")).map_err(|source| { + CliError::Autogen(format!( + "failed to create repository templates dir '{}': {source}", + repo_path.display() + )) + })?; + let repo_toml_path = repo_path.join("repo.toml"); + if !repo_toml_path.exists() { + fs::write(&repo_toml_path, repo_toml).map_err(|source| { + CliError::Autogen(format!( + "failed to write repo.toml '{}': {source}", + repo_toml_path.display() + )) + })?; + } + Ok(()) +} + +fn preserve_autogen_file_in_local( + data_dir: &Path, + db: &MainDb, + package_id: &PackageId, + content: &str, +) -> Result { + let local_repo = ensure_local_repository(data_dir, db)?; + let primary_relative = getter_core::autogen::package_relative_path(package_id); + let primary_target = safe_join(&local_repo, &primary_relative)?; + let relative_path = if primary_target.exists() { + let backup = PathBuf::from("autogen-preserved") + .join(package_id.kind().as_str()) + .join(format!( + "{}.{}.lua", + package_id.name(), + content_hash(content).replace(':', "-") + )); + safe_join(&local_repo, &backup)?; + backup + } else { + primary_relative + }; + let target = safe_join(&local_repo, &relative_path)?; + if let Some(parent) = target.parent() { + fs::create_dir_all(parent).map_err(|source| { + CliError::Autogen(format!( + "failed to create local preservation directory '{}': {source}", + parent.display() + )) + })?; + } + fs::write(&target, content).map_err(|source| { + CliError::Autogen(format!( + "failed to preserve modified autogen file '{}': {source}", + target.display() + )) + })?; + Ok(json!({ + "package_id": package_id.to_string(), + "repository_id": LOCAL_REPOSITORY_ID, + "relative_path": relative_path, + })) +} + +fn read_autogen_manifest(repo_path: &Path) -> Result, CliError> { + let path = repo_path.join(AUTOGEN_MANIFEST_FILE); + if !path.exists() { + return Ok(None); + } + let bytes = fs::read(&path).map_err(|source| { + CliError::Autogen(format!( + "failed to read autogen manifest '{}': {source}", + path.display() + )) + })?; + serde_json::from_slice(&bytes) + .map(Some) + .map_err(|source| CliError::Autogen(format!("failed to parse autogen manifest: {source}"))) +} + +fn write_autogen_manifest(repo_path: &Path, manifest: &AutogenManifest) -> Result<(), CliError> { + fs::create_dir_all(repo_path).map_err(|source| { + CliError::Autogen(format!( + "failed to create autogen repository '{}': {source}", + repo_path.display() + )) + })?; + let path = repo_path.join(AUTOGEN_MANIFEST_FILE); + let bytes = serde_json::to_vec_pretty(manifest) + .map_err(|source| CliError::Autogen(format!("failed to serialize manifest: {source}")))?; + fs::write(&path, bytes).map_err(|source| { + CliError::Autogen(format!( + "failed to write autogen manifest '{}': {source}", + path.display() + )) + }) +} + +fn empty_autogen_manifest() -> AutogenManifest { + AutogenManifest { + version: getter_core::autogen::AUTOGEN_MANIFEST_VERSION, + repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + packages: Vec::new(), + } +} + +fn upsert_manifest_entry(manifest: &mut AutogenManifest, entry: AutogenManifestEntry) { + manifest + .packages + .retain(|existing| existing.package_id != entry.package_id); + manifest.packages.push(entry); + manifest + .packages + .sort_by_key(|existing| existing.package_id.to_string()); +} + +fn safe_join(root: &Path, relative: &Path) -> Result { + if relative.is_absolute() + || relative.components().any(|component| { + matches!( + component, + Component::ParentDir | Component::RootDir | Component::Prefix(_) + ) + }) + { + return Err(CliError::Autogen(format!( + "unsafe relative path '{}'", + relative.display() + ))); + } + Ok(root.join(relative)) +} + +fn local_autogen_repo_path(data_dir: &Path) -> PathBuf { + data_dir + .join("repositories") + .join(LOCAL_AUTOGEN_REPOSITORY_ID) +} + fn repo_path(repo: &StoredRepository) -> Result { repo.path .as_ref() @@ -984,7 +1684,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1037,6 +1737,10 @@ impl CliCommand { Self::RepoValidate { .. } => "repo validate", Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", + Self::AutogenInstalledPreview { .. } => "autogen installed preview", + Self::AutogenInstalledApply { .. } => "autogen installed apply", + Self::AutogenCleanupPreview { .. } => "autogen cleanup preview", + Self::AutogenCleanupApply { .. } => "autogen cleanup apply", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", Self::LegacyImportRoomDb { .. } => "legacy import-room-db", Self::LegacyReportList => "legacy report-list", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 5d07d58..dfe31e2 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -12,6 +12,8 @@ struct CliWorld { data_dir: Option, bundle: Option, legacy_db: Option, + inventory: Option, + autogen_preview: Option, fixture_repo_id: Option, fixture_repo_path: Option, fixture_package_id: Option, @@ -109,6 +111,82 @@ fn legacy_room_v17_database_with_only_unsupported_app_rows(world: &mut CliWorld) world.legacy_db = Some(legacy_db); } +#[given(expr = "an installed inventory with Android app {string} labeled {string}")] +fn installed_inventory_with_android_app(world: &mut CliWorld, package_name: String, label: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let inventory = temp.path().join("installed-inventory.json"); + fs::write( + &inventory, + serde_json::to_vec_pretty(&serde_json::json!({ + "format": "upgradeall-installed-inventory", + "version": 1, + "items": [ + { + "kind": "android", + "package_name": package_name, + "label": label, + "version_name": "1.0.0", + "version_code": 1, + } + ] + })) + .expect("inventory serializes"), + ) + .expect("write inventory"); + world.inventory = Some(inventory); +} + +#[given("an empty installed inventory")] +fn empty_installed_inventory(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let inventory = temp.path().join("installed-inventory.json"); + fs::write( + &inventory, + serde_json::to_vec_pretty(&serde_json::json!({ + "format": "upgradeall-installed-inventory", + "version": 1, + "items": [] + })) + .expect("inventory serializes"), + ) + .expect("write inventory"); + world.inventory = Some(inventory); +} + +#[given(expr = "a tampered autogen cleanup preview for package {string}")] +fn tampered_autogen_cleanup_preview_for_package(world: &mut CliWorld, package_id: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let preview = temp.path().join("tampered-cleanup-preview.json"); + fs::write( + &preview, + serde_json::to_vec_pretty(&serde_json::json!({ + "operation": "cleanup.preview", + "target_repo_id": "local_autogen", + "target_repo_path": local_autogen_repo_path(world), + "summary": { + "candidate_count": 1, + "skipped_count": 0, + "write_count": 0, + "delete_count": 1, + }, + "candidates": [ + { + "package_id": package_id, + "action": "delete", + "output_relative_path": package_relative_path(&package_id), + "content_hash": "fnv1a64:0000000000000000", + "reason": "not_in_installed_inventory", + } + ], + "skipped": [], + "diagnostics": [], + })) + .expect("preview serializes"), + ) + .expect("write tampered cleanup preview"); + world.autogen_preview = Some(preview); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -329,6 +407,113 @@ fn run_getter_legacy_report_list(world: &mut CliWorld) { world.json = None; } +#[when("I run getter autogen installed preview for that inventory")] +fn run_getter_autogen_installed_preview(world: &mut CliWorld) { + let inventory = world.inventory.as_ref().expect("inventory exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "installed".to_owned(), + "preview".to_owned(), + "--inventory".to_owned(), + inventory.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter autogen installed apply for that preview with accept-all")] +fn run_getter_autogen_installed_apply_accept_all(world: &mut CliWorld) { + let preview = world + .autogen_preview + .as_ref() + .expect("autogen preview exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "installed".to_owned(), + "apply".to_owned(), + "--preview".to_owned(), + preview.to_string_lossy().to_string(), + "--accept-all".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter autogen cleanup preview for that inventory")] +fn run_getter_autogen_cleanup_preview(world: &mut CliWorld) { + let inventory = world.inventory.as_ref().expect("inventory exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "cleanup".to_owned(), + "preview".to_owned(), + "--inventory".to_owned(), + inventory.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter autogen cleanup apply for that preview with accept-all")] +fn run_getter_autogen_cleanup_apply_accept_all(world: &mut CliWorld) { + let preview = world + .autogen_preview + .as_ref() + .expect("autogen preview exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "cleanup".to_owned(), + "apply".to_owned(), + "--preview".to_owned(), + preview.to_string_lossy().to_string(), + "--accept-all".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter repo validate for local_autogen")] +fn run_getter_repo_validate_for_local_autogen(world: &mut CliWorld) { + let repo_path = local_autogen_repo_path(world); + let output = run_getter( + world, + [ + "repo".to_owned(), + "validate".to_owned(), + repo_path.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when(expr = "I run getter package eval for package {string} from local_autogen")] +fn run_getter_package_eval_from_local_autogen(world: &mut CliWorld, package_id: String) { + let output = run_getter( + world, + [ + "package".to_owned(), + "eval".to_owned(), + package_id, + "--repo".to_owned(), + "local_autogen".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[then("the command succeeds")] fn command_succeeds(world: &mut CliWorld) { let output = world.output.as_ref().expect("command output exists"); @@ -352,6 +537,17 @@ fn command_fails_with_migration_error(world: &mut CliWorld) { world.json = Some(json); } +#[then("the command fails with an autogen error")] +fn command_fails_with_autogen_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(1)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["command"], "autogen cleanup apply"); + assert_eq!(json["error"]["code"], "autogen.error"); + world.json = Some(json); +} + #[then(expr = "the command fails with direct DB migration error {string}")] fn command_fails_with_direct_db_migration_error(world: &mut CliWorld, code: String) { let output = world.output.as_ref().expect("command output exists"); @@ -515,6 +711,164 @@ fn output_contains_named_package(world: &mut CliWorld, package_id: String, packa assert_eq!(json["data"]["package"]["name"], package_name); } +#[then(expr = "the autogen preview contains candidate {string}")] +fn autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen installed preview"); + assert_eq!(json["data"]["operation"], "installed.preview"); + let candidates = json["data"]["candidates"] + .as_array() + .expect("candidates array"); + assert!( + candidates + .iter() + .any(|candidate| candidate["package_id"].as_str() == Some(package_id.as_str())), + "preview should contain {package_id}: {candidates:?}" + ); +} + +#[then("the local_autogen repository has not been written")] +fn local_autogen_repository_has_not_been_written(world: &mut CliWorld) { + assert!( + !local_autogen_repo_path(world).exists(), + "preview must not create local_autogen" + ); +} + +#[then("I save the autogen preview to a file")] +fn save_autogen_preview_to_file(world: &mut CliWorld) { + let data = current_json(world) + .get("data") + .expect("autogen command data exists") + .clone(); + let temp = world.temp.as_ref().expect("tempdir exists"); + let preview = temp.path().join("autogen-preview.json"); + fs::write( + &preview, + serde_json::to_vec_pretty(&data).expect("preview serializes"), + ) + .expect("write autogen preview"); + world.autogen_preview = Some(preview); +} + +#[then(expr = "the local_autogen repository contains generated package {string}")] +fn local_autogen_repository_contains_generated_package(world: &mut CliWorld, package_id: String) { + let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); + assert!(path.is_file(), "generated package should exist: {path:?}"); + let content = fs::read_to_string(&path).expect("generated package readable"); + assert!(content.contains("@generated by UpgradeAll getter local_autogen")); +} + +#[then(expr = "the app list contains autogen tracked package {string}")] +fn app_list_contains_autogen_tracked_package(world: &mut CliWorld, package_id: String) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); + let json = parse_stdout(&output); + let apps = json["data"]["apps"].as_array().expect("apps array"); + let app = apps + .iter() + .find(|app| app["id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); + assert_eq!(app["repository_id"], "local_autogen"); + assert_eq!(app["package_resolution"], "generate_local_package"); +} + +#[then(expr = "the autogen preview skips package {string} because repository {string} covers it")] +fn autogen_preview_skips_package_because_repository_covers_it( + world: &mut CliWorld, + package_id: String, + repository_id: String, +) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen installed preview"); + let skipped = json["data"]["skipped"].as_array().expect("skipped array"); + let skip = skipped + .iter() + .find(|skip| skip["package_id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| panic!("skipped should contain {package_id}: {skipped:?}")); + assert_eq!(skip["reason"], "covered_by_higher_priority_repo"); + assert_eq!(skip["covering_repo_id"], repository_id); +} + +#[then(expr = "the autogen cleanup preview contains delete candidate {string}")] +fn autogen_cleanup_preview_contains_delete_candidate(world: &mut CliWorld, package_id: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen cleanup preview"); + assert_eq!(json["data"]["operation"], "cleanup.preview"); + let candidates = json["data"]["candidates"] + .as_array() + .expect("candidates array"); + let candidate = candidates + .iter() + .find(|candidate| candidate["package_id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| { + panic!("cleanup candidates should contain {package_id}: {candidates:?}") + }); + assert_eq!(candidate["action"], "delete"); +} + +#[then(expr = "the local_autogen repository does not contain generated package {string}")] +fn local_autogen_repository_does_not_contain_generated_package( + world: &mut CliWorld, + package_id: String, +) { + let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); + assert!( + !path.exists(), + "generated package should be deleted: {path:?}" + ); +} + +#[then(expr = "the app list does not contain package {string}")] +fn app_list_does_not_contain_package(world: &mut CliWorld, package_id: String) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); + let json = parse_stdout(&output); + let apps = json["data"]["apps"].as_array().expect("apps array"); + assert!( + apps.iter() + .all(|app| app["id"].as_str() != Some(package_id.as_str())), + "app list must not contain {package_id}: {apps:?}" + ); +} + +#[then(expr = "I replace generated autogen package {string} with user-edited content")] +fn replace_generated_autogen_package_with_user_edited_content( + world: &mut CliWorld, + package_id: String, +) { + let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); + assert!(path.is_file(), "generated package should exist before edit"); + fs::write( + &path, + format!( + "-- user edited\nreturn package_def {{ id = {id:?}, name = \"Edited Autogen\" }}\n", + id = package_id + ), + ) + .expect("overwrite generated package with user edit"); +} + +#[then(expr = "local repository contains preserved package {string}")] +fn local_repository_contains_preserved_package(world: &mut CliWorld, package_id: String) { + let local_path = world + .data_dir + .as_ref() + .expect("data dir exists") + .join("repositories") + .join("local") + .join(package_relative_path(&package_id)); + assert!( + local_path.is_file(), + "modified autogen file should be preserved into local: {local_path:?}" + ); + let content = fs::read_to_string(local_path).expect("preserved local file readable"); + assert!(content.contains("Edited Autogen")); +} + #[then("no partially usable imported state is created")] fn no_partially_usable_imported_state(world: &mut CliWorld) { let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); @@ -685,6 +1039,24 @@ fn current_json(world: &mut CliWorld) -> &Value { .get_or_insert_with(|| parse_stdout(world.output.as_ref().expect("command output exists"))) } +fn local_autogen_repo_path(world: &CliWorld) -> PathBuf { + world + .data_dir + .as_ref() + .expect("data dir exists") + .join("repositories") + .join("local_autogen") +} + +fn package_relative_path(package_id: &str) -> PathBuf { + let (kind, name) = package_id + .split_once('/') + .expect("test package id has kind/name"); + PathBuf::from("packages") + .join(kind) + .join(format!("{name}.lua")) +} + fn create_fixture_lua_repository( world: &mut CliWorld, repo_id: String, diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature new file mode 100644 index 0000000..fc3c16a --- /dev/null +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -0,0 +1,92 @@ +@getter-cli @autogen +Feature: Installed app autogen + Scenario: User previews installed app fallback generation without writing files + Given an initialized getter data directory + And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" + When I run getter autogen installed preview for that inventory + Then the command succeeds + And the output is valid JSON + And the autogen preview contains candidate "android/com.example.autogen" + And the local_autogen repository has not been written + + Scenario: User applies installed app autogen and evaluates the generated fallback package + Given an initialized getter data directory + And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" + When I run getter autogen installed preview for that inventory + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen installed apply for that preview with accept-all + Then the command succeeds + And the local_autogen repository contains generated package "android/com.example.autogen" + And the app list contains autogen tracked package "android/com.example.autogen" + When I run getter repo validate for local_autogen + Then the output reports a valid repository without network + When I run getter package eval for package "android/com.example.autogen" from local_autogen + Then the output contains package "android/com.example.autogen" named "Example Autogen" + + Scenario: Higher-priority repositories suppress installed app autogen candidates + Given an initialized getter data directory + And a fixture Lua repository "official" with package "android/com.example.autogen" named "Official Example" + And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" + When I run getter repo add for that repository with priority 0 + Then the command succeeds + When I run getter autogen installed preview for that inventory + Then the command succeeds + And the autogen preview skips package "android/com.example.autogen" because repository "official" covers it + + Scenario: Cleanup deletes only accepted generated packages missing from installed inventory + Given an initialized getter data directory + And an installed inventory with Android app "com.example.old" labeled "Old App" + When I run getter autogen installed preview for that inventory + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen installed apply for that preview with accept-all + Then the command succeeds + Given an empty installed inventory + When I run getter autogen cleanup preview for that inventory + Then the command succeeds + And the autogen cleanup preview contains delete candidate "android/com.example.old" + And I save the autogen preview to a file + When I run getter autogen cleanup apply for that preview with accept-all + Then the command succeeds + And the local_autogen repository does not contain generated package "android/com.example.old" + And the app list does not contain package "android/com.example.old" + + Scenario: Cleanup rejects tampered previews for non-autogen tracked packages + Given an initialized getter data directory + And a syntactically valid legacy export bundle with an Android app + And a tampered autogen cleanup preview for package "android/org.fdroid.fdroid" + When I run getter legacy import-room-bundle for that bundle + Then the command succeeds + When I run getter autogen cleanup apply for that preview with accept-all + Then the command fails with an autogen error + And the app list contains imported package "android/org.fdroid.fdroid" + + Scenario: Applying autogen preserves existing tracked user state + Given an initialized getter data directory + And a syntactically valid legacy export bundle with an Android app + When I run getter legacy import-room-bundle for that bundle + Then the command succeeds + Given an installed inventory with Android app "org.fdroid.fdroid" labeled "F-Droid" + When I run getter autogen installed preview for that inventory + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen installed apply for that preview with accept-all + Then the command succeeds + And the app list contains imported package "android/org.fdroid.fdroid" + + Scenario: Applying over modified autogen preserves the user-edited file into local + Given an initialized getter data directory + And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" + When I run getter autogen installed preview for that inventory + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen installed apply for that preview with accept-all + Then the command succeeds + And I replace generated autogen package "android/com.example.autogen" with user-edited content + When I run getter autogen installed preview for that inventory + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen installed apply for that preview with accept-all + Then the command succeeds + And local repository contains preserved package "android/com.example.autogen" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs new file mode 100644 index 0000000..265c6be --- /dev/null +++ b/crates/getter-core/src/autogen.rs @@ -0,0 +1,536 @@ +//! Installed-inventory autogen planning for generated fallback repositories. +//! +//! This module is pure domain logic: platform adapters provide installed +//! inventory facts, while getter decides package ids, generated Lua shape, +//! deterministic paths, and preview DTOs. + +use crate::repository::REPO_API_VERSION_V1; +use crate::{InstalledTarget, PackageId, PackageKind, RepositoryId, RepositoryPriority}; +use serde::{Deserialize, Serialize}; +use std::collections::{BTreeSet, HashMap}; +use std::path::PathBuf; + +pub const INSTALLED_INVENTORY_FORMAT: &str = "upgradeall-installed-inventory"; +pub const INSTALLED_INVENTORY_VERSION: u32 = 1; +pub const AUTOGEN_MANIFEST_VERSION: u32 = 1; +pub const LOCAL_REPOSITORY_ID: &str = "local"; +pub const LOCAL_REPOSITORY_NAME: &str = "Local"; +pub const LOCAL_AUTOGEN_REPOSITORY_ID: &str = "local_autogen"; +pub const LOCAL_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Local Autogen"; +pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter local_autogen"; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct InstalledInventory { + pub format: String, + pub version: u32, + #[serde(default)] + pub items: Vec, +} + +impl InstalledInventory { + pub fn new(items: Vec) -> Self { + Self { + format: INSTALLED_INVENTORY_FORMAT.to_owned(), + version: INSTALLED_INVENTORY_VERSION, + items, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum InstalledInventoryItem { + #[serde(alias = "android")] + AndroidPackage { + package_name: String, + #[serde(default)] + label: Option, + #[serde(default)] + version_name: Option, + #[serde(default)] + version_code: Option, + }, + #[serde(alias = "magisk")] + MagiskModule { + module_id: String, + #[serde(default)] + name: Option, + #[serde(default)] + version_name: Option, + #[serde(default)] + version_code: Option, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenPlan { + pub repository_id: RepositoryId, + pub repository_name: String, + pub repository_priority: RepositoryPriority, + pub candidates: Vec, + pub skipped: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenCandidate { + pub package_id: PackageId, + pub name: String, + pub installed: InstalledTarget, + pub relative_path: PathBuf, + pub content: String, + pub content_hash: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenSkip { + pub package_id: PackageId, + pub reason: AutogenSkipReason, + #[serde(default)] + pub repository_id: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum AutogenSkipReason { + DuplicateInventoryItem, + CoveredByHigherPriorityRepository, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenManifest { + pub version: u32, + pub repository_id: RepositoryId, + pub packages: Vec, +} + +impl AutogenManifest { + pub fn from_candidates(candidates: &[AutogenCandidate]) -> Self { + Self { + version: AUTOGEN_MANIFEST_VERSION, + repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID) + .expect("local_autogen is a valid repository id"), + packages: candidates + .iter() + .map(|candidate| AutogenManifestEntry { + package_id: candidate.package_id.clone(), + relative_path: candidate.relative_path.clone(), + content_hash: candidate.content_hash.clone(), + }) + .collect(), + } + } + + pub fn package(&self, package_id: &PackageId) -> Option<&AutogenManifestEntry> { + self.packages + .iter() + .find(|entry| &entry.package_id == package_id) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenManifestEntry { + pub package_id: PackageId, + pub relative_path: PathBuf, + pub content_hash: String, +} + +#[derive(Debug, thiserror::Error)] +pub enum AutogenError { + #[error("unsupported installed inventory format '{0}'")] + UnsupportedInventoryFormat(String), + #[error("unsupported installed inventory version {found}; expected {expected}")] + UnsupportedInventoryVersion { found: u32, expected: u32 }, + #[error("installed inventory item has an empty identity")] + EmptyIdentity, + #[error("failed to construct package id from installed inventory: {0}")] + PackageId(#[from] crate::PackageIdError), +} + +/// Build a deterministic local_autogen preview from installed inventory. +/// +/// `covered_packages` contains package ids already supplied by repositories with +/// priority higher than `local_autogen`. Those packages are skipped so generated +/// fallback files do not compete with user-authored `local` or upstream package +/// files. +pub fn plan_local_autogen( + inventory: &InstalledInventory, + covered_packages: &HashMap, +) -> Result { + validate_installed_inventory(inventory)?; + let mut seen = BTreeSet::new(); + let mut candidates = Vec::new(); + let mut skipped = Vec::new(); + + for item in &inventory.items { + let candidate = candidate_from_inventory_item(item)?; + if !seen.insert(candidate.package_id.to_string()) { + skipped.push(AutogenSkip { + package_id: candidate.package_id, + reason: AutogenSkipReason::DuplicateInventoryItem, + repository_id: None, + }); + continue; + } + if let Some(repository_id) = covered_packages.get(&candidate.package_id) { + skipped.push(AutogenSkip { + package_id: candidate.package_id, + reason: AutogenSkipReason::CoveredByHigherPriorityRepository, + repository_id: Some(repository_id.clone()), + }); + continue; + } + candidates.push(candidate); + } + + candidates.sort_by_key(|candidate| candidate.package_id.to_string()); + skipped.sort_by_key(|skip| skip.package_id.to_string()); + + Ok(AutogenPlan { + repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID) + .expect("local_autogen is a valid repository id"), + repository_name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), + repository_priority: RepositoryPriority::LOCAL_AUTOGEN, + candidates, + skipped, + }) +} + +pub fn validate_installed_inventory(inventory: &InstalledInventory) -> Result<(), AutogenError> { + if inventory.format != INSTALLED_INVENTORY_FORMAT { + return Err(AutogenError::UnsupportedInventoryFormat( + inventory.format.clone(), + )); + } + if inventory.version != INSTALLED_INVENTORY_VERSION { + return Err(AutogenError::UnsupportedInventoryVersion { + found: inventory.version, + expected: INSTALLED_INVENTORY_VERSION, + }); + } + Ok(()) +} + +pub fn local_autogen_repo_toml() -> String { + repo_toml( + LOCAL_AUTOGEN_REPOSITORY_ID, + LOCAL_AUTOGEN_REPOSITORY_NAME, + RepositoryPriority::LOCAL_AUTOGEN, + ) +} + +pub fn local_repo_toml() -> String { + repo_toml( + LOCAL_REPOSITORY_ID, + LOCAL_REPOSITORY_NAME, + RepositoryPriority::LOCAL, + ) +} + +fn repo_toml(id: &str, name: &str, priority: RepositoryPriority) -> String { + format!( + "id = \"{id}\"\nname = \"{name}\"\npriority = {}\napi_version = \"{REPO_API_VERSION_V1}\"\n", + priority.value() + ) +} + +pub fn generated_file_is_managed(content: &str) -> bool { + content + .lines() + .next() + .is_some_and(|line| line.contains(GENERATED_MARKER)) +} + +pub fn content_hash(content: &str) -> String { + format!("fnv1a64:{:016x}", fnv1a64(content.as_bytes())) +} + +fn candidate_from_inventory_item( + item: &InstalledInventoryItem, +) -> Result { + match item { + InstalledInventoryItem::AndroidPackage { + package_name, + label, + .. + } => { + let package_name = non_empty(package_name)?; + let package_id = PackageId::new(PackageKind::Android, package_name)?; + let name = label + .as_deref() + .filter(|label| !label.trim().is_empty()) + .unwrap_or(package_name) + .to_owned(); + let installed = InstalledTarget::AndroidPackage { + package_name: package_name.to_owned(), + }; + let relative_path = package_relative_path(&package_id); + let content = render_package_lua(&package_id, &name, &installed); + let content_hash = content_hash(&content); + Ok(AutogenCandidate { + package_id, + name, + installed, + relative_path, + content, + content_hash, + }) + } + InstalledInventoryItem::MagiskModule { + module_id, name, .. + } => { + let module_id = non_empty(module_id)?; + let package_id = PackageId::new(PackageKind::Magisk, module_id)?; + let name = name + .as_deref() + .filter(|name| !name.trim().is_empty()) + .unwrap_or(module_id) + .to_owned(); + let installed = InstalledTarget::MagiskModule { + module_id: module_id.to_owned(), + }; + let relative_path = package_relative_path(&package_id); + let content = render_package_lua(&package_id, &name, &installed); + let content_hash = content_hash(&content); + Ok(AutogenCandidate { + package_id, + name, + installed, + relative_path, + content, + content_hash, + }) + } + } +} + +fn non_empty(value: &str) -> Result<&str, AutogenError> { + let value = value.trim(); + if value.is_empty() { + Err(AutogenError::EmptyIdentity) + } else { + Ok(value) + } +} + +pub fn package_relative_path(package_id: &PackageId) -> PathBuf { + let mut path = PathBuf::from("packages"); + path.push(package_id.kind().as_str()); + path.push(format!("{}.lua", package_id.name())); + path +} + +fn render_package_lua(package_id: &PackageId, name: &str, installed: &InstalledTarget) -> String { + let installed_lua = match installed { + InstalledTarget::AndroidPackage { package_name } => format!( + "{{ kind = \"android_package\", package_name = {} }}", + lua_string(package_name) + ), + InstalledTarget::MagiskModule { module_id } => format!( + "{{ kind = \"magisk_module\", module_id = {} }}", + lua_string(module_id) + ), + InstalledTarget::Generic { id } => { + format!("{{ kind = \"generic\", id = {} }}", lua_string(id)) + } + }; + + format!( + "-- {GENERATED_MARKER}\nreturn package_def {{\n id = {},\n name = {},\n installed = {{\n {installed_lua},\n }},\n}}\n", + lua_string(&package_id.to_string()), + lua_string(name) + ) +} + +fn lua_string(value: &str) -> String { + let mut escaped = String::from("\""); + for ch in value.chars() { + match ch { + '\\' => escaped.push_str("\\\\"), + '"' => escaped.push_str("\\\""), + '\n' => escaped.push_str("\\n"), + '\r' => escaped.push_str("\\r"), + '\t' => escaped.push_str("\\t"), + other => escaped.push(other), + } + } + escaped.push('"'); + escaped +} + +fn fnv1a64(bytes: &[u8]) -> u64 { + let mut hash = 0xcbf29ce484222325u64; + for byte in bytes { + hash ^= u64::from(*byte); + hash = hash.wrapping_mul(0x100000001b3); + } + hash +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lua::evaluate_package_file; + use crate::repository::RepositoryLayout; + use std::fs; + + #[test] + fn preview_generates_deterministic_android_package_lua() { + let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid".to_owned()), + version_name: Some("1.20.0".to_owned()), + version_code: Some(1020000), + }]); + + let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + + assert_eq!(plan.repository_id.as_str(), LOCAL_AUTOGEN_REPOSITORY_ID); + assert_eq!(plan.repository_priority, RepositoryPriority::LOCAL_AUTOGEN); + assert_eq!(plan.candidates.len(), 1); + let candidate = &plan.candidates[0]; + assert_eq!( + candidate.package_id.to_string(), + "android/org.fdroid.fdroid" + ); + assert_eq!( + candidate.relative_path, + PathBuf::from("packages/android/org.fdroid.fdroid.lua") + ); + assert!(candidate.content.contains(GENERATED_MARKER)); + assert!(candidate.content.contains("package_def")); + assert_eq!(candidate.content_hash, content_hash(&candidate.content)); + } + + #[test] + fn preview_skips_packages_covered_by_higher_priority_repositories() { + let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + let mut covered = HashMap::new(); + covered.insert(package_id, RepositoryId::new("official").unwrap()); + let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid".to_owned()), + version_name: None, + version_code: None, + }]); + + let plan = plan_local_autogen(&inventory, &covered).unwrap(); + + assert!(plan.candidates.is_empty()); + assert_eq!(plan.skipped.len(), 1); + assert_eq!( + plan.skipped[0].reason, + AutogenSkipReason::CoveredByHigherPriorityRepository + ); + assert_eq!( + plan.skipped[0] + .repository_id + .as_ref() + .map(RepositoryId::as_str), + Some("official") + ); + } + + #[test] + fn preview_deduplicates_inventory_items() { + let inventory = InstalledInventory::new(vec![ + InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid".to_owned()), + version_name: None, + version_code: None, + }, + InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid Duplicate".to_owned()), + version_name: None, + version_code: None, + }, + ]); + + let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + + assert_eq!(plan.candidates.len(), 1); + assert_eq!(plan.skipped.len(), 1); + assert_eq!( + plan.skipped[0].reason, + AutogenSkipReason::DuplicateInventoryItem + ); + } + + #[test] + fn rejects_unsupported_inventory_contracts() { + let inventory = InstalledInventory { + format: "other".to_owned(), + version: INSTALLED_INVENTORY_VERSION, + items: Vec::new(), + }; + + assert!(matches!( + plan_local_autogen(&inventory, &HashMap::new()), + Err(AutogenError::UnsupportedInventoryFormat(_)) + )); + } + + #[test] + fn manifest_records_generated_package_hashes() { + let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid".to_owned()), + version_name: None, + version_code: None, + }]); + let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + + let manifest = AutogenManifest::from_candidates(&plan.candidates); + + assert_eq!(manifest.version, AUTOGEN_MANIFEST_VERSION); + assert_eq!(manifest.repository_id.as_str(), LOCAL_AUTOGEN_REPOSITORY_ID); + assert_eq!(manifest.packages.len(), 1); + assert_eq!( + manifest.packages[0].content_hash, + content_hash(&plan.candidates[0].content) + ); + } + + #[test] + fn generated_lua_evaluates_through_package_boundary() { + let temp = tempfile::tempdir().unwrap(); + let repo = temp.path(); + fs::write(repo.join("repo.toml"), local_autogen_repo_toml()).unwrap(); + fs::create_dir_all(repo.join("packages/android")).unwrap(); + fs::create_dir(repo.join("lib")).unwrap(); + fs::create_dir(repo.join("templates")).unwrap(); + let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid \"client\"".to_owned()), + version_name: None, + version_code: None, + }]); + let candidate = plan_local_autogen(&inventory, &HashMap::new()) + .unwrap() + .candidates + .remove(0); + let package_path = repo.join(&candidate.relative_path); + fs::write(&package_path, candidate.content).unwrap(); + + let layout = RepositoryLayout::load(repo).unwrap(); + let package = evaluate_package_file(&layout, &package_path).unwrap(); + + assert_eq!(package.id.to_string(), "android/org.fdroid.fdroid"); + assert_eq!(package.name, "F-Droid \"client\""); + assert_eq!( + package.installed, + vec![InstalledTarget::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + }] + ); + } + + #[test] + fn generated_file_marker_identifies_managed_files_only() { + assert!(generated_file_is_managed(&format!( + "-- {GENERATED_MARKER}\nreturn {{}}" + ))); + assert!(!generated_file_is_managed("return {}")); + } +} diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 32c110b..642e36f 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -9,6 +9,7 @@ use std::cmp::Ordering; use std::fmt; use std::str::FromStr; +pub mod autogen; pub mod diagnostics; pub mod lua; pub mod repository; diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index a6f0e56..f1285c2 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -184,6 +184,43 @@ ON CONFLICT(id) DO UPDATE SET Ok(()) } + pub fn upsert_generated_tracked_package_preserving_user_state( + &self, + package_id: &PackageId, + repository_id: &RepositoryId, + ) -> Result<(), StorageError> { + self.conn.execute( + r#" +INSERT INTO tracked_packages( + package_id, + enabled, + favorite, + ignored_version, + repository_id, + package_resolution +) +VALUES (?1, 1, 0, NULL, ?2, ?3) +ON CONFLICT(package_id) DO UPDATE SET + repository_id = CASE + WHEN tracked_packages.package_resolution = 'missing_package_definition' + THEN excluded.repository_id + ELSE tracked_packages.repository_id + END, + package_resolution = CASE + WHEN tracked_packages.package_resolution = 'missing_package_definition' + THEN excluded.package_resolution + ELSE tracked_packages.package_resolution + END +"#, + params![ + package_id.to_string(), + repository_id.as_str(), + StoredPackageResolution::GenerateLocalPackage.as_str(), + ], + )?; + Ok(()) + } + pub fn import_tracked_packages_with_migration_record( &self, packages: &[TrackedPackageUpsert], @@ -208,6 +245,27 @@ ON CONFLICT(id) DO UPDATE SET Ok(()) } + pub fn delete_generated_tracked_package( + &self, + package_id: &PackageId, + repository_id: &RepositoryId, + ) -> Result { + let deleted = self.conn.execute( + r#" +DELETE FROM tracked_packages +WHERE package_id = ?1 + AND repository_id = ?2 + AND package_resolution = ?3 +"#, + params![ + package_id.to_string(), + repository_id.as_str(), + StoredPackageResolution::GenerateLocalPackage.as_str(), + ], + )?; + Ok(deleted != 0) + } + pub fn tracked_packages(&self) -> Result, StorageError> { let mut stmt = self.conn.prepare( r#" @@ -535,6 +593,119 @@ mod tests { ); } + #[test] + fn generated_tracking_preserves_existing_user_state_on_conflict() { + let db = MainDb::open_in_memory().unwrap(); + let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + let local_autogen = RepositoryId::new("local_autogen").unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: package_id.clone(), + enabled: false, + favorite: true, + ignored_version: Some("9.9.9".to_owned()), + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + .unwrap(); + + let packages = db.tracked_packages().unwrap(); + assert_eq!(packages.len(), 1); + assert!(!packages[0].enabled); + assert!(packages[0].favorite); + assert_eq!(packages[0].ignored_version.as_deref(), Some("9.9.9")); + assert_eq!(packages[0].repository_id, None); + assert_eq!( + packages[0].package_resolution, + StoredPackageResolution::OfficialRepositoryPackage + ); + } + + #[test] + fn generated_tracking_fills_unresolved_tracking_metadata() { + let db = MainDb::open_in_memory().unwrap(); + let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + let local_autogen = insert_local_autogen_repo(&db); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: package_id.clone(), + enabled: false, + favorite: true, + ignored_version: Some("9.9.9".to_owned()), + repository_id: None, + package_resolution: StoredPackageResolution::MissingPackageDefinition, + }) + .unwrap(); + + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + .unwrap(); + + let packages = db.tracked_packages().unwrap(); + assert_eq!(packages.len(), 1); + assert!(!packages[0].enabled); + assert!(packages[0].favorite); + assert_eq!(packages[0].ignored_version.as_deref(), Some("9.9.9")); + assert_eq!(packages[0].repository_id.as_ref(), Some(&local_autogen)); + assert_eq!( + packages[0].package_resolution, + StoredPackageResolution::GenerateLocalPackage + ); + } + + #[test] + fn generated_tracking_delete_is_guarded_by_repo_and_resolution() { + let db = MainDb::open_in_memory().unwrap(); + let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + let local_autogen = insert_local_autogen_repo(&db); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: package_id.clone(), + enabled: true, + favorite: true, + ignored_version: Some("9.9.9".to_owned()), + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + assert!(!db + .delete_generated_tracked_package(&package_id, &local_autogen) + .unwrap()); + assert_eq!(db.tracked_packages().unwrap().len(), 1); + + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: package_id.clone(), + enabled: true, + favorite: true, + ignored_version: Some("9.9.9".to_owned()), + repository_id: None, + package_resolution: StoredPackageResolution::MissingPackageDefinition, + }) + .unwrap(); + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + .unwrap(); + assert!(db + .delete_generated_tracked_package(&package_id, &local_autogen) + .unwrap()); + assert!(db.tracked_packages().unwrap().is_empty()); + } + + fn insert_local_autogen_repo(db: &MainDb) -> RepositoryId { + let local_autogen = RepositoryId::new("local_autogen").unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: local_autogen.clone(), + name: "Local Autogen".to_owned(), + priority: RepositoryPriority::LOCAL_AUTOGEN, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + None, + None, + ) + .unwrap(); + local_autogen + } + #[test] fn main_db_records_migration_completion() { let db = MainDb::open_in_memory().unwrap(); From 196cd8c2c449a17ef4c58f4fa401a2f6d93efe7b Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 23 Jun 2026 00:56:49 +0800 Subject: [PATCH 15/72] feat(cli): add offline update check --- crates/getter-cli/src/lib.rs | 43 ++- crates/getter-cli/tests/bdd_cli.rs | 202 +++++++++++ .../tests/features/cli/update_check.feature | 68 ++++ crates/getter-core/src/update.rs | 319 +++++++++++++++++- 4 files changed, 628 insertions(+), 4 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/update_check.feature diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 72d92fc..0732d41 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -13,6 +13,7 @@ use getter_core::autogen::{ use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; +use getter_core::update::{run_offline_update_check, OfflineUpdateCheckFixture}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_storage::legacy_room::{ map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, @@ -63,6 +64,9 @@ pub enum CliCommand { repo_id: Option, }, StorageValidate, + UpdateCheck { + fixture: PathBuf, + }, AutogenInstalledPreview { inventory: PathBuf, }, @@ -117,6 +121,8 @@ pub enum CliError { Repository(String), #[error("package evaluation error: {0}")] PackageEval(String), + #[error("update check error: {0}")] + Update(String), #[error("autogen error: {0}")] Autogen(String), #[error("Legacy Room export bundle is invalid")] @@ -134,7 +140,7 @@ impl CliError { match self { Self::Usage(_) => ExitCode::Usage, Self::Storage(_) => ExitCode::Storage, - Self::Repository(_) | Self::PackageEval(_) | Self::Autogen(_) => { + Self::Repository(_) | Self::PackageEval(_) | Self::Update(_) | Self::Autogen(_) => { ExitCode::GenericFailure } Self::InvalidLegacyBundle { .. } @@ -150,6 +156,7 @@ impl CliError { Self::Storage(_) => "storage.error", Self::Repository(_) => "repository.error", Self::PackageEval(_) => "package.eval_error", + Self::Update(_) => "update.check_error", Self::Autogen(_) => "autogen.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", @@ -164,6 +171,7 @@ impl CliError { Self::Storage(_) => "Getter storage operation failed", Self::Repository(_) => "Getter repository operation failed", Self::PackageEval(_) => "Getter package evaluation failed", + Self::Update(_) => "Getter update check failed", Self::Autogen(_) => "Getter autogen operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { @@ -180,6 +188,7 @@ impl CliError { | Self::Storage(detail) | Self::Repository(detail) | Self::PackageEval(detail) + | Self::Update(detail) | Self::Autogen(detail) => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } @@ -198,6 +207,7 @@ impl CliError { | Self::Storage(_) | Self::Repository(_) | Self::PackageEval(_) + | Self::Update(_) | Self::Autogen(_) => None, } } @@ -326,6 +336,13 @@ where [domain, command] if domain == "storage" && command == "validate" => { CliCommand::StorageValidate } + [domain, command, flag, fixture] + if domain == "update" && command == "check" && flag == "--fixture" => + { + CliCommand::UpdateCheck { + fixture: PathBuf::from(fixture), + } + } [domain, subject, action, flag, inventory] if domain == "autogen" && subject == "installed" @@ -471,6 +488,16 @@ fn execute(invocation: CliInvocation) -> Result { "cache_db": cache_db_path(&invocation.data_dir), })) } + CliCommand::UpdateCheck { fixture } => { + open_initialized_storage(&invocation.data_dir)?; + let fixture = read_update_check_fixture(&fixture)?; + serde_json::to_value(run_offline_update_check(fixture).map_err(|source| { + CliError::Update(format!("offline update check failed: {source}")) + })?) + .map_err(|source| { + CliError::Update(format!("failed to serialize update check: {source}")) + }) + } CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; @@ -754,6 +781,17 @@ fn read_installed_inventory(path: &Path) -> Result .map_err(|source| CliError::Autogen(format!("failed to parse inventory JSON: {source}"))) } +fn read_update_check_fixture(path: &Path) -> Result { + let bytes = fs::read(path).map_err(|source| { + CliError::Update(format!("failed to read update check fixture: {source}")) + })?; + serde_json::from_slice(&bytes).map_err(|source| { + CliError::Update(format!( + "failed to parse update check fixture JSON: {source}" + )) + }) +} + fn build_local_autogen_plan( db: &MainDb, inventory: &InstalledInventory, @@ -1684,7 +1722,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|update check --fixture |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1737,6 +1775,7 @@ impl CliCommand { Self::RepoValidate { .. } => "repo validate", Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", + Self::UpdateCheck { .. } => "update check", Self::AutogenInstalledPreview { .. } => "autogen installed preview", Self::AutogenInstalledApply { .. } => "autogen installed apply", Self::AutogenCleanupPreview { .. } => "autogen cleanup preview", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index dfe31e2..fdef3e5 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -14,6 +14,7 @@ struct CliWorld { legacy_db: Option, inventory: Option, autogen_preview: Option, + update_fixture: Option, fixture_repo_id: Option, fixture_repo_path: Option, fixture_package_id: Option, @@ -187,6 +188,79 @@ fn tampered_autogen_cleanup_preview_for_package(world: &mut CliWorld, package_id world.autogen_preview = Some(preview); } +#[given( + expr = "an offline update fixture for package {string} installed version {string} with candidate versions {string}" +)] +fn offline_update_fixture_with_installed_version( + world: &mut CliWorld, + package_id: String, + installed_version: String, + versions: String, +) { + write_offline_update_fixture(world, package_id, Some(installed_version), None, versions); +} + +#[given( + expr = "an offline update fixture for package {string} installed version {string} ignored version {string} with candidate versions {string}" +)] +fn offline_update_fixture_with_ignored_version( + world: &mut CliWorld, + package_id: String, + installed_version: String, + ignored_version: String, + versions: String, +) { + write_offline_update_fixture( + world, + package_id, + Some(installed_version), + Some(ignored_version), + versions, + ); +} + +#[given( + expr = "an offline update fixture for package {string} without installed version with candidate versions {string}" +)] +fn offline_update_fixture_without_installed_version( + world: &mut CliWorld, + package_id: String, + versions: String, +) { + write_offline_update_fixture(world, package_id, None, None, versions); +} + +#[given( + expr = "an offline update fixture for package {string} installed version {string} with artifactless candidate version {string}" +)] +fn offline_update_fixture_with_artifactless_candidate( + world: &mut CliWorld, + package_id: String, + installed_version: String, + candidate_version: String, +) { + write_offline_update_fixture_with_candidates( + world, + package_id, + Some(installed_version), + None, + vec![serde_json::json!({ + "version": candidate_version, + "channel": "stable", + "source": "offline-fixture", + "artifacts": [], + })], + ); +} + +#[given("a malformed offline update fixture")] +fn malformed_offline_update_fixture(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let fixture = temp.path().join("malformed-update-fixture.json"); + fs::write(&fixture, "not-json").expect("write malformed fixture"); + world.update_fixture = Some(fixture); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -407,6 +481,25 @@ fn run_getter_legacy_report_list(world: &mut CliWorld) { world.json = None; } +#[when("I run getter update check for that fixture")] +fn run_getter_update_check(world: &mut CliWorld) { + let fixture = world + .update_fixture + .as_ref() + .expect("update fixture exists"); + let output = run_getter( + world, + [ + "update".to_owned(), + "check".to_owned(), + "--fixture".to_owned(), + fixture.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen installed preview for that inventory")] fn run_getter_autogen_installed_preview(world: &mut CliWorld) { let inventory = world.inventory.as_ref().expect("inventory exists"); @@ -548,6 +641,17 @@ fn command_fails_with_autogen_error(world: &mut CliWorld) { world.json = Some(json); } +#[then("the command fails with an update check error")] +fn command_fails_with_update_check_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(1)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["command"], "update check"); + assert_eq!(json["error"]["code"], "update.check_error"); + world.json = Some(json); +} + #[then(expr = "the command fails with direct DB migration error {string}")] fn command_fails_with_direct_db_migration_error(world: &mut CliWorld, code: String) { let output = world.output.as_ref().expect("command output exists"); @@ -711,6 +815,44 @@ fn output_contains_named_package(world: &mut CliWorld, package_id: String, packa assert_eq!(json["data"]["package"]["name"], package_name); } +#[then(expr = "the update check status is {string}")] +fn update_check_status_is(world: &mut CliWorld, status: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "update check"); + assert_eq!(json["data"]["network_required"], false); + assert_eq!(json["data"]["status"], status); +} + +#[then(expr = "the selected update version is {string}")] +fn selected_update_version_is(world: &mut CliWorld, version: String) { + let json = current_json(world); + assert_eq!(json["data"]["selected"]["candidate"]["version"], version); +} + +#[then(expr = "the update check actions download file {string} and request installer {string}")] +fn update_check_actions_download_and_install( + world: &mut CliWorld, + file_name: String, + installer: String, +) { + let json = current_json(world); + let actions = json["data"]["actions"].as_array().expect("actions array"); + assert_eq!(actions.len(), 2); + assert_eq!(actions[0]["type"], "download"); + assert_eq!(actions[0]["file_name"], file_name); + assert_eq!(actions[1]["type"], "install"); + assert_eq!(actions[1]["installer"], installer); + assert_eq!(actions[1]["file"], file_name); +} + +#[then("the update check has no selected update")] +fn update_check_has_no_selected_update(world: &mut CliWorld) { + let json = current_json(world); + assert!(json["data"]["selected"].is_null()); + assert_eq!(json["data"]["actions"], Value::Array(Vec::new())); +} + #[then(expr = "the autogen preview contains candidate {string}")] fn autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { let json = current_json(world); @@ -1099,6 +1241,66 @@ return package_def {{ world.fixture_package_id = Some(package_id); } +fn write_offline_update_fixture( + world: &mut CliWorld, + package_id: String, + installed_version: Option, + ignored_version: Option, + versions: String, +) { + let candidates: Vec = versions + .split(',') + .map(str::trim) + .filter(|version| !version.is_empty()) + .map(|version| { + serde_json::json!({ + "version": version, + "channel": "stable", + "source": "offline-fixture", + "artifacts": [ + { + "name": "APK", + "url": format!("https://example.invalid/{version}.apk"), + "file_name": "app.apk", + } + ], + }) + }) + .collect(); + write_offline_update_fixture_with_candidates( + world, + package_id, + installed_version, + ignored_version, + candidates, + ); +} + +fn write_offline_update_fixture_with_candidates( + world: &mut CliWorld, + package_id: String, + installed_version: Option, + ignored_version: Option, + candidates: Vec, +) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let fixture = temp.path().join("offline-update-fixture.json"); + fs::write( + &fixture, + serde_json::to_vec_pretty(&serde_json::json!({ + "format": "getter-offline-update-check", + "version": 1, + "package_id": package_id, + "installed_version": installed_version, + "ignored_version": ignored_version, + "candidates": candidates, + })) + .expect("fixture serializes"), + ) + .expect("write offline update fixture"); + world.update_fixture = Some(fixture); +} + fn create_fixture_legacy_room_db(path: &PathBuf, version: u32, include_app_table: bool) { let conn = Connection::open(path).expect("create legacy Room fixture"); conn.pragma_update(None, "user_version", version) diff --git a/crates/getter-cli/tests/features/cli/update_check.feature b/crates/getter-cli/tests/features/cli/update_check.feature new file mode 100644 index 0000000..e5d4828 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/update_check.feature @@ -0,0 +1,68 @@ +@getter-cli @update +Feature: Offline update check + Scenario: User checks an offline fixture with an available update + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" with candidate versions "1.0.1,1.2.0" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "update_available" + And the selected update version is "1.2.0" + And the update check actions download file "app.apk" and request installer "android_package" + + Scenario: User checks an offline fixture that is already up to date + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "2.0.0" with candidate versions "1.9.0,2.0.0" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "up_to_date" + And the update check has no selected update + + Scenario: User checks an offline fixture where the latest update is ignored + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" ignored version "1.2.0" with candidate versions "1.1.0,1.2.0" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "update_available" + And the selected update version is "1.1.0" + + Scenario: User checks an offline fixture where the only update is ignored + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" ignored version "1.2.0" with candidate versions "1.2.0" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "ignored" + And the update check has no selected update + + Scenario: User checks an offline fixture without an installed version + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" without installed version with candidate versions "1.0.0-beta,1.0.0" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "update_available" + And the selected update version is "1.0.0" + + Scenario: User checks an offline fixture with no candidates + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" with candidate versions "" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "no_candidates" + And the update check has no selected update + + Scenario: User receives structured errors when the selected update has no artifact + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" with artifactless candidate version "1.2.0" + When I run getter update check for that fixture + Then the command fails with an update check error + + Scenario: User receives structured errors for malformed offline update fixtures + Given an initialized getter data directory + And a malformed offline update fixture + When I run getter update check for that fixture + Then the command fails with an update check error diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs index ab3256c..72acfa6 100644 --- a/crates/getter-core/src/update.rs +++ b/crates/getter-core/src/update.rs @@ -1,13 +1,198 @@ //! Update selection helpers owned by getter core. -use crate::{PackageId, SelectedUpdate, UpdateArtifact, UpdateCandidate}; +use crate::{ + PackageId, PackageKind, SelectedUpdate, UpdateAction, UpdateArtifact, UpdateCandidate, +}; +use serde::{Deserialize, Serialize}; use std::cmp::Ordering; +pub const OFFLINE_UPDATE_CHECK_FORMAT: &str = "getter-offline-update-check"; +pub const OFFLINE_UPDATE_CHECK_VERSION: u32 = 1; + /// User state that affects update selection. -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct UpdateSelectionPolicy { /// Candidate version the user chose to ignore/mark as skipped. + #[serde(default)] + pub ignored_version: Option, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct OfflineUpdateCheckFixture { + pub format: String, + pub version: u32, + pub package_id: PackageId, + #[serde(default)] + pub installed_version: Option, + #[serde(default)] pub ignored_version: Option, + #[serde(default)] + pub candidates: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct OfflineUpdateCheckResult { + pub network_required: bool, + pub package_id: PackageId, + #[serde(default)] + pub installed_version: Option, + pub policy: UpdateSelectionPolicy, + pub status: UpdateCheckStatus, + #[serde(default)] + pub selected: Option, + pub actions: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum UpdateCheckStatus { + UpdateAvailable, + UpToDate, + NoCandidates, + Ignored, +} + +#[derive(Debug, thiserror::Error)] +pub enum OfflineUpdateCheckError { + #[error("unsupported update check fixture format '{0}'")] + UnsupportedFormat(String), + #[error("unsupported update check fixture version {found}; expected {expected}")] + UnsupportedVersion { found: u32, expected: u32 }, + #[error("selected update candidate '{version}' has no actionable artifacts")] + MissingSelectedArtifact { version: String }, +} + +pub fn run_offline_update_check( + fixture: OfflineUpdateCheckFixture, +) -> Result { + if fixture.format != OFFLINE_UPDATE_CHECK_FORMAT { + return Err(OfflineUpdateCheckError::UnsupportedFormat(fixture.format)); + } + if fixture.version != OFFLINE_UPDATE_CHECK_VERSION { + return Err(OfflineUpdateCheckError::UnsupportedVersion { + found: fixture.version, + expected: OFFLINE_UPDATE_CHECK_VERSION, + }); + } + + let policy = UpdateSelectionPolicy { + ignored_version: fixture.ignored_version, + }; + check_updates_offline( + fixture.package_id, + fixture.installed_version, + fixture.candidates, + policy, + ) +} + +pub fn check_updates_offline( + package_id: PackageId, + installed_version: Option, + candidates: Vec, + policy: UpdateSelectionPolicy, +) -> Result { + let selected = select_update( + package_id.clone(), + installed_version.as_deref(), + &candidates, + &policy, + ); + let status = update_check_status( + selected.as_ref(), + installed_version.as_deref(), + &candidates, + &policy, + ); + let actions = match selected.as_ref() { + Some(selected) => { + if selected.artifact.is_none() { + return Err(OfflineUpdateCheckError::MissingSelectedArtifact { + version: selected.candidate.version.clone(), + }); + } + update_actions_for_selected(selected) + } + None => Vec::new(), + }; + + Ok(OfflineUpdateCheckResult { + network_required: false, + package_id, + installed_version, + policy, + status, + selected, + actions, + }) +} + +pub fn update_actions_for_selected(selected: &SelectedUpdate) -> Vec { + let Some(artifact) = selected.artifact.as_ref() else { + return Vec::new(); + }; + let file_name = artifact_file_name(artifact); + vec![ + UpdateAction::Download { + url: artifact.url.clone(), + file_name: file_name.clone(), + }, + UpdateAction::Install { + installer: installer_for_package_kind(selected.package_id.kind()).to_owned(), + file: file_name, + }, + ] +} + +fn artifact_file_name(artifact: &UpdateArtifact) -> String { + artifact + .file_name + .as_ref() + .filter(|value| !value.trim().is_empty()) + .cloned() + .unwrap_or_else(|| artifact.name.clone()) +} + +fn installer_for_package_kind(kind: PackageKind) -> &'static str { + match kind { + PackageKind::Android => "android_package", + PackageKind::Magisk => "magisk_module", + PackageKind::Generic => "generic_file", + } +} + +fn update_check_status( + selected: Option<&SelectedUpdate>, + installed_version: Option<&str>, + candidates: &[UpdateCandidate], + policy: &UpdateSelectionPolicy, +) -> UpdateCheckStatus { + if candidates.is_empty() { + UpdateCheckStatus::NoCandidates + } else if selected.is_some() { + UpdateCheckStatus::UpdateAvailable + } else if ignored_candidate_would_have_been_update(installed_version, candidates, policy) { + UpdateCheckStatus::Ignored + } else { + UpdateCheckStatus::UpToDate + } +} + +fn ignored_candidate_would_have_been_update( + installed_version: Option<&str>, + candidates: &[UpdateCandidate], + policy: &UpdateSelectionPolicy, +) -> bool { + let Some(ignored) = policy.ignored_version.as_deref() else { + return false; + }; + + candidates.iter().any(|candidate| { + compare_versions(&candidate.version, ignored) == Ordering::Equal + && installed_version.is_none_or(|installed| { + compare_versions(&candidate.version, installed) == Ordering::Greater + }) + }) } /// Compare human-facing version strings using a deterministic token ordering. @@ -257,6 +442,136 @@ mod tests { ); } + #[test] + fn offline_update_check_reports_update_available_with_actions() { + let result = run_offline_update_check(OfflineUpdateCheckFixture { + format: OFFLINE_UPDATE_CHECK_FORMAT.to_owned(), + version: OFFLINE_UPDATE_CHECK_VERSION, + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + installed_version: Some("1.0.0".to_owned()), + ignored_version: None, + candidates: vec![candidate("1.0.1"), candidate("1.2.0")], + }) + .unwrap(); + + assert_eq!(result.status, UpdateCheckStatus::UpdateAvailable); + assert_eq!(result.selected.as_ref().unwrap().candidate.version, "1.2.0"); + assert_eq!( + result.actions, + vec![ + UpdateAction::Download { + url: "https://example.invalid/1.2.0.apk".to_owned(), + file_name: "app.apk".to_owned() + }, + UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned() + } + ] + ); + } + + #[test] + fn offline_update_check_reports_up_to_date() { + let result = check_updates_offline( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("2.0.0".to_owned()), + vec![candidate("1.9.0"), candidate("2.0.0")], + UpdateSelectionPolicy::default(), + ) + .unwrap(); + + assert_eq!(result.status, UpdateCheckStatus::UpToDate); + assert!(result.selected.is_none()); + assert!(result.actions.is_empty()); + } + + #[test] + fn offline_update_check_reports_ignored_when_only_update_is_ignored() { + let result = check_updates_offline( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0".to_owned()), + vec![candidate("1.2.0")], + UpdateSelectionPolicy { + ignored_version: Some("1.2.0".to_owned()), + }, + ) + .unwrap(); + + assert_eq!(result.status, UpdateCheckStatus::Ignored); + assert!(result.selected.is_none()); + } + + #[test] + fn offline_update_check_falls_back_below_ignored_latest() { + let result = check_updates_offline( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0".to_owned()), + vec![candidate("1.1.0"), candidate("1.2.0")], + UpdateSelectionPolicy { + ignored_version: Some("1.2.0".to_owned()), + }, + ) + .unwrap(); + + assert_eq!(result.status, UpdateCheckStatus::UpdateAvailable); + assert_eq!(result.selected.as_ref().unwrap().candidate.version, "1.1.0"); + } + + #[test] + fn offline_update_check_reports_no_candidates() { + let result = check_updates_offline( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0".to_owned()), + Vec::new(), + UpdateSelectionPolicy::default(), + ) + .unwrap(); + + assert_eq!(result.status, UpdateCheckStatus::NoCandidates); + assert!(result.selected.is_none()); + assert!(result.actions.is_empty()); + } + + #[test] + fn offline_update_check_rejects_selected_candidate_without_artifacts() { + let error = check_updates_offline( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0".to_owned()), + vec![UpdateCandidate { + version: "1.2.0".to_owned(), + channel: None, + source: None, + artifacts: Vec::new(), + }], + UpdateSelectionPolicy::default(), + ) + .unwrap_err(); + + assert!(matches!( + error, + OfflineUpdateCheckError::MissingSelectedArtifact { .. } + )); + } + + #[test] + fn offline_update_check_rejects_wrong_contract() { + let error = run_offline_update_check(OfflineUpdateCheckFixture { + format: "wrong".to_owned(), + version: OFFLINE_UPDATE_CHECK_VERSION, + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + installed_version: None, + ignored_version: None, + candidates: Vec::new(), + }) + .unwrap_err(); + + assert!(matches!( + error, + OfflineUpdateCheckError::UnsupportedFormat(_) + )); + } + fn candidate(version: &str) -> UpdateCandidate { UpdateCandidate { version: version.to_owned(), From cb4cb655f3aefa69200ac23cfcc1e770d678d664 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 23 Jun 2026 10:53:49 +0800 Subject: [PATCH 16/72] feat(cli): add offline task lifecycle --- Cargo.lock | 3 + crates/getter-cli/Cargo.toml | 1 + crates/getter-cli/src/lib.rs | 169 ++++- crates/getter-cli/tests/bdd_cli.rs | 259 +++++++ .../tests/features/cli/task_lifecycle.feature | 89 +++ crates/getter-core/src/lib.rs | 1 + crates/getter-core/src/task.rs | 266 +++++++ crates/getter-downloader/Cargo.toml | 2 + crates/getter-downloader/src/lib.rs | 166 ++++- crates/getter-storage/src/lib.rs | 674 +++++++++++++++++- 10 files changed, 1626 insertions(+), 4 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/task_lifecycle.feature create mode 100644 crates/getter-core/src/task.rs diff --git a/Cargo.lock b/Cargo.lock index 6119744..a23b19e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -520,6 +520,7 @@ version = "0.1.0" dependencies = [ "cucumber", "getter-core", + "getter-downloader", "getter-storage", "rusqlite", "serde", @@ -546,6 +547,8 @@ name = "getter-downloader" version = "0.1.0" dependencies = [ "getter-core", + "getter-storage", + "thiserror 1.0.69", ] [[package]] diff --git a/crates/getter-cli/Cargo.toml b/crates/getter-cli/Cargo.toml index ef7d105..248cb01 100644 --- a/crates/getter-cli/Cargo.toml +++ b/crates/getter-cli/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core" } +getter-downloader = { path = "../getter-downloader" } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 0732d41..08d63d0 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -13,8 +13,15 @@ use getter_core::autogen::{ use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; +use getter_core::task::{ + DownloadTaskRequest, InstallHandoffStatus, TaskEventPage, DOWNLOAD_REQUEST_FORMAT, + DOWNLOAD_REQUEST_VERSION, +}; use getter_core::update::{run_offline_update_check, OfflineUpdateCheckFixture}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; +use getter_downloader::{ + cancel_download_task, record_install_result, run_fake_download_task, submit_fake_download_task, +}; use getter_storage::legacy_room::{ map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, LegacyRoomDbImport, LegacyRoomImportWarning, @@ -67,6 +74,24 @@ pub enum CliCommand { UpdateCheck { fixture: PathBuf, }, + TaskSubmit { + request: PathBuf, + }, + TaskRun { + task_id: String, + }, + TaskList, + TaskCancel { + task_id: String, + }, + TaskEvents { + after: u64, + limit: usize, + }, + TaskInstallResult { + handoff_id: String, + status: InstallHandoffStatus, + }, AutogenInstalledPreview { inventory: PathBuf, }, @@ -103,6 +128,7 @@ pub enum ExitCode { Usage = 2, Storage = 10, Migration = 20, + Download = 40, } impl ExitCode { @@ -123,6 +149,8 @@ pub enum CliError { PackageEval(String), #[error("update check error: {0}")] Update(String), + #[error("download task error: {0}")] + Download(String), #[error("autogen error: {0}")] Autogen(String), #[error("Legacy Room export bundle is invalid")] @@ -143,6 +171,7 @@ impl CliError { Self::Repository(_) | Self::PackageEval(_) | Self::Update(_) | Self::Autogen(_) => { ExitCode::GenericFailure } + Self::Download(_) => ExitCode::Download, Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } | Self::InvalidLegacyDb { .. } @@ -157,6 +186,7 @@ impl CliError { Self::Repository(_) => "repository.error", Self::PackageEval(_) => "package.eval_error", Self::Update(_) => "update.check_error", + Self::Download(_) => "download.task_error", Self::Autogen(_) => "autogen.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", @@ -172,6 +202,7 @@ impl CliError { Self::Repository(_) => "Getter repository operation failed", Self::PackageEval(_) => "Getter package evaluation failed", Self::Update(_) => "Getter update check failed", + Self::Download(_) => "Getter download task operation failed", Self::Autogen(_) => "Getter autogen operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { @@ -189,6 +220,7 @@ impl CliError { | Self::Repository(detail) | Self::PackageEval(detail) | Self::Update(detail) + | Self::Download(detail) | Self::Autogen(detail) => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } @@ -208,6 +240,7 @@ impl CliError { | Self::Repository(_) | Self::PackageEval(_) | Self::Update(_) + | Self::Download(_) | Self::Autogen(_) => None, } } @@ -343,6 +376,41 @@ where fixture: PathBuf::from(fixture), } } + [domain, command, flag, request] + if domain == "task" && command == "submit" && flag == "--request" => + { + CliCommand::TaskSubmit { + request: PathBuf::from(request), + } + } + [domain, command, task_id] if domain == "task" && command == "run" => CliCommand::TaskRun { + task_id: task_id.clone(), + }, + [domain, command] if domain == "task" && command == "list" => CliCommand::TaskList, + [domain, command, task_id] if domain == "task" && command == "cancel" => { + CliCommand::TaskCancel { + task_id: task_id.clone(), + } + } + [domain, command, after_flag, after, limit_flag, limit] + if domain == "task" + && command == "events" + && after_flag == "--after" + && limit_flag == "--limit" => + { + CliCommand::TaskEvents { + after: parse_u64(after, "--after")?, + limit: parse_positive_usize(limit, "--limit")?, + } + } + [domain, command, handoff_id, status_flag, status] + if domain == "task" && command == "install-result" && status_flag == "--status" => + { + CliCommand::TaskInstallResult { + handoff_id: handoff_id.clone(), + status: parse_install_handoff_status(status)?, + } + } [domain, subject, action, flag, inventory] if domain == "autogen" && subject == "installed" @@ -498,6 +566,46 @@ fn execute(invocation: CliInvocation) -> Result { CliError::Update(format!("failed to serialize update check: {source}")) }) } + CliCommand::TaskSubmit { request } => { + let db = open_main_db(&invocation.data_dir)?; + let request = read_download_task_request(&request)?; + serde_json::to_value(submit_fake_download_task(&db, request).map_err(|source| { + CliError::Download(format!("offline task submit failed: {source}")) + })?) + .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) + } + CliCommand::TaskRun { task_id } => { + let db = open_main_db(&invocation.data_dir)?; + serde_json::to_value(run_fake_download_task(&db, &task_id).map_err(|source| { + CliError::Download(format!("offline task run failed: {source}")) + })?) + .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) + } + CliCommand::TaskList => { + let db = open_main_db(&invocation.data_dir)?; + Ok(json!({ "tasks": db.download_tasks()? })) + } + CliCommand::TaskCancel { task_id } => { + let db = open_main_db(&invocation.data_dir)?; + serde_json::to_value(cancel_download_task(&db, &task_id).map_err(|source| { + CliError::Download(format!("offline task cancel failed: {source}")) + })?) + .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) + } + CliCommand::TaskEvents { after, limit } => { + let db = open_main_db(&invocation.data_dir)?; + let events: TaskEventPage = db.task_events_after(after, limit)?; + serde_json::to_value(events).map_err(|source| { + CliError::Download(format!("failed to serialize task events: {source}")) + }) + } + CliCommand::TaskInstallResult { handoff_id, status } => { + let db = open_main_db(&invocation.data_dir)?; + serde_json::to_value(record_install_result(&db, &handoff_id, status).map_err( + |source| CliError::Download(format!("offline install result failed: {source}")), + )?) + .map_err(|source| CliError::Download(format!("failed to serialize handoff: {source}"))) + } CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; @@ -686,6 +794,38 @@ fn parse_package_id(value: &str) -> Result { .map_err(|source: getter_core::PackageIdError| CliError::Usage(source.to_string())) } +fn parse_u64(value: &str, flag: &str) -> Result { + value + .parse() + .map_err(|source| CliError::Usage(format!("invalid {flag} value '{value}': {source}"))) +} + +fn parse_usize(value: &str, flag: &str) -> Result { + value + .parse() + .map_err(|source| CliError::Usage(format!("invalid {flag} value '{value}': {source}"))) +} + +fn parse_positive_usize(value: &str, flag: &str) -> Result { + let parsed = parse_usize(value, flag)?; + if parsed == 0 { + return Err(CliError::Usage(format!("{flag} must be greater than zero"))); + } + Ok(parsed) +} + +fn parse_install_handoff_status(value: &str) -> Result { + match value { + "accepted" => Ok(InstallHandoffStatus::Accepted), + "succeeded" => Ok(InstallHandoffStatus::Succeeded), + "failed" => Ok(InstallHandoffStatus::Failed), + "canceled" => Ok(InstallHandoffStatus::Canceled), + _ => Err(CliError::Usage(format!( + "invalid install result status '{value}'; expected accepted, succeeded, failed, or canceled" + ))), + } +} + fn parse_priority(value: &str) -> Result { value .parse::() @@ -792,6 +932,27 @@ fn read_update_check_fixture(path: &Path) -> Result Result { + let bytes = fs::read(path) + .map_err(|source| CliError::Download(format!("failed to read task request: {source}")))?; + let request: DownloadTaskRequest = serde_json::from_slice(&bytes).map_err(|source| { + CliError::Download(format!("failed to parse task request JSON: {source}")) + })?; + if request.format != DOWNLOAD_REQUEST_FORMAT { + return Err(CliError::Download(format!( + "unsupported download request format '{}'", + request.format + ))); + } + if request.version != DOWNLOAD_REQUEST_VERSION { + return Err(CliError::Download(format!( + "unsupported download request version {}; expected {}", + request.version, DOWNLOAD_REQUEST_VERSION + ))); + } + Ok(request) +} + fn build_local_autogen_plan( db: &MainDb, inventory: &InstalledInventory, @@ -1722,7 +1883,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|update check --fixture |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|update check --fixture |task submit --request |task run |task list|task cancel |task events --after --limit |task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1776,6 +1937,12 @@ impl CliCommand { Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", Self::UpdateCheck { .. } => "update check", + Self::TaskSubmit { .. } => "task submit", + Self::TaskRun { .. } => "task run", + Self::TaskList => "task list", + Self::TaskCancel { .. } => "task cancel", + Self::TaskEvents { .. } => "task events", + Self::TaskInstallResult { .. } => "task install-result", Self::AutogenInstalledPreview { .. } => "autogen installed preview", Self::AutogenInstalledApply { .. } => "autogen installed apply", Self::AutogenCleanupPreview { .. } => "autogen cleanup preview", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index fdef3e5..d0230ea 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -15,6 +15,10 @@ struct CliWorld { inventory: Option, autogen_preview: Option, update_fixture: Option, + task_request: Option, + remembered_task_id: Option, + remembered_event_cursor: Option, + remembered_handoff_id: Option, fixture_repo_id: Option, fixture_repo_path: Option, fixture_package_id: Option, @@ -261,6 +265,44 @@ fn malformed_offline_update_fixture(world: &mut CliWorld) { world.update_fixture = Some(fixture); } +#[given(expr = "an offline download request for package {string}")] +fn offline_download_request_for_package(world: &mut CliWorld, package_id: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let request = temp.path().join("download-request.json"); + fs::write( + &request, + serde_json::to_vec_pretty(&serde_json::json!({ + "format": "getter-download-request", + "version": 1, + "package_id": package_id, + "executor": "fake", + "actions": [ + { + "type": "download", + "url": "https://example.invalid/app.apk", + "file_name": "app.apk" + }, + { + "type": "install", + "installer": "android_package", + "file": "app.apk" + } + ] + })) + .expect("request serializes"), + ) + .expect("write download request"); + world.task_request = Some(request); +} + +#[given("a malformed offline download request")] +fn malformed_offline_download_request(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let request = temp.path().join("malformed-download-request.json"); + fs::write(&request, "not-json").expect("write malformed request"); + world.task_request = Some(request); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -500,6 +542,99 @@ fn run_getter_update_check(world: &mut CliWorld) { world.json = None; } +#[when("I run getter task submit for that request")] +fn run_getter_task_submit(world: &mut CliWorld) { + let request = world.task_request.as_ref().expect("task request exists"); + let output = run_getter( + world, + [ + "task".to_owned(), + "submit".to_owned(), + "--request".to_owned(), + request.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter task list")] +fn run_getter_task_list(world: &mut CliWorld) { + let output = run_getter(world, ["task".to_owned(), "list".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter task cancel for the remembered task")] +fn run_getter_task_cancel(world: &mut CliWorld) { + let task_id = world + .remembered_task_id + .as_ref() + .expect("remembered task id exists") + .clone(); + let output = run_getter(world, ["task".to_owned(), "cancel".to_owned(), task_id]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter task run for the remembered task")] +fn run_getter_task_run(world: &mut CliWorld) { + let task_id = world + .remembered_task_id + .as_ref() + .expect("remembered task id exists") + .clone(); + let output = run_getter(world, ["task".to_owned(), "run".to_owned(), task_id]); + world.output = Some(output); + world.json = None; +} + +#[when(expr = "I run getter task events after {int} limit {int}")] +fn run_getter_task_events_after_limit(world: &mut CliWorld, after: u64, limit: u64) { + let output = run_getter( + world, + [ + "task".to_owned(), + "events".to_owned(), + "--after".to_owned(), + after.to_string(), + "--limit".to_owned(), + limit.to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when(expr = "I run getter task events after the remembered cursor limit {int}")] +fn run_getter_task_events_after_remembered_cursor(world: &mut CliWorld, limit: u64) { + let after = world + .remembered_event_cursor + .expect("remembered event cursor exists"); + run_getter_task_events_after_limit(world, after, limit); +} + +#[when(expr = "I run getter task install-result {string} for the remembered handoff")] +fn run_getter_task_install_result(world: &mut CliWorld, status: String) { + let handoff_id = world + .remembered_handoff_id + .as_ref() + .expect("remembered handoff id exists") + .clone(); + let output = run_getter( + world, + [ + "task".to_owned(), + "install-result".to_owned(), + handoff_id, + "--status".to_owned(), + status, + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen installed preview for that inventory")] fn run_getter_autogen_installed_preview(world: &mut CliWorld) { let inventory = world.inventory.as_ref().expect("inventory exists"); @@ -652,6 +787,26 @@ fn command_fails_with_update_check_error(world: &mut CliWorld) { world.json = Some(json); } +#[then("the command fails with a download task error")] +fn command_fails_with_download_task_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(40)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["error"]["code"], "download.task_error"); + world.json = Some(json); +} + +#[then("the command fails with a CLI usage error")] +fn command_fails_with_cli_usage_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(2)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["error"]["code"], "cli.usage"); + world.json = Some(json); +} + #[then(expr = "the command fails with direct DB migration error {string}")] fn command_fails_with_direct_db_migration_error(world: &mut CliWorld, code: String) { let output = world.output.as_ref().expect("command output exists"); @@ -714,6 +869,110 @@ fn output_contains_empty_repository_list(world: &mut CliWorld) { assert_eq!(json["data"]["repositories"], Value::Array(Vec::new())); } +#[then("I remember the submitted task id")] +fn remember_submitted_task_id(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["command"], "task submit"); + let task_id = json["data"]["task"]["id"] + .as_str() + .expect("task id should be a string") + .to_owned(); + world.remembered_task_id = Some(task_id); +} + +#[then(expr = "the task list contains the remembered task with status {string}")] +fn task_list_contains_remembered_task_with_status(world: &mut CliWorld, status: String) { + let task_id = world + .remembered_task_id + .as_ref() + .expect("remembered task id exists") + .clone(); + let json = current_json(world); + assert_eq!(json["command"], "task list"); + let tasks = json["data"]["tasks"].as_array().expect("tasks array"); + let task = tasks + .iter() + .find(|task| task["id"].as_str() == Some(task_id.as_str())) + .unwrap_or_else(|| panic!("task list should contain {task_id}: {tasks:?}")); + assert_eq!(task["status"], status); +} + +#[then(expr = "the task cancel result has status {string} and changed true")] +fn task_cancel_result_changed_true(world: &mut CliWorld, status: String) { + let json = current_json(world); + assert_eq!(json["command"], "task cancel"); + assert_eq!(json["data"]["status"], status); + assert_eq!(json["data"]["changed"], true); +} + +#[then(expr = "the task cancel result has status {string} and changed false")] +fn task_cancel_result_changed_false(world: &mut CliWorld, status: String) { + let json = current_json(world); + assert_eq!(json["command"], "task cancel"); + assert_eq!(json["data"]["status"], status); + assert_eq!(json["data"]["changed"], false); +} + +#[then(expr = "the task run result has status {string} and install handoff {string}")] +fn task_run_result_has_status_and_install_handoff( + world: &mut CliWorld, + status: String, + handoff_status: String, +) { + let json = current_json(world); + assert_eq!(json["command"], "task run"); + assert_eq!(json["data"]["task"]["status"], status); + assert_eq!(json["data"]["install_handoff"]["status"], handoff_status); +} + +#[then(expr = "the task events output contains {int} events and has more events")] +fn task_events_output_contains_events_and_has_more(world: &mut CliWorld, count: usize) { + let json = current_json(world); + assert_eq!(json["command"], "task events"); + assert_eq!(json["data"]["events"].as_array().unwrap().len(), count); + assert_eq!(json["data"]["has_more"], true); +} + +#[then("I remember the next event cursor")] +fn remember_next_event_cursor(world: &mut CliWorld) { + let json = current_json(world); + world.remembered_event_cursor = Some( + json["data"]["next_cursor"] + .as_u64() + .expect("next cursor should be a u64"), + ); +} + +#[then(expr = "the task events output contains event {string}")] +fn task_events_output_contains_event(world: &mut CliWorld, kind: String) { + let json = current_json(world); + let events = json["data"]["events"].as_array().expect("events array"); + assert!( + events + .iter() + .any(|event| event["kind"].as_str() == Some(kind.as_str())), + "events should contain {kind}: {events:?}" + ); +} + +#[then("I remember the install handoff id")] +fn remember_install_handoff_id(world: &mut CliWorld) { + let json = current_json(world); + world.remembered_handoff_id = Some( + json["data"]["install_handoff"]["id"] + .as_str() + .expect("handoff id should be a string") + .to_owned(), + ); +} + +#[then(expr = "the install result output has status {string}")] +fn install_result_output_has_status(world: &mut CliWorld, status: String) { + let json = current_json(world); + assert_eq!(json["command"], "task install-result"); + assert_eq!(json["data"]["handoff"]["status"], status); +} + #[then("the output reports valid storage")] fn output_reports_valid_storage(world: &mut CliWorld) { let json = current_json(world); diff --git a/crates/getter-cli/tests/features/cli/task_lifecycle.feature b/crates/getter-cli/tests/features/cli/task_lifecycle.feature new file mode 100644 index 0000000..6927548 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/task_lifecycle.feature @@ -0,0 +1,89 @@ +@getter-cli @task +Feature: Offline task lifecycle + Scenario: User submits and lists an offline fake download task + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And the output is valid JSON + And I remember the submitted task id + When I run getter task list + Then the command succeeds + And the task list contains the remembered task with status "queued" + + Scenario: User cancels a queued offline task + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And I remember the submitted task id + When I run getter task cancel for the remembered task + Then the command succeeds + And the task cancel result has status "canceled" and changed true + When I run getter task cancel for the remembered task + Then the command succeeds + And the task cancel result has status "canceled" and changed false + + Scenario: User cannot cancel a succeeded offline task + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And I remember the submitted task id + When I run getter task run for the remembered task + Then the command succeeds + And the task run result has status "succeeded" and install handoff "requested" + When I run getter task cancel for the remembered task + Then the command fails with a download task error + + Scenario: User polls offline task events with cursor and limit + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And I remember the submitted task id + When I run getter task run for the remembered task + Then the command succeeds + When I run getter task events after 0 limit 2 + Then the command succeeds + And the task events output contains 2 events and has more events + And I remember the next event cursor + When I run getter task events after the remembered cursor limit 10 + Then the command succeeds + And the task events output contains event "install_handoff_requested" + + Scenario: User records an offline install handoff result + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And I remember the submitted task id + When I run getter task run for the remembered task + Then the command succeeds + And I remember the install handoff id + When I run getter task install-result "succeeded" for the remembered handoff + Then the command succeeds + And the install result output has status "succeeded" + + Scenario: User cannot record getter-created requested state as an install result + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And I remember the submitted task id + When I run getter task run for the remembered task + Then the command succeeds + And I remember the install handoff id + When I run getter task install-result "requested" for the remembered handoff + Then the command fails with a CLI usage error + + Scenario: User cannot poll task events with a zero limit + Given an initialized getter data directory + When I run getter task events after 0 limit 0 + Then the command fails with a CLI usage error + + Scenario: User receives structured errors for malformed task requests + Given an initialized getter data directory + And a malformed offline download request + When I run getter task submit for that request + Then the command fails with a download task error diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 642e36f..aa3bb41 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -13,6 +13,7 @@ pub mod autogen; pub mod diagnostics; pub mod lua; pub mod repository; +pub mod task; pub mod update; /// Error returned when parsing or constructing a [`PackageId`]. diff --git a/crates/getter-core/src/task.rs b/crates/getter-core/src/task.rs new file mode 100644 index 0000000..d69f4f4 --- /dev/null +++ b/crates/getter-core/src/task.rs @@ -0,0 +1,266 @@ +//! Offline task lifecycle DTOs for getter-owned download/install workflows. +//! +//! These types are transport/domain shapes. They describe getter task state, +//! pollable events, and abstract platform install handoffs without performing +//! live network downloads or Android installation. + +use crate::{PackageId, UpdateAction}; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +pub const DOWNLOAD_REQUEST_FORMAT: &str = "getter-download-request"; +pub const DOWNLOAD_REQUEST_VERSION: u32 = 1; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DownloadTaskRequest { + pub format: String, + pub version: u32, + pub package_id: PackageId, + #[serde(default)] + pub actions: Vec, + pub executor: TaskExecutor, +} + +impl DownloadTaskRequest { + pub fn download_action(&self) -> Option<&UpdateAction> { + self.actions + .iter() + .find(|action| matches!(action, UpdateAction::Download { .. })) + } + + pub fn install_action(&self) -> Option<&UpdateAction> { + self.actions + .iter() + .find(|action| matches!(action, UpdateAction::Install { .. })) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TaskExecutor { + Fake, +} + +impl TaskExecutor { + pub const fn as_str(self) -> &'static str { + match self { + Self::Fake => "fake", + } + } +} + +impl FromStr for TaskExecutor { + type Err = TaskModelError; + + fn from_str(value: &str) -> Result { + match value { + "fake" => Ok(Self::Fake), + other => Err(TaskModelError::InvalidExecutor(other.to_owned())), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DownloadTaskStatus { + Queued, + Running, + Succeeded, + Failed, + Canceled, +} + +impl DownloadTaskStatus { + pub const fn as_str(self) -> &'static str { + match self { + Self::Queued => "queued", + Self::Running => "running", + Self::Succeeded => "succeeded", + Self::Failed => "failed", + Self::Canceled => "canceled", + } + } + + pub const fn is_terminal(self) -> bool { + matches!(self, Self::Succeeded | Self::Failed | Self::Canceled) + } +} + +impl FromStr for DownloadTaskStatus { + type Err = TaskModelError; + + fn from_str(value: &str) -> Result { + match value { + "queued" => Ok(Self::Queued), + "running" => Ok(Self::Running), + "succeeded" => Ok(Self::Succeeded), + "failed" => Ok(Self::Failed), + "canceled" => Ok(Self::Canceled), + other => Err(TaskModelError::InvalidTaskStatus(other.to_owned())), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TaskEventKind { + TaskCreated, + TaskStarted, + TaskSucceeded, + TaskFailed, + TaskCanceled, + InstallHandoffRequested, + InstallResultRecorded, +} + +impl TaskEventKind { + pub const fn as_str(self) -> &'static str { + match self { + Self::TaskCreated => "task_created", + Self::TaskStarted => "task_started", + Self::TaskSucceeded => "task_succeeded", + Self::TaskFailed => "task_failed", + Self::TaskCanceled => "task_canceled", + Self::InstallHandoffRequested => "install_handoff_requested", + Self::InstallResultRecorded => "install_result_recorded", + } + } +} + +impl FromStr for TaskEventKind { + type Err = TaskModelError; + + fn from_str(value: &str) -> Result { + match value { + "task_created" => Ok(Self::TaskCreated), + "task_started" => Ok(Self::TaskStarted), + "task_succeeded" => Ok(Self::TaskSucceeded), + "task_failed" => Ok(Self::TaskFailed), + "task_canceled" => Ok(Self::TaskCanceled), + "install_handoff_requested" => Ok(Self::InstallHandoffRequested), + "install_result_recorded" => Ok(Self::InstallResultRecorded), + other => Err(TaskModelError::InvalidEventKind(other.to_owned())), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DownloadTaskSummary { + pub id: String, + pub package_id: PackageId, + pub status: DownloadTaskStatus, + pub executor: TaskExecutor, + pub actions: Vec, + pub download_file_name: String, + #[serde(default)] + pub downloaded_file: Option, + #[serde(default)] + pub failure_message: Option, + #[serde(default)] + pub install_handoff_id: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskEvent { + pub cursor: u64, + pub task_id: String, + pub kind: TaskEventKind, + #[serde(default)] + pub status: Option, + #[serde(default)] + pub message: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskEventPage { + pub events: Vec, + pub next_cursor: u64, + pub has_more: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskCancelResult { + pub task_id: String, + pub status: DownloadTaskStatus, + pub changed: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskRunResult { + pub task: DownloadTaskSummary, + #[serde(default)] + pub install_handoff: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskSubmitResult { + pub task: DownloadTaskSummary, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct InstallResultRecord { + pub handoff: InstallHandoffSummary, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum InstallHandoffStatus { + Requested, + Accepted, + Succeeded, + Failed, + Canceled, +} + +impl InstallHandoffStatus { + pub const fn as_str(self) -> &'static str { + match self { + Self::Requested => "requested", + Self::Accepted => "accepted", + Self::Succeeded => "succeeded", + Self::Failed => "failed", + Self::Canceled => "canceled", + } + } +} + +impl FromStr for InstallHandoffStatus { + type Err = TaskModelError; + + fn from_str(value: &str) -> Result { + match value { + "requested" => Ok(Self::Requested), + "accepted" => Ok(Self::Accepted), + "succeeded" => Ok(Self::Succeeded), + "failed" => Ok(Self::Failed), + "canceled" => Ok(Self::Canceled), + other => Err(TaskModelError::InvalidInstallHandoffStatus( + other.to_owned(), + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct InstallHandoffSummary { + pub id: String, + pub task_id: String, + pub package_id: PackageId, + pub installer: String, + pub file: String, + pub status: InstallHandoffStatus, + #[serde(default)] + pub message: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum TaskModelError { + #[error("invalid task executor '{0}'")] + InvalidExecutor(String), + #[error("invalid download task status '{0}'")] + InvalidTaskStatus(String), + #[error("invalid task event kind '{0}'")] + InvalidEventKind(String), + #[error("invalid install handoff status '{0}'")] + InvalidInstallHandoffStatus(String), +} diff --git a/crates/getter-downloader/Cargo.toml b/crates/getter-downloader/Cargo.toml index d8b2562..8cf48c4 100644 --- a/crates/getter-downloader/Cargo.toml +++ b/crates/getter-downloader/Cargo.toml @@ -5,3 +5,5 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core" } +getter-storage = { path = "../getter-storage" } +thiserror = "1" diff --git a/crates/getter-downloader/src/lib.rs b/crates/getter-downloader/src/lib.rs index 4a11c7d..52318c5 100644 --- a/crates/getter-downloader/src/lib.rs +++ b/crates/getter-downloader/src/lib.rs @@ -1,3 +1,167 @@ -//! getter-downloader rewrite crate skeleton. +//! Deterministic offline downloader lifecycle for the getter rewrite. +//! +//! This crate intentionally does not perform live network I/O. It proves the +//! getter-owned task state, cancellation, pollable events, and install-handoff +//! contract with an explicit fake executor. + +use getter_core::task::{ + DownloadTaskRequest, InstallHandoffStatus, InstallResultRecord, TaskCancelResult, + TaskRunResult, TaskSubmitResult, DOWNLOAD_REQUEST_FORMAT, DOWNLOAD_REQUEST_VERSION, +}; +use getter_storage::{MainDb, StorageError}; pub use getter_core as core; + +#[derive(Debug, thiserror::Error)] +pub enum DownloaderError { + #[error("unsupported download request format '{0}'")] + UnsupportedFormat(String), + #[error("unsupported download request version {found}; expected {expected}")] + UnsupportedVersion { found: u32, expected: u32 }, + #[error("download request must use the fake executor for this offline slice")] + UnsupportedExecutor, + #[error("download request must include a download action")] + MissingDownloadAction, + #[error("storage error: {0}")] + Storage(#[from] StorageError), +} + +pub fn submit_fake_download_task( + db: &MainDb, + request: DownloadTaskRequest, +) -> Result { + validate_fake_request(&request)?; + let task = db.create_download_task(&request)?; + Ok(TaskSubmitResult { task }) +} + +pub fn run_fake_download_task( + db: &MainDb, + task_id: &str, +) -> Result { + db.start_download_task(task_id)?; + let task = db.succeed_download_task(task_id)?; + let install_handoff = db.create_install_handoff_for_task(task_id)?; + Ok(TaskRunResult { + task, + install_handoff, + }) +} + +pub fn cancel_download_task( + db: &MainDb, + task_id: &str, +) -> Result { + Ok(db.cancel_download_task(task_id)?) +} + +pub fn record_install_result( + db: &MainDb, + handoff_id: &str, + status: InstallHandoffStatus, +) -> Result { + let handoff = db.record_install_result(handoff_id, status, None)?; + Ok(InstallResultRecord { handoff }) +} + +fn validate_fake_request(request: &DownloadTaskRequest) -> Result<(), DownloaderError> { + if request.format != DOWNLOAD_REQUEST_FORMAT { + return Err(DownloaderError::UnsupportedFormat(request.format.clone())); + } + if request.version != DOWNLOAD_REQUEST_VERSION { + return Err(DownloaderError::UnsupportedVersion { + found: request.version, + expected: DOWNLOAD_REQUEST_VERSION, + }); + } + // This match is intentionally explicit so adding a real executor later forces + // this offline-only validation point to be revisited. + match request.executor { + getter_core::task::TaskExecutor::Fake => {} + } + if request.download_action().is_none() { + return Err(DownloaderError::MissingDownloadAction); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::task::{DownloadTaskStatus, TaskEventKind, TaskExecutor}; + use getter_core::{PackageId, UpdateAction}; + + #[test] + fn fake_submit_run_records_install_handoff_and_events() { + let db = MainDb::open_in_memory().unwrap(); + let submitted = submit_fake_download_task(&db, request()).unwrap(); + assert_eq!(submitted.task.id, "task-1"); + assert_eq!(submitted.task.status, DownloadTaskStatus::Queued); + + let result = run_fake_download_task(&db, &submitted.task.id).unwrap(); + assert_eq!(result.task.status, DownloadTaskStatus::Succeeded); + assert_eq!( + result.install_handoff.as_ref().unwrap().installer, + "android_package" + ); + + let events = db.task_events_after(0, 10).unwrap().events; + assert_eq!(events[0].kind, TaskEventKind::TaskCreated); + assert_eq!(events[1].kind, TaskEventKind::TaskStarted); + assert_eq!(events[2].kind, TaskEventKind::TaskSucceeded); + assert_eq!(events[3].kind, TaskEventKind::InstallHandoffRequested); + } + + #[test] + fn fake_cancel_before_run_is_persisted() { + let db = MainDb::open_in_memory().unwrap(); + let submitted = submit_fake_download_task(&db, request()).unwrap(); + + let canceled = cancel_download_task(&db, &submitted.task.id).unwrap(); + assert!(canceled.changed); + assert_eq!(canceled.status, DownloadTaskStatus::Canceled); + assert!(run_fake_download_task(&db, &submitted.task.id).is_err()); + } + + #[test] + fn fake_submit_rejects_wrong_contract() { + let db = MainDb::open_in_memory().unwrap(); + let mut request = request(); + request.format = "wrong".to_owned(); + + let error = submit_fake_download_task(&db, request).unwrap_err(); + assert!(matches!(error, DownloaderError::UnsupportedFormat(_))); + } + + #[test] + fn install_result_can_be_recorded_after_handoff() { + let db = MainDb::open_in_memory().unwrap(); + let submitted = submit_fake_download_task(&db, request()).unwrap(); + let result = run_fake_download_task(&db, &submitted.task.id).unwrap(); + let handoff = result.install_handoff.unwrap(); + + let recorded = + record_install_result(&db, &handoff.id, InstallHandoffStatus::Succeeded).unwrap(); + assert_eq!(recorded.handoff.status, InstallHandoffStatus::Succeeded); + } + + fn request() -> DownloadTaskRequest { + DownloadTaskRequest { + format: DOWNLOAD_REQUEST_FORMAT.to_owned(), + version: DOWNLOAD_REQUEST_VERSION, + package_id: PackageId::new(getter_core::PackageKind::Android, "org.fdroid.fdroid") + .unwrap(), + executor: TaskExecutor::Fake, + actions: vec![ + UpdateAction::Download { + url: "https://example.invalid/app.apk".to_owned(), + file_name: "app.apk".to_owned(), + }, + UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned(), + }, + ], + } + } +} diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index f1285c2..23b0e8a 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -3,8 +3,13 @@ pub mod legacy_room; use getter_core::repository::RepositoryMetadata; -use getter_core::{PackageId, RepositoryId, RepositoryPriority}; -use rusqlite::{params, Connection, Params, Transaction}; +use getter_core::task::{ + DownloadTaskRequest, DownloadTaskStatus, DownloadTaskSummary, InstallHandoffStatus, + InstallHandoffSummary, TaskCancelResult, TaskEvent, TaskEventKind, TaskEventPage, + TaskModelError, +}; +use getter_core::{PackageId, RepositoryId, RepositoryPriority, UpdateAction}; +use rusqlite::{params, Connection, OptionalExtension, Params, Transaction}; use std::path::Path; use std::str::FromStr; @@ -18,6 +23,20 @@ pub enum StorageError { RepositoryId(#[from] getter_core::RepositoryIdError), #[error("invalid package resolution in database: {0}")] PackageResolution(String), + #[error("invalid task state in database: {0}")] + TaskState(String), + #[error("download task not found: {0}")] + TaskNotFound(String), + #[error("install handoff not found: {0}")] + InstallHandoffNotFound(String), + #[error("invalid install handoff transition for {handoff_id}: {reason}")] + InvalidInstallHandoffTransition { handoff_id: String, reason: String }, + #[error("invalid task transition for {task_id}: {reason}")] + InvalidTaskTransition { task_id: String, reason: String }, + #[error("invalid task request: {0}")] + InvalidTaskRequest(String), + #[error("JSON error: {0}")] + Json(#[from] serde_json::Error), } pub struct MainDb { @@ -78,6 +97,43 @@ CREATE TABLE IF NOT EXISTS migration_records ( completed_at_unix INTEGER NOT NULL DEFAULT (unixepoch()), report_json TEXT NOT NULL DEFAULT '{}' ); + +CREATE TABLE IF NOT EXISTS download_tasks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id TEXT UNIQUE, + package_id TEXT NOT NULL, + status TEXT NOT NULL, + executor TEXT NOT NULL, + actions_json TEXT NOT NULL, + download_file_name TEXT NOT NULL, + downloaded_file TEXT, + failure_message TEXT, + install_handoff_id TEXT, + created_at_unix INTEGER NOT NULL DEFAULT (unixepoch()), + updated_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + +CREATE TABLE IF NOT EXISTS task_events ( + cursor INTEGER PRIMARY KEY AUTOINCREMENT, + task_id TEXT NOT NULL, + kind TEXT NOT NULL, + status TEXT, + message TEXT, + created_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + +CREATE TABLE IF NOT EXISTS install_handoffs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + handoff_id TEXT UNIQUE, + task_id TEXT NOT NULL, + package_id TEXT NOT NULL, + installer TEXT NOT NULL, + file TEXT NOT NULL, + status TEXT NOT NULL, + message TEXT, + created_at_unix INTEGER NOT NULL DEFAULT (unixepoch()), + updated_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); "#, )?; self.ensure_column("tracked_packages", "ignored_version", "TEXT")?; @@ -90,6 +146,10 @@ CREATE TABLE IF NOT EXISTS migration_records ( "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('main-v1')", [], )?; + self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('main-task-v1')", + [], + )?; Ok(()) } @@ -345,6 +405,375 @@ ON CONFLICT(id) DO UPDATE SET } Ok(records) } + + pub fn create_download_task( + &self, + request: &DownloadTaskRequest, + ) -> Result { + let download_file_name = match request.download_action() { + Some(UpdateAction::Download { file_name, .. }) => file_name.clone(), + _ => { + return Err(StorageError::InvalidTaskRequest( + "download task request must include a download action".to_owned(), + )) + } + }; + let actions_json = serde_json::to_string(&request.actions)?; + let tx = self.conn.unchecked_transaction()?; + tx.execute( + r#" +INSERT INTO download_tasks(package_id, status, executor, actions_json, download_file_name) +VALUES (?1, ?2, ?3, ?4, ?5) +"#, + params![ + request.package_id.to_string(), + DownloadTaskStatus::Queued.as_str(), + request.executor.as_str(), + actions_json, + download_file_name, + ], + )?; + let row_id = tx.last_insert_rowid(); + let task_id = format!("task-{row_id}"); + tx.execute( + "UPDATE download_tasks SET task_id = ?1 WHERE id = ?2", + params![task_id, row_id], + )?; + insert_task_event_with_executor( + &tx, + &task_id, + TaskEventKind::TaskCreated, + Some(DownloadTaskStatus::Queued), + Some("Task queued"), + )?; + tx.commit()?; + self.download_task(&task_id) + } + + pub fn download_task(&self, task_id: &str) -> Result { + let mut stmt = self.conn.prepare( + r#" +SELECT task_id, package_id, status, executor, actions_json, download_file_name, + downloaded_file, failure_message, install_handoff_id +FROM download_tasks +WHERE task_id = ?1 +"#, + )?; + let row = stmt + .query_row([task_id], |row| { + Ok(DownloadTaskRow { + task_id: row.get(0)?, + package_id: row.get(1)?, + status: row.get(2)?, + executor: row.get(3)?, + actions_json: row.get(4)?, + download_file_name: row.get(5)?, + downloaded_file: row.get(6)?, + failure_message: row.get(7)?, + install_handoff_id: row.get(8)?, + }) + }) + .optional()?; + row.map(download_task_from_row) + .transpose()? + .ok_or_else(|| StorageError::TaskNotFound(task_id.to_owned())) + } + + pub fn download_tasks(&self) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + r#" +SELECT task_id, package_id, status, executor, actions_json, download_file_name, + downloaded_file, failure_message, install_handoff_id +FROM download_tasks +ORDER BY id ASC +"#, + )?; + let rows = stmt.query_map([], |row| { + Ok(DownloadTaskRow { + task_id: row.get(0)?, + package_id: row.get(1)?, + status: row.get(2)?, + executor: row.get(3)?, + actions_json: row.get(4)?, + download_file_name: row.get(5)?, + downloaded_file: row.get(6)?, + failure_message: row.get(7)?, + install_handoff_id: row.get(8)?, + }) + })?; + let mut tasks = Vec::new(); + for row in rows { + tasks.push(download_task_from_row(row?)?); + } + Ok(tasks) + } + + pub fn start_download_task(&self, task_id: &str) -> Result { + let task = self.download_task(task_id)?; + match task.status { + DownloadTaskStatus::Queued => { + self.update_download_task_status( + task_id, + DownloadTaskStatus::Running, + None, + None, + TaskEventKind::TaskStarted, + "Task started", + )?; + self.download_task(task_id) + } + DownloadTaskStatus::Running => Ok(task), + status => Err(StorageError::InvalidTaskTransition { + task_id: task_id.to_owned(), + reason: format!("cannot start task with status {}", status.as_str()), + }), + } + } + + pub fn succeed_download_task( + &self, + task_id: &str, + ) -> Result { + let task = self.download_task(task_id)?; + match task.status { + DownloadTaskStatus::Queued | DownloadTaskStatus::Running => { + self.update_download_task_status( + task_id, + DownloadTaskStatus::Succeeded, + Some(&task.download_file_name), + None, + TaskEventKind::TaskSucceeded, + "Task succeeded", + )?; + self.download_task(task_id) + } + status => Err(StorageError::InvalidTaskTransition { + task_id: task_id.to_owned(), + reason: format!("cannot succeed task with status {}", status.as_str()), + }), + } + } + + pub fn cancel_download_task(&self, task_id: &str) -> Result { + let task = self.download_task(task_id)?; + match task.status { + DownloadTaskStatus::Queued | DownloadTaskStatus::Running => { + self.update_download_task_status( + task_id, + DownloadTaskStatus::Canceled, + None, + None, + TaskEventKind::TaskCanceled, + "Task canceled", + )?; + Ok(TaskCancelResult { + task_id: task_id.to_owned(), + status: DownloadTaskStatus::Canceled, + changed: true, + }) + } + DownloadTaskStatus::Canceled => Ok(TaskCancelResult { + task_id: task_id.to_owned(), + status: DownloadTaskStatus::Canceled, + changed: false, + }), + status => Err(StorageError::InvalidTaskTransition { + task_id: task_id.to_owned(), + reason: format!("cannot cancel task with status {}", status.as_str()), + }), + } + } + + pub fn create_install_handoff_for_task( + &self, + task_id: &str, + ) -> Result, StorageError> { + let task = self.download_task(task_id)?; + if task.status != DownloadTaskStatus::Succeeded { + return Err(StorageError::InvalidTaskTransition { + task_id: task_id.to_owned(), + reason: format!( + "cannot request install handoff for task with status {}", + task.status.as_str() + ), + }); + } + if let Some(existing) = task.install_handoff_id.as_deref() { + return Ok(Some(self.install_handoff(existing)?)); + } + let Some(UpdateAction::Install { installer, file }) = task + .actions + .iter() + .find(|action| matches!(action, UpdateAction::Install { .. })) + else { + return Ok(None); + }; + let tx = self.conn.unchecked_transaction()?; + tx.execute( + r#" +INSERT INTO install_handoffs(task_id, package_id, installer, file, status) +VALUES (?1, ?2, ?3, ?4, ?5) +"#, + params![ + task_id, + task.package_id.to_string(), + installer, + file, + InstallHandoffStatus::Requested.as_str(), + ], + )?; + let row_id = tx.last_insert_rowid(); + let handoff_id = format!("handoff-{row_id}"); + tx.execute( + "UPDATE install_handoffs SET handoff_id = ?1 WHERE id = ?2", + params![handoff_id, row_id], + )?; + tx.execute( + "UPDATE download_tasks SET install_handoff_id = ?1, updated_at_unix = unixepoch() WHERE task_id = ?2", + params![handoff_id, task_id], + )?; + insert_task_event_with_executor( + &tx, + task_id, + TaskEventKind::InstallHandoffRequested, + Some(task.status), + Some("Install handoff requested"), + )?; + tx.commit()?; + Ok(Some(self.install_handoff(&handoff_id)?)) + } + + pub fn install_handoff(&self, handoff_id: &str) -> Result { + let mut stmt = self.conn.prepare( + r#" +SELECT handoff_id, task_id, package_id, installer, file, status, message +FROM install_handoffs +WHERE handoff_id = ?1 +"#, + )?; + let row = stmt + .query_row([handoff_id], |row| { + Ok(InstallHandoffRow { + handoff_id: row.get(0)?, + task_id: row.get(1)?, + package_id: row.get(2)?, + installer: row.get(3)?, + file: row.get(4)?, + status: row.get(5)?, + message: row.get(6)?, + }) + }) + .optional()?; + row.map(install_handoff_from_row) + .transpose()? + .ok_or_else(|| StorageError::InstallHandoffNotFound(handoff_id.to_owned())) + } + + pub fn record_install_result( + &self, + handoff_id: &str, + status: InstallHandoffStatus, + message: Option<&str>, + ) -> Result { + if status == InstallHandoffStatus::Requested { + return Err(StorageError::InvalidInstallHandoffTransition { + handoff_id: handoff_id.to_owned(), + reason: "requested is getter-created state, not a platform install result" + .to_owned(), + }); + } + let handoff = self.install_handoff(handoff_id)?; + let tx = self.conn.unchecked_transaction()?; + tx.execute( + r#" +UPDATE install_handoffs +SET status = ?1, message = ?2, updated_at_unix = unixepoch() +WHERE handoff_id = ?3 +"#, + params![status.as_str(), message, handoff_id], + )?; + insert_task_event_with_executor( + &tx, + &handoff.task_id, + TaskEventKind::InstallResultRecorded, + None, + Some("Install result recorded"), + )?; + tx.commit()?; + self.install_handoff(handoff_id) + } + + pub fn task_events_after( + &self, + after: u64, + limit: usize, + ) -> Result { + let fetch_limit = limit.saturating_add(1) as i64; + let mut stmt = self.conn.prepare( + r#" +SELECT cursor, task_id, kind, status, message +FROM task_events +WHERE cursor > ?1 +ORDER BY cursor ASC +LIMIT ?2 +"#, + )?; + let rows = stmt.query_map(params![after as i64, fetch_limit], |row| { + Ok(TaskEventRow { + cursor: row.get(0)?, + task_id: row.get(1)?, + kind: row.get(2)?, + status: row.get(3)?, + message: row.get(4)?, + }) + })?; + let mut events = Vec::new(); + for row in rows { + events.push(task_event_from_row(row?)?); + } + let has_more = events.len() > limit; + if has_more { + events.truncate(limit); + } + let next_cursor = events.last().map(|event| event.cursor).unwrap_or(after); + Ok(TaskEventPage { + events, + next_cursor, + has_more, + }) + } + + fn update_download_task_status( + &self, + task_id: &str, + status: DownloadTaskStatus, + downloaded_file: Option<&str>, + failure_message: Option<&str>, + event_kind: TaskEventKind, + event_message: &str, + ) -> Result<(), StorageError> { + let tx = self.conn.unchecked_transaction()?; + tx.execute( + r#" +UPDATE download_tasks +SET status = ?1, + downloaded_file = COALESCE(?2, downloaded_file), + failure_message = ?3, + updated_at_unix = unixepoch() +WHERE task_id = ?4 +"#, + params![status.as_str(), downloaded_file, failure_message, task_id], + )?; + insert_task_event_with_executor( + &tx, + task_id, + event_kind, + Some(status), + Some(event_message), + )?; + tx.commit()?; + Ok(()) + } } impl CacheDb { @@ -435,6 +864,36 @@ pub struct StoredMigrationRecord { pub report_json: String, } +struct DownloadTaskRow { + task_id: String, + package_id: String, + status: String, + executor: String, + actions_json: String, + download_file_name: String, + downloaded_file: Option, + failure_message: Option, + install_handoff_id: Option, +} + +struct TaskEventRow { + cursor: i64, + task_id: String, + kind: String, + status: Option, + message: Option, +} + +struct InstallHandoffRow { + handoff_id: String, + task_id: String, + package_id: String, + installer: String, + file: String, + status: String, + message: Option, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MigrationRecordUpsert<'a> { pub id: &'a str, @@ -529,10 +988,84 @@ fn bool_to_i64(value: bool) -> i64 { } } +fn download_task_from_row(row: DownloadTaskRow) -> Result { + Ok(DownloadTaskSummary { + id: row.task_id, + package_id: row.package_id.parse()?, + status: row + .status + .parse() + .map_err(|error: TaskModelError| StorageError::TaskState(error.to_string()))?, + executor: row + .executor + .parse() + .map_err(|error: TaskModelError| StorageError::TaskState(error.to_string()))?, + actions: serde_json::from_str(&row.actions_json)?, + download_file_name: row.download_file_name, + downloaded_file: row.downloaded_file, + failure_message: row.failure_message, + install_handoff_id: row.install_handoff_id, + }) +} + +fn task_event_from_row(row: TaskEventRow) -> Result { + Ok(TaskEvent { + cursor: row.cursor as u64, + task_id: row.task_id, + kind: row + .kind + .parse() + .map_err(|error: TaskModelError| StorageError::TaskState(error.to_string()))?, + status: row + .status + .map(|status| status.parse()) + .transpose() + .map_err(|error: TaskModelError| StorageError::TaskState(error.to_string()))?, + message: row.message, + }) +} + +fn install_handoff_from_row(row: InstallHandoffRow) -> Result { + Ok(InstallHandoffSummary { + id: row.handoff_id, + task_id: row.task_id, + package_id: row.package_id.parse()?, + installer: row.installer, + file: row.file, + status: row + .status + .parse() + .map_err(|error: TaskModelError| StorageError::TaskState(error.to_string()))?, + message: row.message, + }) +} + +fn insert_task_event_with_executor( + conn: &impl SqlExecutor, + task_id: &str, + kind: TaskEventKind, + status: Option, + message: Option<&str>, +) -> Result { + conn.execute_statement( + r#" +INSERT INTO task_events(task_id, kind, status, message) +VALUES (?1, ?2, ?3, ?4) +"#, + params![ + task_id, + kind.as_str(), + status.map(DownloadTaskStatus::as_str), + message, + ], + ) +} + #[cfg(test)] mod tests { use super::*; use getter_core::repository::REPO_API_VERSION_V1; + use getter_core::task::TaskExecutor; #[test] fn main_db_stores_repository_registry_ordered_by_priority() { @@ -720,6 +1253,143 @@ mod tests { assert!(db.migration_record_exists("legacy-room-v17").unwrap()); } + #[test] + fn task_lifecycle_persists_across_reopen() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("main.db"); + { + let db = MainDb::open(&db_path).unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + assert_eq!(task.id, "task-1"); + assert_eq!(task.status, DownloadTaskStatus::Queued); + } + + let db = MainDb::open(&db_path).unwrap(); + let tasks = db.download_tasks().unwrap(); + assert_eq!(tasks.len(), 1); + assert_eq!(tasks[0].id, "task-1"); + assert_eq!(tasks[0].package_id.to_string(), "android/org.fdroid.fdroid"); + assert_eq!(tasks[0].status, DownloadTaskStatus::Queued); + } + + #[test] + fn task_cancel_is_persistent_and_idempotent_before_terminal_state() { + let db = MainDb::open_in_memory().unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + + let first = db.cancel_download_task(&task.id).unwrap(); + assert!(first.changed); + assert_eq!(first.status, DownloadTaskStatus::Canceled); + let second = db.cancel_download_task(&task.id).unwrap(); + assert!(!second.changed); + assert_eq!( + db.download_task(&task.id).unwrap().status, + DownloadTaskStatus::Canceled + ); + } + + #[test] + fn task_cancel_after_success_is_rejected() { + let db = MainDb::open_in_memory().unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + db.start_download_task(&task.id).unwrap(); + db.succeed_download_task(&task.id).unwrap(); + + let error = db.cancel_download_task(&task.id).unwrap_err(); + assert!(matches!(error, StorageError::InvalidTaskTransition { .. })); + } + + #[test] + fn task_events_are_pollable_with_cursor_and_limit() { + let db = MainDb::open_in_memory().unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + db.start_download_task(&task.id).unwrap(); + db.succeed_download_task(&task.id).unwrap(); + + let first_page = db.task_events_after(0, 2).unwrap(); + assert_eq!(first_page.events.len(), 2); + assert!(first_page.has_more); + assert_eq!(first_page.events[0].kind, TaskEventKind::TaskCreated); + assert_eq!(first_page.events[1].kind, TaskEventKind::TaskStarted); + + let second_page = db.task_events_after(first_page.next_cursor, 2).unwrap(); + assert_eq!(second_page.events.len(), 1); + assert!(!second_page.has_more); + assert_eq!(second_page.events[0].kind, TaskEventKind::TaskSucceeded); + } + + #[test] + fn install_handoff_result_is_recorded() { + let db = MainDb::open_in_memory().unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + db.start_download_task(&task.id).unwrap(); + db.succeed_download_task(&task.id).unwrap(); + let handoff = db + .create_install_handoff_for_task(&task.id) + .unwrap() + .unwrap(); + assert_eq!(handoff.id, "handoff-1"); + assert_eq!(handoff.status, InstallHandoffStatus::Requested); + + let updated = db + .record_install_result(&handoff.id, InstallHandoffStatus::Succeeded, Some("ok")) + .unwrap(); + assert_eq!(updated.status, InstallHandoffStatus::Succeeded); + assert_eq!(updated.message.as_deref(), Some("ok")); + } + + #[test] + fn install_result_rejects_getter_created_requested_state() { + let db = MainDb::open_in_memory().unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + db.start_download_task(&task.id).unwrap(); + db.succeed_download_task(&task.id).unwrap(); + let handoff = db + .create_install_handoff_for_task(&task.id) + .unwrap() + .unwrap(); + + let error = db + .record_install_result(&handoff.id, InstallHandoffStatus::Requested, None) + .unwrap_err(); + assert!(matches!( + error, + StorageError::InvalidInstallHandoffTransition { .. } + )); + } + + #[test] + fn task_request_requires_download_action() { + let db = MainDb::open_in_memory().unwrap(); + let mut request = download_request(); + request.actions = vec![UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned(), + }]; + + let error = db.create_download_task(&request).unwrap_err(); + assert!(matches!(error, StorageError::InvalidTaskRequest(_))); + } + + fn download_request() -> DownloadTaskRequest { + DownloadTaskRequest { + format: getter_core::task::DOWNLOAD_REQUEST_FORMAT.to_owned(), + version: getter_core::task::DOWNLOAD_REQUEST_VERSION, + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + executor: TaskExecutor::Fake, + actions: vec![ + UpdateAction::Download { + url: "https://example.invalid/app.apk".to_owned(), + file_name: "app.apk".to_owned(), + }, + UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned(), + }, + ], + } + } + #[test] fn cache_db_migrates_schema() { let _db = CacheDb::open_in_memory().unwrap(); From cee3b007d5e03d9035f5319a582f452dfbc7c911 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 23 Jun 2026 23:57:34 +0800 Subject: [PATCH 17/72] feat: share installed autogen operations --- Cargo.lock | 12 + Cargo.toml | 7 +- crates/getter-cli/Cargo.toml | 1 + crates/getter-cli/src/lib.rs | 610 ++--------------------- crates/getter-core/Cargo.toml | 6 +- crates/getter-core/src/lib.rs | 2 + crates/getter-operations/Cargo.toml | 10 + crates/getter-operations/src/autogen.rs | 637 ++++++++++++++++++++++++ crates/getter-operations/src/lib.rs | 8 + crates/getter-storage/Cargo.toml | 2 +- src/lib.rs | 2 + 11 files changed, 714 insertions(+), 583 deletions(-) create mode 100644 crates/getter-operations/Cargo.toml create mode 100644 crates/getter-operations/src/autogen.rs create mode 100644 crates/getter-operations/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a23b19e..1ff310d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -508,6 +508,7 @@ version = "0.1.0" dependencies = [ "getter-cli", "getter-core", + "getter-operations", "getter-storage", "rustls-platform-verifier", "thiserror 1.0.69", @@ -521,6 +522,7 @@ dependencies = [ "cucumber", "getter-core", "getter-downloader", + "getter-operations", "getter-storage", "rusqlite", "serde", @@ -558,6 +560,16 @@ dependencies = [ "getter-core", ] +[[package]] +name = "getter-operations" +version = "0.1.0" +dependencies = [ + "getter-core", + "getter-storage", + "serde_json", + "thiserror 1.0.69", +] + [[package]] name = "getter-plugin-api" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index e45af61..9347d01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "crates/getter-storage", "crates/getter-providers", "crates/getter-downloader", + "crates/getter-operations", "crates/getter-plugin-api", "crates/getter-rpc", "crates/getter-cli", @@ -22,7 +23,8 @@ edition = "2021" [features] default = ["cli", "domain"] -domain = ["dep:getter-core", "dep:getter-storage"] +domain = ["dep:getter-core", "dep:getter-storage", "dep:getter-operations"] +lua = ["getter-core/lua"] cli = ["dep:getter-cli"] rustls-platform-verifier = ["dep:rustls-platform-verifier"] rustls-platform-verifier-android = ["rustls-platform-verifier", "rustls-platform-verifier/jni"] @@ -30,8 +32,9 @@ webpki-roots = [] native-tokio = [] [dependencies] -getter-core = { path = "crates/getter-core", optional = true } +getter-core = { path = "crates/getter-core", default-features = false, optional = true } getter-storage = { path = "crates/getter-storage", optional = true } +getter-operations = { path = "crates/getter-operations", optional = true } getter-cli = { path = "crates/getter-cli", optional = true } thiserror = "1" tokio = { version = "1", features = ["net"] } diff --git a/crates/getter-cli/Cargo.toml b/crates/getter-cli/Cargo.toml index 248cb01..a5cec89 100644 --- a/crates/getter-cli/Cargo.toml +++ b/crates/getter-cli/Cargo.toml @@ -6,6 +6,7 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core" } getter-downloader = { path = "../getter-downloader" } +getter-operations = { path = "../getter-operations" } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 08d63d0..d9f4d47 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -4,15 +4,10 @@ //! while durable state is initialized through `getter-storage` so the command //! surface exercises the same Rust-owned SQLite direction used by embedders. -use getter_core::autogen::{ - content_hash, local_autogen_repo_toml, local_repo_toml, plan_local_autogen, AutogenManifest, - AutogenManifestEntry, AutogenPlan, AutogenSkipReason, InstalledInventory, - LOCAL_AUTOGEN_REPOSITORY_ID, LOCAL_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, - LOCAL_REPOSITORY_NAME, -}; +use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; -use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; +use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; use getter_core::task::{ DownloadTaskRequest, InstallHandoffStatus, TaskEventPage, DOWNLOAD_REQUEST_FORMAT, DOWNLOAD_REQUEST_VERSION, @@ -22,6 +17,7 @@ use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_downloader::{ cancel_download_task, record_install_result, run_fake_download_task, submit_fake_download_task, }; +use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; use getter_storage::legacy_room::{ map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, LegacyRoomDbImport, LegacyRoomImportWarning, @@ -33,15 +29,13 @@ use getter_storage::{ }; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; -use std::collections::{BTreeSet, HashMap}; use std::fs; -use std::path::{Component, Path, PathBuf}; +use std::path::{Path, PathBuf}; const MAIN_DB_FILE: &str = "main.db"; const CACHE_DB_FILE: &str = "cache.db"; const MIGRATION_REPORTS_DIR: &str = "migration-reports"; const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; -const AUTOGEN_MANIFEST_FILE: &str = "autogen-manifest.json"; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliInvocation { @@ -115,12 +109,6 @@ pub enum CliCommand { LegacyReportList, } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum AutogenAcceptance { - AcceptAll, - Accept(Vec), -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExitCode { Success = 0, @@ -252,6 +240,16 @@ impl From for CliError { } } +impl From for CliError { + fn from(value: AutogenOperationError) -> Self { + match value { + AutogenOperationError::Storage(source) => Self::Storage(source.to_string()), + AutogenOperationError::Repository(detail) => Self::Repository(detail), + AutogenOperationError::Autogen(detail) => Self::Autogen(detail), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliOutput { pub exit_code: ExitCode, @@ -609,8 +607,8 @@ fn execute(invocation: CliInvocation) -> Result { CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; - let plan = build_local_autogen_plan(&db, &inventory)?; - Ok(autogen_installed_preview_json(&invocation.data_dir, &plan)) + let plan = autogen::build_local_autogen_plan(&db, &inventory)?; + Ok(autogen::installed_preview_json(&invocation.data_dir, &plan)) } CliCommand::AutogenInstalledApply { preview, @@ -618,12 +616,14 @@ fn execute(invocation: CliInvocation) -> Result { } => { let db = open_main_db(&invocation.data_dir)?; let preview = read_autogen_preview(&preview, "installed.preview")?; - apply_autogen_installed_preview(&invocation.data_dir, &db, &preview, &acceptance) + autogen::apply_installed_preview(&invocation.data_dir, &db, &preview, &acceptance) + .map_err(CliError::from) } CliCommand::AutogenCleanupPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; - cleanup_preview_json(&invocation.data_dir, &db, &inventory) + autogen::cleanup_preview_json(&invocation.data_dir, &db, &inventory) + .map_err(CliError::from) } CliCommand::AutogenCleanupApply { preview, @@ -631,7 +631,8 @@ fn execute(invocation: CliInvocation) -> Result { } => { let db = open_main_db(&invocation.data_dir)?; let preview = read_autogen_preview(&preview, "cleanup.preview")?; - apply_autogen_cleanup_preview(&invocation.data_dir, &db, &preview, &acceptance) + autogen::apply_cleanup_preview(&invocation.data_dir, &db, &preview, &acceptance) + .map_err(CliError::from) } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; @@ -921,6 +922,15 @@ fn read_installed_inventory(path: &Path) -> Result .map_err(|source| CliError::Autogen(format!("failed to parse inventory JSON: {source}"))) } +fn read_autogen_preview(path: &Path, expected_operation: &str) -> Result { + let bytes = fs::read(path) + .map_err(|source| CliError::Autogen(format!("failed to read autogen preview: {source}")))?; + let raw: Value = serde_json::from_slice(&bytes).map_err(|source| { + CliError::Autogen(format!("failed to parse autogen preview JSON: {source}")) + })?; + autogen::unwrap_preview_payload(raw, expected_operation).map_err(CliError::from) +} + fn read_update_check_fixture(path: &Path) -> Result { let bytes = fs::read(path).map_err(|source| { CliError::Update(format!("failed to read update check fixture: {source}")) @@ -953,564 +963,6 @@ fn read_download_task_request(path: &Path) -> Result Result { - let covered = higher_priority_package_coverage(db)?; - plan_local_autogen(inventory, &covered).map_err(|source| CliError::Autogen(source.to_string())) -} - -fn higher_priority_package_coverage( - db: &MainDb, -) -> Result, CliError> { - let mut covered = HashMap::new(); - for repo in db.repositories()? { - if repo.id.as_str() == LOCAL_AUTOGEN_REPOSITORY_ID { - continue; - } - if repo.priority <= RepositoryPriority::LOCAL_AUTOGEN { - continue; - } - let Some(path) = repo.path.as_ref() else { - continue; - }; - let layout = load_repository_layout(Path::new(path))?; - for package in layout.packages { - covered.entry(package.id).or_insert_with(|| repo.id.clone()); - } - } - Ok(covered) -} - -fn autogen_installed_preview_json(data_dir: &Path, plan: &AutogenPlan) -> Value { - let candidates: Vec = plan.candidates.iter().map(autogen_candidate_json).collect(); - let skipped: Vec = plan.skipped.iter().map(autogen_skip_json).collect(); - json!({ - "operation": "installed.preview", - "target_repo_id": plan.repository_id.as_str(), - "target_repo_path": local_autogen_repo_path(data_dir), - "summary": { - "candidate_count": candidates.len(), - "skipped_count": skipped.len(), - "write_count": candidates.len(), - "delete_count": 0, - }, - "candidates": candidates, - "skipped": skipped, - "diagnostics": [], - }) -} - -fn autogen_candidate_json(candidate: &getter_core::autogen::AutogenCandidate) -> Value { - json!({ - "package_id": candidate.package_id.to_string(), - "kind": candidate.package_id.kind().as_str(), - "display_name": candidate.name, - "installed_target": candidate.installed, - "action": "create", - "output_relative_path": candidate.relative_path, - "content_hash": candidate.content_hash, - "content": candidate.content, - }) -} - -fn autogen_skip_json(skip: &getter_core::autogen::AutogenSkip) -> Value { - json!({ - "package_id": skip.package_id.to_string(), - "reason": match skip.reason { - AutogenSkipReason::DuplicateInventoryItem => "duplicate_inventory_item", - AutogenSkipReason::CoveredByHigherPriorityRepository => "covered_by_higher_priority_repo", - }, - "covering_repo_id": skip.repository_id.as_ref().map(RepositoryId::as_str), - }) -} - -fn read_autogen_preview(path: &Path, expected_operation: &str) -> Result { - let bytes = fs::read(path) - .map_err(|source| CliError::Autogen(format!("failed to read autogen preview: {source}")))?; - let raw: Value = serde_json::from_slice(&bytes).map_err(|source| { - CliError::Autogen(format!("failed to parse autogen preview JSON: {source}")) - })?; - let payload = if raw.get("ok").is_some() && raw.get("data").is_some() { - raw.get("data").cloned().unwrap_or(Value::Null) - } else { - raw - }; - if payload.get("operation").and_then(Value::as_str) != Some(expected_operation) { - return Err(CliError::Autogen(format!( - "autogen preview operation must be '{expected_operation}'" - ))); - } - Ok(payload) -} - -fn apply_autogen_installed_preview( - data_dir: &Path, - db: &MainDb, - preview: &Value, - acceptance: &AutogenAcceptance, -) -> Result { - let repo_path = local_autogen_repo_path(data_dir); - ensure_local_autogen_repository(data_dir, db)?; - let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); - let mut applied = Vec::new(); - let mut preserved = Vec::new(); - - for candidate in accepted { - let package_id = preview_package_id(candidate)?; - let relative_path = preview_relative_path(candidate)?; - let content = candidate - .get("content") - .and_then(Value::as_str) - .ok_or_else(|| CliError::Autogen("preview candidate missing content".to_owned()))?; - let expected_hash = candidate - .get("content_hash") - .and_then(Value::as_str) - .ok_or_else(|| { - CliError::Autogen("preview candidate missing content_hash".to_owned()) - })?; - if content_hash(content) != expected_hash { - return Err(CliError::Autogen(format!( - "preview content hash mismatch for {package_id}" - ))); - } - let target = safe_join(&repo_path, &relative_path)?; - if target.exists() { - let current = fs::read_to_string(&target).map_err(|source| { - CliError::Autogen(format!( - "failed to read existing autogen file '{}': {source}", - target.display() - )) - })?; - let current_hash = content_hash(¤t); - let known_hash = manifest - .package(&package_id) - .map(|entry| entry.content_hash.as_str()); - if known_hash != Some(current_hash.as_str()) { - preserved.push(preserve_autogen_file_in_local( - data_dir, - db, - &package_id, - ¤t, - )?); - } - } - if let Some(parent) = target.parent() { - fs::create_dir_all(parent).map_err(|source| { - CliError::Autogen(format!( - "failed to create autogen package directory '{}': {source}", - parent.display() - )) - })?; - } - fs::write(&target, content).map_err(|source| { - CliError::Autogen(format!( - "failed to write autogen package '{}': {source}", - target.display() - )) - })?; - upsert_manifest_entry( - &mut manifest, - AutogenManifestEntry { - package_id: package_id.clone(), - relative_path: relative_path.clone(), - content_hash: expected_hash.to_owned(), - }, - ); - db.upsert_generated_tracked_package_preserving_user_state( - &package_id, - &RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), - )?; - applied.push(json!({ - "package_id": package_id.to_string(), - "output_relative_path": relative_path, - })); - } - - write_autogen_manifest(&repo_path, &manifest)?; - Ok(json!({ - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, - "target_repo_path": repo_path, - "applied_count": applied.len(), - "applied": applied, - "preserved_to_local": preserved, - })) -} - -fn cleanup_preview_json( - data_dir: &Path, - db: &MainDb, - inventory: &InstalledInventory, -) -> Result { - let repo_path = local_autogen_repo_path(data_dir); - let Some(manifest) = read_autogen_manifest(&repo_path)? else { - return Ok(json!({ - "operation": "cleanup.preview", - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, - "target_repo_path": repo_path, - "summary": { "candidate_count": 0, "skipped_count": 0, "write_count": 0, "delete_count": 0 }, - "candidates": [], - "skipped": [], - "diagnostics": [], - })); - }; - let plan = build_local_autogen_plan(db, inventory)?; - let installed_ids: BTreeSet = plan - .candidates - .iter() - .map(|candidate| candidate.package_id.to_string()) - .chain(plan.skipped.iter().map(|skip| skip.package_id.to_string())) - .collect(); - let mut candidates = Vec::new(); - for entry in manifest.packages { - if !installed_ids.contains(&entry.package_id.to_string()) { - candidates.push(json!({ - "package_id": entry.package_id.to_string(), - "action": "delete", - "output_relative_path": entry.relative_path, - "content_hash": entry.content_hash, - "reason": "not_in_installed_inventory", - })); - } - } - candidates.sort_by_key(|candidate| { - candidate - .get("package_id") - .and_then(Value::as_str) - .unwrap_or_default() - .to_owned() - }); - Ok(json!({ - "operation": "cleanup.preview", - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, - "target_repo_path": repo_path, - "summary": { - "candidate_count": candidates.len(), - "skipped_count": 0, - "write_count": 0, - "delete_count": candidates.len(), - }, - "candidates": candidates, - "skipped": [], - "diagnostics": [], - })) -} - -fn apply_autogen_cleanup_preview( - data_dir: &Path, - db: &MainDb, - preview: &Value, - acceptance: &AutogenAcceptance, -) -> Result { - if preview.get("target_repo_id").and_then(Value::as_str) != Some(LOCAL_AUTOGEN_REPOSITORY_ID) { - return Err(CliError::Autogen(format!( - "cleanup preview target_repo_id must be '{LOCAL_AUTOGEN_REPOSITORY_ID}'" - ))); - } - let repo_path = local_autogen_repo_path(data_dir); - let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); - let mut deleted = Vec::new(); - let mut preserved = Vec::new(); - let local_autogen_id = RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"); - for candidate in accepted { - let package_id = preview_package_id(candidate)?; - let relative_path = preview_relative_path(candidate)?; - let expected_hash = candidate - .get("content_hash") - .and_then(Value::as_str) - .ok_or_else(|| { - CliError::Autogen("cleanup preview candidate missing content_hash".to_owned()) - })?; - let manifest_entry = manifest.package(&package_id).ok_or_else(|| { - CliError::Autogen(format!( - "cleanup preview candidate {package_id} is not managed by local_autogen manifest" - )) - })?; - if manifest_entry.relative_path != relative_path - || manifest_entry.content_hash != expected_hash - { - return Err(CliError::Autogen(format!( - "cleanup preview candidate {package_id} does not match local_autogen manifest" - ))); - } - let target = safe_join(&repo_path, &relative_path)?; - if target.exists() { - let current = fs::read_to_string(&target).map_err(|source| { - CliError::Autogen(format!( - "failed to read existing autogen file '{}': {source}", - target.display() - )) - })?; - if content_hash(¤t) != expected_hash { - preserved.push(preserve_autogen_file_in_local( - data_dir, - db, - &package_id, - ¤t, - )?); - } - fs::remove_file(&target).map_err(|source| { - CliError::Autogen(format!( - "failed to delete autogen package '{}': {source}", - target.display() - )) - })?; - } - manifest - .packages - .retain(|entry| entry.package_id != package_id); - db.delete_generated_tracked_package(&package_id, &local_autogen_id)?; - deleted.push(json!({ - "package_id": package_id.to_string(), - "output_relative_path": relative_path, - })); - } - write_autogen_manifest(&repo_path, &manifest)?; - Ok(json!({ - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, - "target_repo_path": repo_path, - "deleted_count": deleted.len(), - "deleted": deleted, - "preserved_to_local": preserved, - })) -} - -fn accepted_preview_candidates<'a>( - preview: &'a Value, - acceptance: &AutogenAcceptance, -) -> Result, CliError> { - let candidates = preview - .get("candidates") - .and_then(Value::as_array) - .ok_or_else(|| CliError::Autogen("autogen preview missing candidates".to_owned()))?; - match acceptance { - AutogenAcceptance::AcceptAll => Ok(candidates.iter().collect()), - AutogenAcceptance::Accept(ids) => { - let accepted: BTreeSet = ids.iter().map(ToString::to_string).collect(); - Ok(candidates - .iter() - .filter(|candidate| { - candidate - .get("package_id") - .and_then(Value::as_str) - .is_some_and(|id| accepted.contains(id)) - }) - .collect()) - } - } -} - -fn preview_package_id(candidate: &Value) -> Result { - candidate - .get("package_id") - .and_then(Value::as_str) - .ok_or_else(|| CliError::Autogen("preview candidate missing package_id".to_owned()))? - .parse() - .map_err(|source: getter_core::PackageIdError| CliError::Autogen(source.to_string())) -} - -fn preview_relative_path(candidate: &Value) -> Result { - candidate - .get("output_relative_path") - .and_then(Value::as_str) - .map(PathBuf::from) - .ok_or_else(|| { - CliError::Autogen("preview candidate missing output_relative_path".to_owned()) - }) -} - -fn ensure_local_autogen_repository(data_dir: &Path, db: &MainDb) -> Result { - let repo_path = local_autogen_repo_path(data_dir); - ensure_repository_layout(&repo_path, &local_autogen_repo_toml())?; - db.upsert_repository( - &RepositoryMetadata { - id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), - name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), - priority: RepositoryPriority::LOCAL_AUTOGEN, - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_path), - None, - )?; - Ok(repo_path) -} - -fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> Result { - let local_id = RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id"); - if let Ok(existing) = find_repository(db, &local_id) { - let repo_path = repo_path(&existing)?; - ensure_repository_layout(&repo_path, &local_repo_toml())?; - return Ok(repo_path); - } - - let repo_path = data_dir.join("repositories").join(LOCAL_REPOSITORY_ID); - ensure_repository_layout(&repo_path, &local_repo_toml())?; - db.upsert_repository( - &RepositoryMetadata { - id: local_id, - name: LOCAL_REPOSITORY_NAME.to_owned(), - priority: RepositoryPriority::LOCAL, - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_path), - None, - )?; - Ok(repo_path) -} - -fn ensure_repository_layout(repo_path: &Path, repo_toml: &str) -> Result<(), CliError> { - fs::create_dir_all(repo_path.join("packages")).map_err(|source| { - CliError::Autogen(format!( - "failed to create repository packages dir '{}': {source}", - repo_path.display() - )) - })?; - fs::create_dir_all(repo_path.join("lib")).map_err(|source| { - CliError::Autogen(format!( - "failed to create repository lib dir '{}': {source}", - repo_path.display() - )) - })?; - fs::create_dir_all(repo_path.join("templates")).map_err(|source| { - CliError::Autogen(format!( - "failed to create repository templates dir '{}': {source}", - repo_path.display() - )) - })?; - let repo_toml_path = repo_path.join("repo.toml"); - if !repo_toml_path.exists() { - fs::write(&repo_toml_path, repo_toml).map_err(|source| { - CliError::Autogen(format!( - "failed to write repo.toml '{}': {source}", - repo_toml_path.display() - )) - })?; - } - Ok(()) -} - -fn preserve_autogen_file_in_local( - data_dir: &Path, - db: &MainDb, - package_id: &PackageId, - content: &str, -) -> Result { - let local_repo = ensure_local_repository(data_dir, db)?; - let primary_relative = getter_core::autogen::package_relative_path(package_id); - let primary_target = safe_join(&local_repo, &primary_relative)?; - let relative_path = if primary_target.exists() { - let backup = PathBuf::from("autogen-preserved") - .join(package_id.kind().as_str()) - .join(format!( - "{}.{}.lua", - package_id.name(), - content_hash(content).replace(':', "-") - )); - safe_join(&local_repo, &backup)?; - backup - } else { - primary_relative - }; - let target = safe_join(&local_repo, &relative_path)?; - if let Some(parent) = target.parent() { - fs::create_dir_all(parent).map_err(|source| { - CliError::Autogen(format!( - "failed to create local preservation directory '{}': {source}", - parent.display() - )) - })?; - } - fs::write(&target, content).map_err(|source| { - CliError::Autogen(format!( - "failed to preserve modified autogen file '{}': {source}", - target.display() - )) - })?; - Ok(json!({ - "package_id": package_id.to_string(), - "repository_id": LOCAL_REPOSITORY_ID, - "relative_path": relative_path, - })) -} - -fn read_autogen_manifest(repo_path: &Path) -> Result, CliError> { - let path = repo_path.join(AUTOGEN_MANIFEST_FILE); - if !path.exists() { - return Ok(None); - } - let bytes = fs::read(&path).map_err(|source| { - CliError::Autogen(format!( - "failed to read autogen manifest '{}': {source}", - path.display() - )) - })?; - serde_json::from_slice(&bytes) - .map(Some) - .map_err(|source| CliError::Autogen(format!("failed to parse autogen manifest: {source}"))) -} - -fn write_autogen_manifest(repo_path: &Path, manifest: &AutogenManifest) -> Result<(), CliError> { - fs::create_dir_all(repo_path).map_err(|source| { - CliError::Autogen(format!( - "failed to create autogen repository '{}': {source}", - repo_path.display() - )) - })?; - let path = repo_path.join(AUTOGEN_MANIFEST_FILE); - let bytes = serde_json::to_vec_pretty(manifest) - .map_err(|source| CliError::Autogen(format!("failed to serialize manifest: {source}")))?; - fs::write(&path, bytes).map_err(|source| { - CliError::Autogen(format!( - "failed to write autogen manifest '{}': {source}", - path.display() - )) - }) -} - -fn empty_autogen_manifest() -> AutogenManifest { - AutogenManifest { - version: getter_core::autogen::AUTOGEN_MANIFEST_VERSION, - repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), - packages: Vec::new(), - } -} - -fn upsert_manifest_entry(manifest: &mut AutogenManifest, entry: AutogenManifestEntry) { - manifest - .packages - .retain(|existing| existing.package_id != entry.package_id); - manifest.packages.push(entry); - manifest - .packages - .sort_by_key(|existing| existing.package_id.to_string()); -} - -fn safe_join(root: &Path, relative: &Path) -> Result { - if relative.is_absolute() - || relative.components().any(|component| { - matches!( - component, - Component::ParentDir | Component::RootDir | Component::Prefix(_) - ) - }) - { - return Err(CliError::Autogen(format!( - "unsafe relative path '{}'", - relative.display() - ))); - } - Ok(root.join(relative)) -} - -fn local_autogen_repo_path(data_dir: &Path) -> PathBuf { - data_dir - .join("repositories") - .join(LOCAL_AUTOGEN_REPOSITORY_ID) -} - fn repo_path(repo: &StoredRepository) -> Result { repo.path .as_ref() diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 47326c5..93325e9 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -3,8 +3,12 @@ name = "getter-core" version.workspace = true edition.workspace = true +[features] +default = ["lua"] +lua = ["dep:mlua"] + [dependencies] -mlua = { version = "0.10", features = ["lua54", "vendored"] } +mlua = { version = "0.10", features = ["lua54", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index aa3bb41..ce5c030 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -10,7 +10,9 @@ use std::fmt; use std::str::FromStr; pub mod autogen; +#[cfg(feature = "lua")] pub mod diagnostics; +#[cfg(feature = "lua")] pub mod lua; pub mod repository; pub mod task; diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml new file mode 100644 index 0000000..f24bcff --- /dev/null +++ b/crates/getter-operations/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "getter-operations" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core", default-features = false } +getter-storage = { path = "../getter-storage" } +serde_json = "1" +thiserror = "1" diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs new file mode 100644 index 0000000..caab4aa --- /dev/null +++ b/crates/getter-operations/src/autogen.rs @@ -0,0 +1,637 @@ +//! Getter-owned installed-inventory autogen operations. +//! +//! The CLI and native bridge both call this module so there is one implementation +//! of `local_autogen` preview/apply semantics. Platform layers provide installed +//! inventory facts; this module decides generated package ids, repository +//! coverage, file writes, manifest updates, preservation behavior, and tracked +//! state updates. + +use getter_core::autogen::{ + content_hash, local_autogen_repo_toml, local_repo_toml, plan_local_autogen, AutogenManifest, + AutogenManifestEntry, AutogenPlan, AutogenSkipReason, InstalledInventory, + LOCAL_AUTOGEN_REPOSITORY_ID, LOCAL_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, + LOCAL_REPOSITORY_NAME, +}; +use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; +use getter_core::{PackageId, RepositoryId, RepositoryPriority}; +use getter_storage::{MainDb, StorageError, StoredRepository}; +use serde_json::{json, Value}; +use std::collections::{BTreeSet, HashMap}; +use std::fs; +use std::path::{Component, Path, PathBuf}; + +pub const AUTOGEN_MANIFEST_FILE: &str = "autogen-manifest.json"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AutogenAcceptance { + AcceptAll, + Accept(Vec), +} + +#[derive(Debug, thiserror::Error)] +pub enum AutogenOperationError { + #[error("storage error: {0}")] + Storage(#[from] StorageError), + #[error("repository error: {0}")] + Repository(String), + #[error("autogen error: {0}")] + Autogen(String), +} + +pub type AutogenOperationResult = Result; + +pub fn build_local_autogen_plan( + db: &MainDb, + inventory: &InstalledInventory, +) -> AutogenOperationResult { + let covered = higher_priority_package_coverage(db)?; + plan_local_autogen(inventory, &covered) + .map_err(|source| AutogenOperationError::Autogen(source.to_string())) +} + +pub fn installed_preview_json(data_dir: &Path, plan: &AutogenPlan) -> Value { + let candidates: Vec = plan.candidates.iter().map(autogen_candidate_json).collect(); + let skipped: Vec = plan.skipped.iter().map(autogen_skip_json).collect(); + json!({ + "operation": "installed.preview", + "target_repo_id": plan.repository_id.as_str(), + "target_repo_path": local_autogen_repo_path(data_dir), + "summary": { + "candidate_count": candidates.len(), + "skipped_count": skipped.len(), + "write_count": candidates.len(), + "delete_count": 0, + }, + "candidates": candidates, + "skipped": skipped, + "diagnostics": [], + }) +} + +pub fn cleanup_preview_json( + data_dir: &Path, + db: &MainDb, + inventory: &InstalledInventory, +) -> AutogenOperationResult { + let repo_path = local_autogen_repo_path(data_dir); + let Some(manifest) = read_autogen_manifest(&repo_path)? else { + return Ok(json!({ + "operation": "cleanup.preview", + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "summary": { "candidate_count": 0, "skipped_count": 0, "write_count": 0, "delete_count": 0 }, + "candidates": [], + "skipped": [], + "diagnostics": [], + })); + }; + let plan = build_local_autogen_plan(db, inventory)?; + let installed_ids: BTreeSet = plan + .candidates + .iter() + .map(|candidate| candidate.package_id.to_string()) + .chain(plan.skipped.iter().map(|skip| skip.package_id.to_string())) + .collect(); + let mut candidates = Vec::new(); + for entry in manifest.packages { + if !installed_ids.contains(&entry.package_id.to_string()) { + candidates.push(json!({ + "package_id": entry.package_id.to_string(), + "action": "delete", + "output_relative_path": entry.relative_path, + "content_hash": entry.content_hash, + "reason": "not_in_installed_inventory", + })); + } + } + candidates.sort_by_key(|candidate| { + candidate + .get("package_id") + .and_then(Value::as_str) + .unwrap_or_default() + .to_owned() + }); + Ok(json!({ + "operation": "cleanup.preview", + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "summary": { + "candidate_count": candidates.len(), + "skipped_count": 0, + "write_count": 0, + "delete_count": candidates.len(), + }, + "candidates": candidates, + "skipped": [], + "diagnostics": [], + })) +} + +pub fn apply_installed_preview( + data_dir: &Path, + db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> AutogenOperationResult { + let repo_path = local_autogen_repo_path(data_dir); + ensure_local_autogen_repository(data_dir, db)?; + let accepted = accepted_preview_candidates(preview, acceptance)?; + let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut applied = Vec::new(); + let mut preserved = Vec::new(); + + for candidate in accepted { + let package_id = preview_package_id(candidate)?; + let relative_path = preview_relative_path(candidate)?; + let content = candidate + .get("content") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen("preview candidate missing content".to_owned()) + })?; + let expected_hash = candidate + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen("preview candidate missing content_hash".to_owned()) + })?; + if content_hash(content) != expected_hash { + return Err(AutogenOperationError::Autogen(format!( + "preview content hash mismatch for {package_id}" + ))); + } + let target = safe_join(&repo_path, &relative_path)?; + if target.exists() { + let current = fs::read_to_string(&target).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to read existing autogen file '{}': {source}", + target.display() + )) + })?; + let current_hash = content_hash(¤t); + let known_hash = manifest + .package(&package_id) + .map(|entry| entry.content_hash.as_str()); + if known_hash != Some(current_hash.as_str()) { + preserved.push(preserve_autogen_file_in_local( + data_dir, + db, + &package_id, + ¤t, + )?); + } + } + if let Some(parent) = target.parent() { + fs::create_dir_all(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create autogen package directory '{}': {source}", + parent.display() + )) + })?; + } + fs::write(&target, content).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to write autogen package '{}': {source}", + target.display() + )) + })?; + upsert_manifest_entry( + &mut manifest, + AutogenManifestEntry { + package_id: package_id.clone(), + relative_path: relative_path.clone(), + content_hash: expected_hash.to_owned(), + }, + ); + db.upsert_generated_tracked_package_preserving_user_state( + &package_id, + &RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + )?; + applied.push(json!({ + "package_id": package_id.to_string(), + "output_relative_path": relative_path, + })); + } + + write_autogen_manifest(&repo_path, &manifest)?; + Ok(json!({ + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "applied_count": applied.len(), + "applied": applied, + "preserved_to_local": preserved, + })) +} + +pub fn apply_cleanup_preview( + data_dir: &Path, + db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> AutogenOperationResult { + if preview.get("target_repo_id").and_then(Value::as_str) != Some(LOCAL_AUTOGEN_REPOSITORY_ID) { + return Err(AutogenOperationError::Autogen(format!( + "cleanup preview target_repo_id must be '{LOCAL_AUTOGEN_REPOSITORY_ID}'" + ))); + } + let repo_path = local_autogen_repo_path(data_dir); + let accepted = accepted_preview_candidates(preview, acceptance)?; + let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut deleted = Vec::new(); + let mut preserved = Vec::new(); + let local_autogen_id = RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"); + for candidate in accepted { + let package_id = preview_package_id(candidate)?; + let relative_path = preview_relative_path(candidate)?; + let expected_hash = candidate + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "cleanup preview candidate missing content_hash".to_owned(), + ) + })?; + let manifest_entry = manifest.package(&package_id).ok_or_else(|| { + AutogenOperationError::Autogen(format!( + "cleanup preview candidate {package_id} is not managed by local_autogen manifest" + )) + })?; + if manifest_entry.relative_path != relative_path + || manifest_entry.content_hash != expected_hash + { + return Err(AutogenOperationError::Autogen(format!( + "cleanup preview candidate {package_id} does not match local_autogen manifest" + ))); + } + let target = safe_join(&repo_path, &relative_path)?; + if target.exists() { + let current = fs::read_to_string(&target).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to read existing autogen file '{}': {source}", + target.display() + )) + })?; + if content_hash(¤t) != expected_hash { + preserved.push(preserve_autogen_file_in_local( + data_dir, + db, + &package_id, + ¤t, + )?); + } + fs::remove_file(&target).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to delete autogen package '{}': {source}", + target.display() + )) + })?; + } + manifest + .packages + .retain(|entry| entry.package_id != package_id); + db.delete_generated_tracked_package(&package_id, &local_autogen_id)?; + deleted.push(json!({ + "package_id": package_id.to_string(), + "output_relative_path": relative_path, + })); + } + write_autogen_manifest(&repo_path, &manifest)?; + Ok(json!({ + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "deleted_count": deleted.len(), + "deleted": deleted, + "preserved_to_local": preserved, + })) +} + +pub fn unwrap_preview_payload( + raw: Value, + expected_operation: &str, +) -> AutogenOperationResult { + let payload = if raw.get("ok").is_some() && raw.get("data").is_some() { + raw.get("data").cloned().unwrap_or(Value::Null) + } else { + raw + }; + if payload.get("operation").and_then(Value::as_str) != Some(expected_operation) { + return Err(AutogenOperationError::Autogen(format!( + "autogen preview operation must be '{expected_operation}'" + ))); + } + Ok(payload) +} + +pub fn local_autogen_repo_path(data_dir: &Path) -> PathBuf { + data_dir + .join("repositories") + .join(LOCAL_AUTOGEN_REPOSITORY_ID) +} + +fn higher_priority_package_coverage( + db: &MainDb, +) -> AutogenOperationResult> { + let mut covered = HashMap::new(); + for repo in db.repositories()? { + if repo.id.as_str() == LOCAL_AUTOGEN_REPOSITORY_ID { + continue; + } + if repo.priority <= RepositoryPriority::LOCAL_AUTOGEN { + continue; + } + let Some(path) = repo.path.as_ref() else { + continue; + }; + let layout = load_repository_layout(Path::new(path))?; + for package in layout.packages { + covered.entry(package.id).or_insert_with(|| repo.id.clone()); + } + } + Ok(covered) +} + +fn load_repository_layout(path: &Path) -> AutogenOperationResult { + RepositoryLayout::load(path) + .map_err(|source| AutogenOperationError::Repository(source.to_string())) +} + +fn autogen_candidate_json(candidate: &getter_core::autogen::AutogenCandidate) -> Value { + json!({ + "package_id": candidate.package_id.to_string(), + "kind": candidate.package_id.kind().as_str(), + "display_name": candidate.name, + "installed_target": candidate.installed, + "action": "create", + "output_relative_path": candidate.relative_path, + "content_hash": candidate.content_hash, + "content": candidate.content, + }) +} + +fn autogen_skip_json(skip: &getter_core::autogen::AutogenSkip) -> Value { + json!({ + "package_id": skip.package_id.to_string(), + "reason": match skip.reason { + AutogenSkipReason::DuplicateInventoryItem => "duplicate_inventory_item", + AutogenSkipReason::CoveredByHigherPriorityRepository => "covered_by_higher_priority_repo", + }, + "covering_repo_id": skip.repository_id.as_ref().map(RepositoryId::as_str), + }) +} + +fn accepted_preview_candidates<'a>( + preview: &'a Value, + acceptance: &AutogenAcceptance, +) -> AutogenOperationResult> { + let candidates = preview + .get("candidates") + .and_then(Value::as_array) + .ok_or_else(|| { + AutogenOperationError::Autogen("autogen preview missing candidates".to_owned()) + })?; + match acceptance { + AutogenAcceptance::AcceptAll => Ok(candidates.iter().collect()), + AutogenAcceptance::Accept(ids) => { + let accepted: BTreeSet = ids.iter().map(ToString::to_string).collect(); + Ok(candidates + .iter() + .filter(|candidate| { + candidate + .get("package_id") + .and_then(Value::as_str) + .is_some_and(|id| accepted.contains(id)) + }) + .collect()) + } + } +} + +fn preview_package_id(candidate: &Value) -> AutogenOperationResult { + candidate + .get("package_id") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen("preview candidate missing package_id".to_owned()) + })? + .parse() + .map_err(|source: getter_core::PackageIdError| { + AutogenOperationError::Autogen(source.to_string()) + }) +} + +fn preview_relative_path(candidate: &Value) -> AutogenOperationResult { + candidate + .get("output_relative_path") + .and_then(Value::as_str) + .map(PathBuf::from) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "preview candidate missing output_relative_path".to_owned(), + ) + }) +} + +fn ensure_local_autogen_repository( + data_dir: &Path, + db: &MainDb, +) -> AutogenOperationResult { + let repo_path = local_autogen_repo_path(data_dir); + ensure_repository_layout(&repo_path, &local_autogen_repo_toml())?; + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), + priority: RepositoryPriority::LOCAL_AUTOGEN, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + )?; + Ok(repo_path) +} + +fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> AutogenOperationResult { + let local_id = RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id"); + if let Ok(existing) = find_repository(db, &local_id) { + let repo_path = repo_path(&existing)?; + ensure_repository_layout(&repo_path, &local_repo_toml())?; + return Ok(repo_path); + } + + let repo_path = data_dir.join("repositories").join(LOCAL_REPOSITORY_ID); + ensure_repository_layout(&repo_path, &local_repo_toml())?; + db.upsert_repository( + &RepositoryMetadata { + id: local_id, + name: LOCAL_REPOSITORY_NAME.to_owned(), + priority: RepositoryPriority::LOCAL, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + )?; + Ok(repo_path) +} + +fn find_repository(db: &MainDb, id: &RepositoryId) -> AutogenOperationResult { + db.repositories()? + .into_iter() + .find(|repo| &repo.id == id) + .ok_or_else(|| { + AutogenOperationError::Repository(format!("repository '{id}' is not registered")) + }) +} + +fn repo_path(repo: &StoredRepository) -> AutogenOperationResult { + repo.path.as_ref().map(PathBuf::from).ok_or_else(|| { + AutogenOperationError::Repository(format!("repository '{}' has no path", repo.id)) + }) +} + +fn ensure_repository_layout(repo_path: &Path, repo_toml: &str) -> AutogenOperationResult<()> { + fs::create_dir_all(repo_path.join("packages")).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create repository packages dir '{}': {source}", + repo_path.display() + )) + })?; + fs::create_dir_all(repo_path.join("lib")).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create repository lib dir '{}': {source}", + repo_path.display() + )) + })?; + fs::create_dir_all(repo_path.join("templates")).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create repository templates dir '{}': {source}", + repo_path.display() + )) + })?; + let repo_toml_path = repo_path.join("repo.toml"); + if !repo_toml_path.exists() { + fs::write(&repo_toml_path, repo_toml).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to write repo.toml '{}': {source}", + repo_toml_path.display() + )) + })?; + } + Ok(()) +} + +fn preserve_autogen_file_in_local( + data_dir: &Path, + db: &MainDb, + package_id: &PackageId, + content: &str, +) -> AutogenOperationResult { + let local_repo = ensure_local_repository(data_dir, db)?; + let primary_relative = getter_core::autogen::package_relative_path(package_id); + let primary_target = safe_join(&local_repo, &primary_relative)?; + let relative_path = if primary_target.exists() { + let backup = PathBuf::from("autogen-preserved") + .join(package_id.kind().as_str()) + .join(format!( + "{}.{}.lua", + package_id.name(), + content_hash(content).replace(':', "-") + )); + safe_join(&local_repo, &backup)?; + backup + } else { + primary_relative + }; + let target = safe_join(&local_repo, &relative_path)?; + if let Some(parent) = target.parent() { + fs::create_dir_all(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create local preservation directory '{}': {source}", + parent.display() + )) + })?; + } + fs::write(&target, content).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to preserve modified autogen file '{}': {source}", + target.display() + )) + })?; + Ok(json!({ + "package_id": package_id.to_string(), + "repository_id": LOCAL_REPOSITORY_ID, + "relative_path": relative_path, + })) +} + +fn read_autogen_manifest(repo_path: &Path) -> AutogenOperationResult> { + let path = repo_path.join(AUTOGEN_MANIFEST_FILE); + if !path.exists() { + return Ok(None); + } + let bytes = fs::read(&path).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to read autogen manifest '{}': {source}", + path.display() + )) + })?; + serde_json::from_slice(&bytes).map(Some).map_err(|source| { + AutogenOperationError::Autogen(format!("failed to parse autogen manifest: {source}")) + }) +} + +fn write_autogen_manifest( + repo_path: &Path, + manifest: &AutogenManifest, +) -> AutogenOperationResult<()> { + fs::create_dir_all(repo_path).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create autogen repository '{}': {source}", + repo_path.display() + )) + })?; + let path = repo_path.join(AUTOGEN_MANIFEST_FILE); + let bytes = serde_json::to_vec_pretty(manifest).map_err(|source| { + AutogenOperationError::Autogen(format!("failed to serialize manifest: {source}")) + })?; + fs::write(&path, bytes).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to write autogen manifest '{}': {source}", + path.display() + )) + }) +} + +fn empty_autogen_manifest() -> AutogenManifest { + AutogenManifest { + version: getter_core::autogen::AUTOGEN_MANIFEST_VERSION, + repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + packages: Vec::new(), + } +} + +fn upsert_manifest_entry(manifest: &mut AutogenManifest, entry: AutogenManifestEntry) { + manifest + .packages + .retain(|existing| existing.package_id != entry.package_id); + manifest.packages.push(entry); + manifest + .packages + .sort_by_key(|existing| existing.package_id.to_string()); +} + +fn safe_join(root: &Path, relative: &Path) -> AutogenOperationResult { + if relative.is_absolute() + || relative.components().any(|component| { + matches!( + component, + Component::ParentDir | Component::RootDir | Component::Prefix(_) + ) + }) + { + return Err(AutogenOperationError::Autogen(format!( + "unsafe relative path '{}'", + relative.display() + ))); + } + Ok(root.join(relative)) +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs new file mode 100644 index 0000000..f4432e2 --- /dev/null +++ b/crates/getter-operations/src/lib.rs @@ -0,0 +1,8 @@ +//! Reusable getter-owned operations shared by CLI and native embedders. +//! +//! This crate intentionally contains product/domain orchestration that must not +//! be duplicated in Flutter or Android/Kotlin bridge glue. Platform adapters may +//! provide raw facts to callers, but the rules for repository coverage, +//! generated package files, manifests, and tracked state live here. + +pub mod autogen; diff --git a/crates/getter-storage/Cargo.toml b/crates/getter-storage/Cargo.toml index bc0964b..da9172d 100644 --- a/crates/getter-storage/Cargo.toml +++ b/crates/getter-storage/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true [dependencies] -getter-core = { path = "../getter-core" } +getter-core = { path = "../getter-core", default-features = false } rusqlite = { version = "0.32", features = ["bundled"] } serde_json = "1" thiserror = "1" diff --git a/src/lib.rs b/src/lib.rs index 607df46..d29d0ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,8 @@ #[cfg(feature = "domain")] pub use getter_core as core; #[cfg(feature = "domain")] +pub use getter_operations as operations; +#[cfg(feature = "domain")] pub use getter_storage as storage; #[cfg(feature = "rustls-platform-verifier")] From 21dc994f5d452238a0b5fda3ecf9cd09e541155a Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Wed, 24 Jun 2026 10:17:21 +0800 Subject: [PATCH 18/72] feat: share legacy room operations --- Cargo.lock | 1 + crates/getter-cli/src/lib.rs | 208 +--------- crates/getter-operations/Cargo.toml | 1 + crates/getter-operations/src/legacy_room.rs | 426 ++++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + 5 files changed, 448 insertions(+), 189 deletions(-) create mode 100644 crates/getter-operations/src/legacy_room.rs diff --git a/Cargo.lock b/Cargo.lock index 1ff310d..fce5fc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -566,6 +566,7 @@ version = "0.1.0" dependencies = [ "getter-core", "getter-storage", + "serde", "serde_json", "thiserror 1.0.69", ] diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index d9f4d47..3562bf1 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -18,10 +18,9 @@ use getter_downloader::{ cancel_download_task, record_install_result, run_fake_download_task, submit_fake_download_task, }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; +use getter_operations::legacy_room::{self, LegacyRoomOperationError}; use getter_storage::legacy_room::{ - map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, - LegacyExtraAppRecord, LegacyPackageResolution, LegacyRoomDbImport, LegacyRoomImportWarning, - LegacyRoomReadError, + map_legacy_app, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, }; use getter_storage::{ CacheDb, MainDb, MigrationRecordUpsert, StorageError, StoredPackageResolution, @@ -250,6 +249,20 @@ impl From for CliError { } } +impl From for CliError { + fn from(value: LegacyRoomOperationError) -> Self { + match value { + LegacyRoomOperationError::Storage(detail) => Self::Storage(detail), + LegacyRoomOperationError::UnsupportedDb { report_path } => { + Self::UnsupportedLegacyDb { report_path } + } + LegacyRoomOperationError::InvalidDb { report_path } => { + Self::InvalidLegacyDb { report_path } + } + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliOutput { pub exit_code: ExitCode, @@ -705,64 +718,11 @@ fn execute(invocation: CliInvocation) -> Result { })) } CliCommand::LegacyImportRoomDb { db: legacy_db } => { - let db = open_main_db(&invocation.data_dir)?; - if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { - let records = db.tracked_packages()?; - return Ok(json!({ - "already_imported": true, - "imported_records": 0, - "source_counts": { - "app_rows": 0, - "extra_app_rows": 0, - "hub_rows": 0, - "extra_hub_rows": 0, - }, - "warnings": [], - "apps": tracked_packages_json(records), - })); - } - let import = read_legacy_room_database(&legacy_db).map_err(|source| { - legacy_db_import_error(&invocation.data_dir, &legacy_db, source) - })?; - let imported_records = import.apps.len(); - let source_counts = source_counts_json(&import); - let warnings = import_warnings_json(&import.warnings); - if import.source_counts.app_rows > 0 && imported_records == 0 { - let report_path = create_migration_report_with_source_counts( - &invocation.data_dir, - &legacy_db, - "migration.invalid_db", - "Legacy Room database has app rows but no importable app rows", - 0, - 0, - &warnings, - Some(&source_counts), - )?; - return Err(CliError::InvalidLegacyDb { report_path }); - } - import_legacy_room_db(&db, &import)?; - let report_path = create_migration_report_with_source_counts( - &invocation.data_dir, - &legacy_db, - "migration.imported", - "Legacy Room database imported", - imported_records as u64, - imported_records as u64, - &warnings, - Some(&source_counts), - )?; - let records = db.tracked_packages()?; - Ok(json!({ - "report_path": report_path, - "imported_records": imported_records, - "source_counts": source_counts, - "warnings": warnings, - "apps": tracked_packages_json(records), - })) + legacy_room::import_room_db_json(&invocation.data_dir, &legacy_db) + .map_err(CliError::from) } CliCommand::LegacyReportList => { - open_initialized_storage(&invocation.data_dir)?; - Ok(json!({ "reports": list_migration_reports(&invocation.data_dir)? })) + legacy_room::report_list_json(&invocation.data_dir).map_err(CliError::from) } } } @@ -1053,34 +1013,6 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( Ok(()) } -fn import_legacy_room_db(db: &MainDb, import: &LegacyRoomDbImport) -> Result<(), CliError> { - let mut packages = Vec::new(); - for app in &import.apps { - let mapping = map_legacy_app(&app.app, Some(&app.user_state)) - .map_err(|source| CliError::Storage(source.to_string()))?; - packages.push(tracked_package_upsert(mapping)); - } - - let report_json = json!({ - "ok": true, - "source": "legacy-room-db", - "version": import.version, - "imported_records": import.apps.len(), - "source_counts": source_counts_json(import), - "warnings": import_warnings_json(&import.warnings), - }) - .to_string(); - db.import_tracked_packages_with_migration_record( - &packages, - &MigrationRecordUpsert { - id: LEGACY_ROOM_MIGRATION_ID, - source: "legacy-room-db", - report_json: &report_json, - }, - )?; - Ok(()) -} - fn tracked_package_upsert( mapping: getter_storage::legacy_room::LegacyAppMapping, ) -> TrackedPackageUpsert { @@ -1108,54 +1040,6 @@ fn stored_resolution(resolution: LegacyPackageResolution) -> StoredPackageResolu } } -fn legacy_db_import_error( - data_dir: &Path, - legacy_db: &Path, - source: LegacyRoomReadError, -) -> CliError { - let (code, unsupported) = match source { - LegacyRoomReadError::UnsupportedVersion { .. } => ("migration.unsupported_db", true), - LegacyRoomReadError::Sqlite(_) | LegacyRoomReadError::MissingRequiredTable(_) => { - ("migration.invalid_db", false) - } - }; - match create_migration_report_with_source_counts( - data_dir, - legacy_db, - code, - &source.to_string(), - 0, - 0, - &[], - None, - ) { - Ok(report_path) if unsupported => CliError::UnsupportedLegacyDb { report_path }, - Ok(report_path) => CliError::InvalidLegacyDb { report_path }, - Err(error) => error, - } -} - -fn source_counts_json(import: &LegacyRoomDbImport) -> Value { - json!({ - "app_rows": import.source_counts.app_rows, - "extra_app_rows": import.source_counts.extra_app_rows, - "hub_rows": import.source_counts.hub_rows, - "extra_hub_rows": import.source_counts.extra_hub_rows, - }) -} - -fn import_warnings_json(warnings: &[LegacyRoomImportWarning]) -> Vec { - warnings - .iter() - .map(|warning| { - json!({ - "code": warning.code(), - "message": warning.message(), - }) - }) - .collect() -} - fn main_db_path(data_dir: &Path) -> PathBuf { data_dir.join(MAIN_DB_FILE) } @@ -1232,60 +1116,6 @@ fn report_file_name(code: &str) -> String { format!("{}.json", code.replace('.', "-")) } -fn list_migration_reports(data_dir: &Path) -> Result, CliError> { - let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); - if !reports_dir.exists() { - return Ok(Vec::new()); - } - - let mut report_paths = fs::read_dir(&reports_dir) - .map_err(|source| CliError::Storage(format!("failed to read migration reports: {source}")))? - .map(|entry| entry.map(|entry| entry.path())) - .collect::, _>>() - .map_err(|source| { - CliError::Storage(format!("failed to read migration report entry: {source}")) - })?; - report_paths.retain(|path| { - path.extension() - .is_some_and(|extension| extension == "json") - }); - report_paths.sort(); - - report_paths - .into_iter() - .map(|path| { - let bytes = fs::read(&path).map_err(|source| { - CliError::Storage(format!( - "failed to read migration report '{}': {source}", - path.display() - )) - })?; - let report: Value = serde_json::from_slice(&bytes).map_err(|source| { - CliError::Storage(format!( - "failed to parse migration report '{}': {source}", - path.display() - )) - })?; - Ok(json!({ - "ok": report.get("ok").and_then(Value::as_bool).unwrap_or(false), - "code": report.get("code").and_then(Value::as_str).unwrap_or("migration.unknown"), - "message": report.get("message").and_then(Value::as_str).unwrap_or("Legacy migration report"), - "source_file_name": report - .get("source_file_name") - .or_else(|| report.get("bundle_file_name")) - .and_then(Value::as_str), - "imported_records": report.get("imported_records").and_then(Value::as_u64).unwrap_or(0), - "tracked_records": report.get("tracked_records").and_then(Value::as_u64).unwrap_or(0), - "warnings": report - .get("warnings") - .cloned() - .unwrap_or_else(|| Value::Array(Vec::new())), - "source_counts": report.get("source_counts").cloned().unwrap_or(Value::Null), - })) - }) - .collect() -} - #[derive(Debug, Serialize)] struct MigrationReport<'a> { ok: bool, diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index f24bcff..a44e0b6 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -6,5 +6,6 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core", default-features = false } getter-storage = { path = "../getter-storage" } +serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" diff --git a/crates/getter-operations/src/legacy_room.rs b/crates/getter-operations/src/legacy_room.rs new file mode 100644 index 0000000..40c7c34 --- /dev/null +++ b/crates/getter-operations/src/legacy_room.rs @@ -0,0 +1,426 @@ +//! Getter-owned legacy Room migration operations. +//! +//! Platform layers may prepare/copy/checkpoint a legacy SQLite database and pass +//! its path to getter, but this module owns reading Room rows, mapping legacy app +//! records, writing tracked package state, recording migration completion, and +//! producing sanitized migration reports. + +use getter_storage::legacy_room::{ + map_legacy_app, read_legacy_room_database, LegacyPackageResolution, LegacyRoomDbImport, + LegacyRoomImportWarning, LegacyRoomReadError, +}; +use getter_storage::{ + CacheDb, MainDb, MigrationRecordUpsert, StorageError, StoredPackageResolution, + StoredTrackedPackage, TrackedPackageUpsert, +}; +use serde::Serialize; +use serde_json::{json, Value}; +use std::fs; +use std::path::{Path, PathBuf}; + +const MAIN_DB_FILE: &str = "main.db"; +const CACHE_DB_FILE: &str = "cache.db"; +pub const MIGRATION_REPORTS_DIR: &str = "migration-reports"; +pub const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; + +#[derive(Debug, thiserror::Error)] +pub enum LegacyRoomOperationError { + #[error("storage error: {0}")] + Storage(String), + #[error("unsupported legacy Room database")] + UnsupportedDb { report_path: PathBuf }, + #[error("invalid legacy Room database")] + InvalidDb { report_path: PathBuf }, +} + +impl LegacyRoomOperationError { + pub fn code(&self) -> &'static str { + match self { + Self::Storage(_) => "storage.error", + Self::UnsupportedDb { .. } => "migration.unsupported_db", + Self::InvalidDb { .. } => "migration.invalid_db", + } + } + + pub fn message(&self) -> &'static str { + match self { + Self::Storage(_) => "Getter storage operation failed", + Self::UnsupportedDb { .. } => "Legacy Room database version is not supported", + Self::InvalidDb { .. } => "Legacy Room database is invalid", + } + } + + pub fn detail(&self) -> Option { + match self { + Self::Storage(detail) => Some(detail.clone()), + Self::UnsupportedDb { .. } | Self::InvalidDb { .. } => None, + } + } + + pub fn report_path(&self) -> Option<&Path> { + match self { + Self::UnsupportedDb { report_path } | Self::InvalidDb { report_path } => { + Some(report_path.as_path()) + } + Self::Storage(_) => None, + } + } +} + +impl From for LegacyRoomOperationError { + fn from(source: StorageError) -> Self { + Self::Storage(source.to_string()) + } +} + +pub type LegacyRoomOperationResult = Result; + +pub fn import_room_db_json(data_dir: &Path, legacy_db: &Path) -> LegacyRoomOperationResult { + let db = open_main_db(data_dir)?; + if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { + let records = db.tracked_packages()?; + return Ok(json!({ + "already_imported": true, + "imported_records": 0, + "source_counts": { + "app_rows": 0, + "extra_app_rows": 0, + "hub_rows": 0, + "extra_hub_rows": 0, + }, + "warnings": [], + "apps": tracked_packages_json(records), + })); + } + + let import = read_legacy_room_database(legacy_db) + .map_err(|source| legacy_db_import_error(data_dir, legacy_db, source))?; + let imported_records = import.apps.len(); + let source_counts = source_counts_json(&import); + let warnings = import_warnings_json(&import.warnings); + if import.source_counts.app_rows > 0 && imported_records == 0 { + let report_path = create_migration_report_with_source_counts( + data_dir, + legacy_db, + "migration.invalid_db", + "Legacy Room database has app rows but no importable app rows", + 0, + 0, + &warnings, + Some(&source_counts), + )?; + return Err(LegacyRoomOperationError::InvalidDb { report_path }); + } + + import_legacy_room_db(&db, &import)?; + let report_path = create_migration_report_with_source_counts( + data_dir, + legacy_db, + "migration.imported", + "Legacy Room database imported", + imported_records as u64, + imported_records as u64, + &warnings, + Some(&source_counts), + )?; + let records = db.tracked_packages()?; + Ok(json!({ + "report_path": report_path, + "imported_records": imported_records, + "source_counts": source_counts, + "warnings": warnings, + "apps": tracked_packages_json(records), + })) +} + +pub fn report_list_json(data_dir: &Path) -> LegacyRoomOperationResult { + open_initialized_storage(data_dir)?; + Ok(json!({ "reports": list_migration_reports(data_dir)? })) +} + +fn initialize_storage(data_dir: &Path) -> LegacyRoomOperationResult<()> { + fs::create_dir_all(data_dir).map_err(|source| { + LegacyRoomOperationError::Storage(format!("failed to create data directory: {source}")) + })?; + MainDb::open(data_dir.join(MAIN_DB_FILE))?; + CacheDb::open(data_dir.join(CACHE_DB_FILE))?; + Ok(()) +} + +fn open_initialized_storage(data_dir: &Path) -> LegacyRoomOperationResult<()> { + initialize_storage(data_dir) +} + +fn open_main_db(data_dir: &Path) -> LegacyRoomOperationResult { + initialize_storage(data_dir)?; + Ok(MainDb::open(data_dir.join(MAIN_DB_FILE))?) +} + +fn import_legacy_room_db( + db: &MainDb, + import: &LegacyRoomDbImport, +) -> LegacyRoomOperationResult<()> { + let mut packages = Vec::new(); + for app in &import.apps { + let mapping = map_legacy_app(&app.app, Some(&app.user_state)) + .map_err(|source| LegacyRoomOperationError::Storage(source.to_string()))?; + packages.push(tracked_package_upsert(mapping)); + } + + let report_json = json!({ + "ok": true, + "source": "legacy-room-db", + "version": import.version, + "imported_records": import.apps.len(), + "source_counts": source_counts_json(import), + "warnings": import_warnings_json(&import.warnings), + }) + .to_string(); + db.import_tracked_packages_with_migration_record( + &packages, + &MigrationRecordUpsert { + id: LEGACY_ROOM_MIGRATION_ID, + source: "legacy-room-db", + report_json: &report_json, + }, + )?; + Ok(()) +} + +fn tracked_package_upsert( + mapping: getter_storage::legacy_room::LegacyAppMapping, +) -> TrackedPackageUpsert { + TrackedPackageUpsert { + package_id: mapping.package_id, + enabled: true, + favorite: mapping.user_state.favorite, + ignored_version: mapping.user_state.ignored_version, + repository_id: None, + package_resolution: stored_resolution(mapping.package_resolution), + } +} + +fn stored_resolution(resolution: LegacyPackageResolution) -> StoredPackageResolution { + match resolution { + LegacyPackageResolution::OfficialRepositoryPackage => { + StoredPackageResolution::OfficialRepositoryPackage + } + LegacyPackageResolution::GenerateLocalPackage => { + StoredPackageResolution::GenerateLocalPackage + } + LegacyPackageResolution::MissingPackageDefinition => { + StoredPackageResolution::MissingPackageDefinition + } + } +} + +fn legacy_db_import_error( + data_dir: &Path, + legacy_db: &Path, + source: LegacyRoomReadError, +) -> LegacyRoomOperationError { + let (code, unsupported) = match source { + LegacyRoomReadError::UnsupportedVersion { .. } => ("migration.unsupported_db", true), + LegacyRoomReadError::Sqlite(_) | LegacyRoomReadError::MissingRequiredTable(_) => { + ("migration.invalid_db", false) + } + }; + match create_migration_report_with_source_counts( + data_dir, + legacy_db, + code, + &source.to_string(), + 0, + 0, + &[], + None, + ) { + Ok(report_path) if unsupported => LegacyRoomOperationError::UnsupportedDb { report_path }, + Ok(report_path) => LegacyRoomOperationError::InvalidDb { report_path }, + Err(error) => error, + } +} + +fn tracked_packages_json(packages: Vec) -> Vec { + packages + .into_iter() + .map(|package| { + json!({ + "id": package.package_id.to_string(), + "enabled": package.enabled, + "favorite": package.favorite, + "ignored_version": package.ignored_version, + "repository_id": package.repository_id.map(|id| id.to_string()), + "package_resolution": package.package_resolution.as_str(), + }) + }) + .collect() +} + +fn source_counts_json(import: &LegacyRoomDbImport) -> Value { + json!({ + "app_rows": import.source_counts.app_rows, + "extra_app_rows": import.source_counts.extra_app_rows, + "hub_rows": import.source_counts.hub_rows, + "extra_hub_rows": import.source_counts.extra_hub_rows, + }) +} + +fn import_warnings_json(warnings: &[LegacyRoomImportWarning]) -> Vec { + warnings + .iter() + .map(|warning| { + json!({ + "code": warning.code(), + "message": warning.message(), + }) + }) + .collect() +} + +#[allow(clippy::too_many_arguments)] +fn create_migration_report_with_source_counts( + data_dir: &Path, + source_file: &Path, + code: &str, + detail: &str, + imported_records: u64, + tracked_records: u64, + warnings: &[Value], + source_counts: Option<&Value>, +) -> LegacyRoomOperationResult { + let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); + fs::create_dir_all(&reports_dir).map_err(|source| { + LegacyRoomOperationError::Storage(format!( + "failed to create migration report directory: {source}" + )) + })?; + let report_path = reports_dir.join(report_file_name(code)); + let report = MigrationReport { + ok: code == "migration.imported", + code, + message: match code { + "migration.invalid_db" => "Legacy Room database is invalid", + "migration.unsupported_db" => "Legacy Room database version is not supported", + "migration.imported" => "Legacy Room data imported", + _ => "Legacy migration failed", + }, + source_file_name: source_file.file_name().and_then(|name| name.to_str()), + detail, + imported_records, + tracked_records, + warnings, + source_counts, + }; + let bytes = serde_json::to_vec_pretty(&report).map_err(|source| { + LegacyRoomOperationError::Storage(format!("failed to serialize report: {source}")) + })?; + fs::write(&report_path, bytes).map_err(|source| { + LegacyRoomOperationError::Storage(format!("failed to write report: {source}")) + })?; + Ok(report_path) +} + +fn report_file_name(code: &str) -> String { + format!("{}.json", code.replace('.', "-")) +} + +fn list_migration_reports(data_dir: &Path) -> LegacyRoomOperationResult> { + let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); + if !reports_dir.exists() { + return Ok(Vec::new()); + } + + let mut report_paths = fs::read_dir(&reports_dir) + .map_err(|source| { + LegacyRoomOperationError::Storage(format!("failed to read migration reports: {source}")) + })? + .map(|entry| entry.map(|entry| entry.path())) + .collect::, _>>() + .map_err(|source| { + LegacyRoomOperationError::Storage(format!( + "failed to read migration report entry: {source}" + )) + })?; + report_paths.retain(|path| { + path.extension() + .is_some_and(|extension| extension == "json") + }); + report_paths.sort(); + + report_paths + .into_iter() + .map(|path| { + let bytes = fs::read(&path).map_err(|source| { + LegacyRoomOperationError::Storage(format!( + "failed to read migration report '{}': {source}", + path.display() + )) + })?; + let report: Value = serde_json::from_slice(&bytes).map_err(|source| { + LegacyRoomOperationError::Storage(format!( + "failed to parse migration report '{}': {source}", + path.display() + )) + })?; + Ok(json!({ + "ok": report.get("ok").and_then(Value::as_bool).unwrap_or(false), + "code": report.get("code").and_then(Value::as_str).unwrap_or("migration.unknown"), + "message": report.get("message").and_then(Value::as_str).unwrap_or("Legacy migration report"), + "source_file_name": report + .get("source_file_name") + .or_else(|| report.get("bundle_file_name")) + .and_then(Value::as_str), + "imported_records": report.get("imported_records").and_then(Value::as_u64).unwrap_or(0), + "tracked_records": report.get("tracked_records").and_then(Value::as_u64).unwrap_or(0), + "warnings": report + .get("warnings") + .cloned() + .unwrap_or_else(|| Value::Array(Vec::new())), + "source_counts": report.get("source_counts").cloned().unwrap_or(Value::Null), + })) + }) + .collect() +} + +#[derive(Debug, Serialize)] +struct MigrationReport<'a> { + ok: bool, + code: &'a str, + message: &'a str, + source_file_name: Option<&'a str>, + detail: &'a str, + imported_records: u64, + tracked_records: u64, + warnings: &'a [Value], + #[serde(skip_serializing_if = "Option::is_none")] + source_counts: Option<&'a Value>, +} + +#[cfg(test)] +mod tests { + use super::*; + use std::time::{SystemTime, UNIX_EPOCH}; + + #[test] + fn import_room_db_rejects_malformed_db_with_report() { + let temp = temp_dir("malformed"); + fs::create_dir_all(&temp).unwrap(); + let db_path = temp.join("legacy.db"); + fs::write(&db_path, b"not sqlite").unwrap(); + + let error = import_room_db_json(&temp.join("getter"), &db_path).unwrap_err(); + + assert!(matches!(error, LegacyRoomOperationError::InvalidDb { .. })); + assert_eq!(error.code(), "migration.invalid_db"); + assert!(error.report_path().is_some_and(Path::exists)); + } + + fn temp_dir(name: &str) -> PathBuf { + let nanos = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + std::env::temp_dir().join(format!("upgradeall-legacy-room-op-{name}-{nanos}")) + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index f4432e2..71a0ca1 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -6,3 +6,4 @@ //! generated package files, manifests, and tracked state live here. pub mod autogen; +pub mod legacy_room; From 8e3e3c12803f1f8c2659361bfeba78c666917772 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 10:20:45 +0800 Subject: [PATCH 19/72] feat(core): add in-memory task runtime --- crates/getter-core/src/autogen.rs | 4 + crates/getter-core/src/lib.rs | 1 + crates/getter-core/src/runtime.rs | 936 ++++++++++++++++++++++++++++++ 3 files changed, 941 insertions(+) create mode 100644 crates/getter-core/src/runtime.rs diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index 265c6be..2001ac5 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -369,8 +369,11 @@ fn fnv1a64(bytes: &[u8]) -> u64 { #[cfg(test)] mod tests { use super::*; + #[cfg(feature = "lua")] use crate::lua::evaluate_package_file; + #[cfg(feature = "lua")] use crate::repository::RepositoryLayout; + #[cfg(feature = "lua")] use std::fs; #[test] @@ -492,6 +495,7 @@ mod tests { ); } + #[cfg(feature = "lua")] #[test] fn generated_lua_evaluates_through_package_boundary() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index ce5c030..96cdb95 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -15,6 +15,7 @@ pub mod diagnostics; #[cfg(feature = "lua")] pub mod lua; pub mod repository; +pub mod runtime; pub mod task; pub mod update; diff --git a/crates/getter-core/src/runtime.rs b/crates/getter-core/src/runtime.rs new file mode 100644 index 0000000..3dd4b7c --- /dev/null +++ b/crates/getter-core/src/runtime.rs @@ -0,0 +1,936 @@ +//! In-memory Phase D runtime for getter-owned update/download/install tasks. +//! +//! This module implements the ADR-0011 task/action/runtime-notification shape. +//! It deliberately keeps task state in the current process only: no SQLite task +//! persistence, no daemon, no recovery, and no replayable event log. Product +//! embedders can hold this runtime as a process-lifetime singleton and expose +//! [`RuntimeNotification`] through a push stream. + +use crate::{PackageId, UpdateAction}; +use serde::{Deserialize, Serialize}; +use std::collections::{BTreeMap, HashMap}; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PackageVersionLuaObject { + pub object_id: String, + pub dependency_digest: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SealedActionPlan { + pub package_id: PackageId, + #[serde(default)] + pub actions: Vec, + pub lua_object: PackageVersionLuaObject, +} + +impl SealedActionPlan { + fn has_install_action(&self) -> bool { + self.actions + .iter() + .any(|action| matches!(action, UpdateAction::Install { .. })) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct IssuedAction { + pub action_id: String, + pub package_id: PackageId, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskSnapshot { + pub task_id: String, + pub package_id: PackageId, + pub status: RuntimeTaskStatus, + pub phase: TaskPhase, + #[serde(default)] + pub progress: Option, + pub capabilities: TaskCapabilities, + #[serde(default)] + pub current_diagnostic: Option, + pub updated_at: u64, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum RuntimeTaskStatus { + Queued, + Running, + Paused, + Failed, + Completed, + Canceled, +} + +impl RuntimeTaskStatus { + pub const fn is_active(self) -> bool { + matches!(self, Self::Queued | Self::Running | Self::Paused) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskPhase { + pub category: TaskPhaseCategory, + #[serde(default)] + pub reason: Option, +} + +impl TaskPhase { + pub const fn new(category: TaskPhaseCategory) -> Self { + Self { + category, + reason: None, + } + } + + pub const fn with_reason(category: TaskPhaseCategory, reason: TaskPhaseReason) -> Self { + Self { + category, + reason: Some(reason), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TaskPhaseCategory { + Queued, + Download, + WaitingUser, + Install, + Completed, + Failed, + Canceled, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TaskPhaseReason { + InstallHandoff, + PackageLocked, + UserRejected, + DownloadFailed, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskProgress { + pub unit: TaskProgressUnit, + pub current: u64, + #[serde(default)] + pub total: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TaskProgressUnit { + Percent, + Bit, +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskCapabilities { + pub cancel: bool, + pub pause: bool, + pub resume: bool, + pub retry: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskDiagnostic { + pub code: String, + pub message: String, + pub severity: DiagnosticSeverity, +} + +impl TaskDiagnostic { + pub fn error(code: impl Into, message: impl Into) -> Self { + Self { + code: code.into(), + message: message.into(), + severity: DiagnosticSeverity::Error, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DiagnosticSeverity { + Info, + Warning, + Error, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum RuntimeNotification { + TaskChanged { task: TaskSnapshot }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum UserResult { + Accepted, + Rejected, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TaskCleanMode { + /// Remove completed and canceled tasks only. + Default, + /// Remove failed tasks only. + Failed, + /// Remove completed, canceled, and failed tasks. + AllInactive, +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum RuntimeError { + #[error("update action is no longer available: {0}")] + ActionNotFound(String), + #[error("task not found: {0}")] + TaskNotFound(String), + #[error("task is not waiting for a user result: {0}")] + TaskNotWaitingForUser(String), + #[error("task cannot be paused in its current state: {0}")] + PauseNotSupported(String), + #[error("task cannot be resumed in its current state: {0}")] + ResumeNotSupported(String), + #[error("task cannot be retried in its current state: {0}")] + RetryNotSupported(String), + #[error("task cannot be canceled in its current state: {0}")] + CancelNotSupported(String), + #[error("task is active and must be canceled before removal: {0}")] + TaskActive(String), +} + +impl RuntimeError { + pub const fn code(&self) -> &'static str { + match self { + Self::ActionNotFound(_) => "action.not_found", + Self::TaskNotFound(_) => "task.not_found", + Self::TaskNotWaitingForUser(_) => "task.not_waiting_for_user", + Self::PauseNotSupported(_) => "task.pause_not_supported", + Self::ResumeNotSupported(_) => "task.resume_not_supported", + Self::RetryNotSupported(_) => "task.retry_not_supported", + Self::CancelNotSupported(_) => "task.cancel_not_supported", + Self::TaskActive(_) => "task.active", + } + } +} + +type RuntimeNotificationSink = Box; + +#[derive(Default)] +pub struct GetterRuntime { + next_action_id: u64, + next_task_id: u64, + logical_clock: u64, + actions: HashMap, + tasks: BTreeMap, + package_locks: HashMap, + notification_sink: Option, +} + +impl GetterRuntime { + pub fn new() -> Self { + Self::default() + } + + pub fn set_notification_sink( + &mut self, + sink: impl FnMut(RuntimeNotification) + Send + 'static, + ) { + self.notification_sink = Some(Box::new(sink)); + } + + pub fn issue_action(&mut self, plan: SealedActionPlan) -> IssuedAction { + self.next_action_id += 1; + let action_id = format!("action-{}", self.next_action_id); + let package_id = plan.package_id.clone(); + self.actions.insert(action_id.clone(), plan); + IssuedAction { + action_id, + package_id, + } + } + + pub fn submit_action(&mut self, action_id: &str) -> Result { + let plan = self + .actions + .remove(action_id) + .ok_or_else(|| RuntimeError::ActionNotFound(action_id.to_owned()))?; + self.next_task_id += 1; + let task_id = format!("task-{}", self.next_task_id); + let task = RuntimeTask::new(task_id.clone(), plan, self.tick()); + let snapshot = task.snapshot(); + self.tasks.insert(task_id, task); + self.notify_task_changed(snapshot.clone()); + Ok(snapshot) + } + + pub fn task(&self, task_id: &str) -> Result { + Ok(self.task_ref(task_id)?.snapshot()) + } + + pub fn tasks(&self) -> Vec { + self.tasks.values().map(RuntimeTask::snapshot).collect() + } + + pub fn active_tasks(&self) -> Vec { + self.tasks + .values() + .filter(|task| task.status.is_active()) + .map(RuntimeTask::snapshot) + .collect() + } + + pub fn tasks_for_package(&self, package_id: &PackageId) -> Vec { + self.tasks + .values() + .filter(|task| &task.plan.package_id == package_id) + .map(RuntimeTask::snapshot) + .collect() + } + + pub fn start_task(&mut self, task_id: &str) -> Result { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + match task.status { + RuntimeTaskStatus::Queued => task.start_download(clock), + RuntimeTaskStatus::Running => {} + _ => return Err(RuntimeError::PauseNotSupported(task_id.to_owned())), + } + } + self.snapshot_and_notify(task_id) + } + + pub fn set_download_progress( + &mut self, + task_id: &str, + current_bits: u64, + total_bits: Option, + ) -> Result { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + if !task.is_running_download() { + return Err(RuntimeError::PauseNotSupported(task_id.to_owned())); + } + task.progress = Some(TaskProgress { + unit: TaskProgressUnit::Bit, + current: current_bits, + total: total_bits, + }); + task.updated_at = clock; + } + self.snapshot_and_notify(task_id) + } + + pub fn complete_download(&mut self, task_id: &str) -> Result { + let needs_install = { + let task = self.task_ref(task_id)?; + task.plan.has_install_action() + }; + + if needs_install { + self.enter_install_handoff(task_id) + } else { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + if !matches!( + task.status, + RuntimeTaskStatus::Queued | RuntimeTaskStatus::Running + ) { + return Err(RuntimeError::RetryNotSupported(task_id.to_owned())); + } + task.complete(clock); + } + self.snapshot_and_notify(task_id) + } + } + + pub fn pause_task(&mut self, task_id: &str) -> Result { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + if !task.is_running_download() { + return Err(RuntimeError::PauseNotSupported(task_id.to_owned())); + } + task.status = RuntimeTaskStatus::Paused; + task.updated_at = clock; + } + self.snapshot_and_notify(task_id) + } + + pub fn resume_task(&mut self, task_id: &str) -> Result { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + if task.status != RuntimeTaskStatus::Paused { + return Err(RuntimeError::ResumeNotSupported(task_id.to_owned())); + } + task.status = RuntimeTaskStatus::Running; + task.phase = TaskPhase::new(TaskPhaseCategory::Download); + task.updated_at = clock; + } + self.snapshot_and_notify(task_id) + } + + pub fn user_result( + &mut self, + task_id: &str, + result: UserResult, + reason: Option<&str>, + ) -> Result { + { + let task = self.task_ref(task_id)?; + if !task.is_waiting_user() { + return Err(RuntimeError::TaskNotWaitingForUser(task_id.to_owned())); + } + } + self.unlock_package_for_task(task_id); + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + match result { + UserResult::Accepted => task.complete(clock), + UserResult::Rejected => task.fail( + clock, + TaskPhase::with_reason( + TaskPhaseCategory::Failed, + TaskPhaseReason::UserRejected, + ), + TaskDiagnostic::error( + "user.rejected", + reason.unwrap_or("User rejected the pending step"), + ), + RetryResume::InstallHandoff, + ), + } + } + self.snapshot_and_notify(task_id) + } + + pub fn cancel_task(&mut self, task_id: &str) -> Result { + { + let task = self.task_ref(task_id)?; + if !matches!( + task.status, + RuntimeTaskStatus::Queued | RuntimeTaskStatus::Running | RuntimeTaskStatus::Paused + ) { + return Err(RuntimeError::CancelNotSupported(task_id.to_owned())); + } + } + self.unlock_package_for_task(task_id); + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.cancel(clock); + } + self.snapshot_and_notify(task_id) + } + + pub fn retry_task(&mut self, task_id: &str) -> Result { + let retry_resume = { + let task = self.task_ref(task_id)?; + if task.status != RuntimeTaskStatus::Failed { + return Err(RuntimeError::RetryNotSupported(task_id.to_owned())); + } + task.retry_resume + }; + match retry_resume { + RetryResume::Download => { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.start_download(clock); + } + self.snapshot_and_notify(task_id) + } + RetryResume::InstallHandoff => self.enter_install_handoff(task_id), + } + } + + pub fn remove_task(&mut self, task_id: &str) -> Result { + let task = self.task_ref(task_id)?; + if task.status.is_active() { + return Err(RuntimeError::TaskActive(task_id.to_owned())); + } + let removed = self + .tasks + .remove(task_id) + .expect("task existence checked before remove"); + Ok(removed.snapshot()) + } + + pub fn clean_tasks(&mut self, mode: TaskCleanMode) -> Vec { + let task_ids: Vec = self + .tasks + .iter() + .filter_map(|(task_id, task)| { + let remove = match mode { + TaskCleanMode::Default => matches!( + task.status, + RuntimeTaskStatus::Completed | RuntimeTaskStatus::Canceled + ), + TaskCleanMode::Failed => task.status == RuntimeTaskStatus::Failed, + TaskCleanMode::AllInactive => !task.status.is_active(), + }; + remove.then(|| task_id.clone()) + }) + .collect(); + + task_ids + .into_iter() + .filter_map(|task_id| { + let removed = self.tasks.remove(&task_id)?; + Some(removed.snapshot()) + }) + .collect() + } + + fn enter_install_handoff(&mut self, task_id: &str) -> Result { + let package_id = self.task_ref(task_id)?.plan.package_id.clone(); + if self.package_locks.contains_key(&package_id) { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.fail( + clock, + TaskPhase::with_reason( + TaskPhaseCategory::Install, + TaskPhaseReason::PackageLocked, + ), + TaskDiagnostic::error( + "package.locked", + format!( + "Package {} is already being modified by another task", + package_id + ), + ), + RetryResume::InstallHandoff, + ); + } + return self.snapshot_and_notify(task_id); + } + + self.package_locks.insert(package_id, task_id.to_owned()); + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.enter_waiting_install(clock); + } + self.snapshot_and_notify(task_id) + } + + fn unlock_package_for_task(&mut self, task_id: &str) { + self.package_locks + .retain(|_, locked_by_task| locked_by_task != task_id); + } + + fn snapshot_and_notify(&mut self, task_id: &str) -> Result { + let snapshot = self.task(task_id)?; + self.notify_task_changed(snapshot.clone()); + Ok(snapshot) + } + + fn notify_task_changed(&mut self, task: TaskSnapshot) { + if let Some(sink) = self.notification_sink.as_mut() { + sink(RuntimeNotification::TaskChanged { task }); + } + } + + fn task_ref(&self, task_id: &str) -> Result<&RuntimeTask, RuntimeError> { + self.tasks + .get(task_id) + .ok_or_else(|| RuntimeError::TaskNotFound(task_id.to_owned())) + } + + fn task_mut(&mut self, task_id: &str) -> Result<&mut RuntimeTask, RuntimeError> { + self.tasks + .get_mut(task_id) + .ok_or_else(|| RuntimeError::TaskNotFound(task_id.to_owned())) + } + + fn tick(&mut self) -> u64 { + self.logical_clock += 1; + self.logical_clock + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum RetryResume { + Download, + InstallHandoff, +} + +#[derive(Debug, Clone)] +struct RuntimeTask { + id: String, + plan: SealedActionPlan, + status: RuntimeTaskStatus, + phase: TaskPhase, + progress: Option, + current_diagnostic: Option, + retry_resume: RetryResume, + updated_at: u64, +} + +impl RuntimeTask { + fn new(id: String, plan: SealedActionPlan, updated_at: u64) -> Self { + Self { + id, + plan, + status: RuntimeTaskStatus::Queued, + phase: TaskPhase::new(TaskPhaseCategory::Queued), + progress: None, + current_diagnostic: None, + retry_resume: RetryResume::Download, + updated_at, + } + } + + fn snapshot(&self) -> TaskSnapshot { + TaskSnapshot { + task_id: self.id.clone(), + package_id: self.plan.package_id.clone(), + status: self.status, + phase: self.phase.clone(), + progress: self.progress.clone(), + capabilities: self.capabilities(), + current_diagnostic: self.current_diagnostic.clone(), + updated_at: self.updated_at, + } + } + + fn capabilities(&self) -> TaskCapabilities { + TaskCapabilities { + cancel: matches!( + self.status, + RuntimeTaskStatus::Queued | RuntimeTaskStatus::Running | RuntimeTaskStatus::Paused + ), + pause: self.is_running_download(), + resume: self.status == RuntimeTaskStatus::Paused, + retry: self.status == RuntimeTaskStatus::Failed, + } + } + + fn is_running_download(&self) -> bool { + self.status == RuntimeTaskStatus::Running + && self.phase.category == TaskPhaseCategory::Download + } + + fn is_waiting_user(&self) -> bool { + self.status == RuntimeTaskStatus::Running + && self.phase + == TaskPhase::with_reason( + TaskPhaseCategory::WaitingUser, + TaskPhaseReason::InstallHandoff, + ) + } + + fn start_download(&mut self, updated_at: u64) { + self.status = RuntimeTaskStatus::Running; + self.phase = TaskPhase::new(TaskPhaseCategory::Download); + self.progress = Some(TaskProgress { + unit: TaskProgressUnit::Bit, + current: 0, + total: None, + }); + self.current_diagnostic = None; + self.updated_at = updated_at; + } + + fn enter_waiting_install(&mut self, updated_at: u64) { + self.status = RuntimeTaskStatus::Running; + self.phase = TaskPhase::with_reason( + TaskPhaseCategory::WaitingUser, + TaskPhaseReason::InstallHandoff, + ); + self.progress = None; + self.current_diagnostic = None; + self.updated_at = updated_at; + } + + fn complete(&mut self, updated_at: u64) { + self.status = RuntimeTaskStatus::Completed; + self.phase = TaskPhase::new(TaskPhaseCategory::Completed); + self.progress = None; + self.current_diagnostic = None; + self.updated_at = updated_at; + } + + fn cancel(&mut self, updated_at: u64) { + self.status = RuntimeTaskStatus::Canceled; + self.phase = TaskPhase::new(TaskPhaseCategory::Canceled); + self.progress = None; + self.current_diagnostic = None; + self.updated_at = updated_at; + } + + fn fail( + &mut self, + updated_at: u64, + phase: TaskPhase, + diagnostic: TaskDiagnostic, + retry_resume: RetryResume, + ) { + self.status = RuntimeTaskStatus::Failed; + self.phase = phase; + self.progress = None; + self.current_diagnostic = Some(diagnostic); + self.retry_resume = retry_resume; + self.updated_at = updated_at; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{PackageKind, UpdateAction}; + use std::sync::{Arc, Mutex}; + + #[test] + fn submit_consumes_action_id_and_pushes_snapshot() { + let notifications = Arc::new(Mutex::new(Vec::new())); + let sink_notifications = notifications.clone(); + let mut runtime = GetterRuntime::new(); + runtime.set_notification_sink(move |notification| { + sink_notifications.lock().unwrap().push(notification); + }); + let action = runtime.issue_action(plan("android/org.fdroid.fdroid")); + + let task = runtime.submit_action(&action.action_id).unwrap(); + + assert_eq!(task.task_id, "task-1"); + assert_eq!(task.status, RuntimeTaskStatus::Queued); + assert_eq!(task.capabilities.cancel, true); + let error = runtime.submit_action(&action.action_id).unwrap_err(); + assert_eq!(error.code(), "action.not_found"); + assert_eq!(notifications.lock().unwrap().len(), 1); + } + + #[test] + fn fake_install_waits_for_generic_user_result() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + + runtime.start_task(&task_id).unwrap(); + let waiting = runtime.complete_download(&task_id).unwrap(); + + assert_eq!(waiting.status, RuntimeTaskStatus::Running); + assert_eq!( + waiting.phase, + TaskPhase::with_reason( + TaskPhaseCategory::WaitingUser, + TaskPhaseReason::InstallHandoff + ) + ); + assert_eq!(waiting.capabilities.pause, false); + assert_eq!(waiting.capabilities.resume, false); + assert_eq!(waiting.capabilities.cancel, true); + + let completed = runtime + .user_result(&task_id, UserResult::Accepted, None) + .unwrap(); + assert_eq!(completed.status, RuntimeTaskStatus::Completed); + assert_eq!( + completed.phase, + TaskPhase::new(TaskPhaseCategory::Completed) + ); + } + + #[test] + fn rejected_user_result_fails_and_retry_reuses_same_task() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + runtime.start_task(&task_id).unwrap(); + runtime.complete_download(&task_id).unwrap(); + + let failed = runtime + .user_result(&task_id, UserResult::Rejected, Some("not now")) + .unwrap(); + + assert_eq!(failed.status, RuntimeTaskStatus::Failed); + assert_eq!(failed.capabilities.retry, true); + assert_eq!( + failed.current_diagnostic.as_ref().unwrap().code, + "user.rejected" + ); + + let retried = runtime.retry_task(&task_id).unwrap(); + assert_eq!(retried.task_id, task_id); + assert_eq!(retried.status, RuntimeTaskStatus::Running); + assert_eq!( + retried.phase, + TaskPhase::with_reason( + TaskPhaseCategory::WaitingUser, + TaskPhaseReason::InstallHandoff + ) + ); + } + + #[test] + fn user_result_outside_waiting_user_is_an_error_without_mutation() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + let before = runtime.task(&task_id).unwrap(); + + let error = runtime + .user_result(&task_id, UserResult::Accepted, None) + .unwrap_err(); + + assert_eq!(error.code(), "task.not_waiting_for_user"); + assert_eq!(runtime.task(&task_id).unwrap(), before); + } + + #[test] + fn pause_resume_are_download_phase_controls_only() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + runtime.start_task(&task_id).unwrap(); + runtime + .set_download_progress(&task_id, 42, Some(100)) + .unwrap(); + + let paused = runtime.pause_task(&task_id).unwrap(); + assert_eq!(paused.status, RuntimeTaskStatus::Paused); + assert_eq!(paused.capabilities.resume, true); + assert_eq!(paused.capabilities.cancel, true); + + let resumed = runtime.resume_task(&task_id).unwrap(); + assert_eq!(resumed.status, RuntimeTaskStatus::Running); + assert_eq!(resumed.phase, TaskPhase::new(TaskPhaseCategory::Download)); + + runtime.complete_download(&task_id).unwrap(); + let error = runtime.pause_task(&task_id).unwrap_err(); + assert_eq!(error.code(), "task.pause_not_supported"); + } + + #[test] + fn same_package_install_lock_fails_later_task_without_waiting() { + let mut runtime = GetterRuntime::new(); + let first_task = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + let second_task = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + + runtime.start_task(&first_task).unwrap(); + runtime.complete_download(&first_task).unwrap(); + runtime.start_task(&second_task).unwrap(); + let failed = runtime.complete_download(&second_task).unwrap(); + + assert_eq!(failed.status, RuntimeTaskStatus::Failed); + assert_eq!( + failed.phase, + TaskPhase::with_reason(TaskPhaseCategory::Install, TaskPhaseReason::PackageLocked) + ); + assert_eq!( + failed.current_diagnostic.as_ref().unwrap().code, + "package.locked" + ); + + runtime + .user_result(&first_task, UserResult::Accepted, None) + .unwrap(); + let retried = runtime.retry_task(&second_task).unwrap(); + assert_eq!(retried.status, RuntimeTaskStatus::Running); + assert_eq!( + retried.phase, + TaskPhase::with_reason( + TaskPhaseCategory::WaitingUser, + TaskPhaseReason::InstallHandoff + ) + ); + } + + #[test] + fn cancel_remove_and_clean_follow_in_memory_task_lifecycle() { + let mut runtime = GetterRuntime::new(); + let canceled = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + let failed = submit_plan(&mut runtime, plan("android/com.termux")); + let completed = submit_plan(&mut runtime, plan_without_install("generic/example")); + + runtime.cancel_task(&canceled).unwrap(); + runtime.start_task(&failed).unwrap(); + runtime.complete_download(&failed).unwrap(); + runtime + .user_result(&failed, UserResult::Rejected, None) + .unwrap(); + runtime.start_task(&completed).unwrap(); + runtime.complete_download(&completed).unwrap(); + + let removed_by_default = runtime.clean_tasks(TaskCleanMode::Default); + let removed_ids: Vec<_> = removed_by_default + .into_iter() + .map(|task| task.task_id) + .collect(); + assert!(removed_ids.contains(&canceled)); + assert!(removed_ids.contains(&completed)); + assert!(!removed_ids.contains(&failed)); + assert_eq!( + runtime.task(&failed).unwrap().status, + RuntimeTaskStatus::Failed + ); + + let removed_failed = runtime.remove_task(&failed).unwrap(); + assert_eq!(removed_failed.status, RuntimeTaskStatus::Failed); + assert_eq!(runtime.task(&failed).unwrap_err().code(), "task.not_found"); + } + + #[test] + fn active_tasks_cannot_be_removed_and_can_be_canceled_while_paused() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + runtime.start_task(&task_id).unwrap(); + runtime.pause_task(&task_id).unwrap(); + + let error = runtime.remove_task(&task_id).unwrap_err(); + assert_eq!(error.code(), "task.active"); + + let canceled = runtime.cancel_task(&task_id).unwrap(); + assert_eq!(canceled.status, RuntimeTaskStatus::Canceled); + runtime.remove_task(&task_id).unwrap(); + } + + fn submit_plan(runtime: &mut GetterRuntime, plan: SealedActionPlan) -> String { + let action = runtime.issue_action(plan); + runtime.submit_action(&action.action_id).unwrap().task_id + } + + fn plan(package_id: &str) -> SealedActionPlan { + SealedActionPlan { + package_id: package_id.parse().unwrap(), + lua_object: PackageVersionLuaObject { + object_id: format!("lua:{package_id}:1.0"), + dependency_digest: "sha256-test".to_owned(), + }, + actions: vec![ + UpdateAction::Download { + url: "https://example.invalid/app.apk".to_owned(), + file_name: "app.apk".to_owned(), + }, + UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned(), + }, + ], + } + } + + fn plan_without_install(package_id: &str) -> SealedActionPlan { + SealedActionPlan { + package_id: PackageId::new(PackageKind::Generic, package_id.split('/').nth(1).unwrap()) + .unwrap(), + lua_object: PackageVersionLuaObject { + object_id: format!("lua:{package_id}:1.0"), + dependency_digest: "sha256-test".to_owned(), + }, + actions: vec![UpdateAction::Download { + url: "https://example.invalid/file.bin".to_owned(), + file_name: "file.bin".to_owned(), + }], + } + } +} From aa0b75eed55f5be2614ec40623c13aa1982d4559 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 10:37:13 +0800 Subject: [PATCH 20/72] feat(operations): expose runtime task controls --- crates/getter-operations/src/lib.rs | 1 + crates/getter-operations/src/runtime.rs | 373 ++++++++++++++++++++++++ 2 files changed, 374 insertions(+) create mode 100644 crates/getter-operations/src/runtime.rs diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 71a0ca1..6d75c88 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -7,3 +7,4 @@ pub mod autogen; pub mod legacy_room; +pub mod runtime; diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs new file mode 100644 index 0000000..0d6e11d --- /dev/null +++ b/crates/getter-operations/src/runtime.rs @@ -0,0 +1,373 @@ +//! Shared JSON operation helpers for the in-memory Phase D getter runtime. +//! +//! These functions are intentionally thin over [`getter_core::runtime`]. They +//! give embedders (native bridge, future single-process debug CLI, tests) one +//! place to map JSON requests into runtime controls without reintroducing +//! persisted task state or duplicating task/control semantics in Flutter or +//! Android glue. + +use getter_core::{ + runtime::{ + GetterRuntime, IssuedAction, RuntimeError, SealedActionPlan, TaskCleanMode, TaskSnapshot, + UserResult, + }, + PackageId, +}; +use serde::Deserialize; +use serde_json::{json, Value}; + +#[derive(Debug, thiserror::Error)] +pub enum RuntimeOperationError { + #[error("invalid runtime request: {0}")] + InvalidRequest(String), + #[error("runtime error: {0}")] + Runtime(#[from] RuntimeError), + #[error("runtime response serialization failed: {0}")] + Serialize(String), +} + +impl RuntimeOperationError { + pub fn code(&self) -> &'static str { + match self { + Self::InvalidRequest(_) => "runtime.invalid_request", + Self::Runtime(error) => error.code(), + Self::Serialize(_) => "runtime.serialize_error", + } + } + + pub fn message(&self) -> &'static str { + match self { + Self::InvalidRequest(_) => "Getter runtime request is invalid", + Self::Runtime(_) => "Getter runtime operation failed", + Self::Serialize(_) => "Getter runtime response serialization failed", + } + } + + pub fn detail(&self) -> Option { + match self { + Self::InvalidRequest(detail) | Self::Serialize(detail) => Some(detail.clone()), + Self::Runtime(error) => Some(error.to_string()), + } + } +} + +pub fn issue_action(runtime: &mut GetterRuntime, plan: SealedActionPlan) -> Value { + issued_action_json(runtime.issue_action(plan)) +} + +pub fn submit_action_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: SubmitActionRequest = parse_request(request_json)?; + task_json(runtime.submit_action(&request.action_id)?) +} + +pub fn task_get_json( + runtime: &GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.task(&request.task_id)?) +} + +pub fn task_list_json( + runtime: &GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskListRequest = parse_request(request_json)?; + let tasks = if let Some(package_id) = request.package_id.as_ref() { + runtime.tasks_for_package(package_id) + } else if request.active { + runtime.active_tasks() + } else { + runtime.tasks() + }; + tasks_json(tasks) +} + +pub fn task_start_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.start_task(&request.task_id)?) +} + +pub fn task_download_progress_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: DownloadProgressRequest = parse_request(request_json)?; + task_json(runtime.set_download_progress( + &request.task_id, + request.current_bits, + request.total_bits, + )?) +} + +pub fn task_complete_download_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.complete_download(&request.task_id)?) +} + +pub fn task_pause_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.pause_task(&request.task_id)?) +} + +pub fn task_resume_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.resume_task(&request.task_id)?) +} + +pub fn task_user_result_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: UserResultRequest = parse_request(request_json)?; + task_json(runtime.user_result(&request.task_id, request.result, request.reason.as_deref())?) +} + +pub fn task_cancel_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.cancel_task(&request.task_id)?) +} + +pub fn task_retry_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.retry_task(&request.task_id)?) +} + +pub fn task_remove_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.remove_task(&request.task_id)?) +} + +pub fn task_clean_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: CleanTasksRequest = parse_request(request_json)?; + tasks_json(runtime.clean_tasks(request.mode()?)) +} + +fn parse_request(request_json: &str) -> Result +where + T: for<'de> Deserialize<'de>, +{ + serde_json::from_str(request_json) + .map_err(|source| RuntimeOperationError::InvalidRequest(source.to_string())) +} + +fn issued_action_json(action: IssuedAction) -> Value { + json!({ + "action_id": action.action_id, + "package_id": action.package_id, + }) +} + +fn task_json(task: TaskSnapshot) -> Result { + serde_json::to_value(task) + .map_err(|source| RuntimeOperationError::Serialize(source.to_string())) +} + +fn tasks_json(tasks: Vec) -> Result { + Ok(json!({ "tasks": tasks })) +} + +#[derive(Debug, Deserialize)] +struct SubmitActionRequest { + action_id: String, +} + +#[derive(Debug, Deserialize)] +struct TaskIdRequest { + task_id: String, +} + +#[derive(Debug, Default, Deserialize)] +struct TaskListRequest { + #[serde(default)] + active: bool, + #[serde(default)] + package_id: Option, +} + +#[derive(Debug, Deserialize)] +struct DownloadProgressRequest { + task_id: String, + current_bits: u64, + #[serde(default)] + total_bits: Option, +} + +#[derive(Debug, Deserialize)] +struct UserResultRequest { + task_id: String, + result: UserResult, + #[serde(default)] + reason: Option, +} + +#[derive(Debug, Default, Deserialize)] +struct CleanTasksRequest { + #[serde(default)] + mode: Option, +} + +impl CleanTasksRequest { + fn mode(self) -> Result { + match self.mode.as_deref().unwrap_or("default") { + "default" => Ok(TaskCleanMode::Default), + "failed" => Ok(TaskCleanMode::Failed), + "all_inactive" => Ok(TaskCleanMode::AllInactive), + other => Err(RuntimeOperationError::InvalidRequest(format!( + "unsupported task clean mode '{other}'" + ))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::{runtime::PackageVersionLuaObject, runtime::RuntimeTaskStatus, UpdateAction}; + + #[test] + fn json_submit_and_user_result_round_trip_consumes_action() { + let mut runtime = GetterRuntime::new(); + let issued = issue_action(&mut runtime, plan("android/org.fdroid.fdroid")); + let action_id = issued["action_id"].as_str().unwrap(); + + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + + assert_eq!(submitted["status"], "queued"); + let error = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap_err(); + assert_eq!(error.code(), "action.not_found"); + + let task_id = submitted["task_id"].as_str().unwrap(); + task_start_json(&mut runtime, &json!({ "task_id": task_id }).to_string()).unwrap(); + task_complete_download_json(&mut runtime, &json!({ "task_id": task_id }).to_string()) + .unwrap(); + let completed = task_user_result_json( + &mut runtime, + &json!({ "task_id": task_id, "result": "accepted" }).to_string(), + ) + .unwrap(); + + assert_eq!(completed["status"], "completed"); + assert_eq!( + runtime.task(task_id).unwrap().status, + RuntimeTaskStatus::Completed + ); + } + + #[test] + fn json_list_filters_and_clean_modes_follow_runtime_semantics() { + let mut runtime = GetterRuntime::new(); + let fdroid = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + let termux = submit_plan(&mut runtime, plan("android/com.termux")); + runtime.cancel_task(&fdroid).unwrap(); + runtime.start_task(&termux).unwrap(); + + let active = task_list_json(&runtime, &json!({ "active": true }).to_string()).unwrap(); + assert_eq!(active["tasks"].as_array().unwrap().len(), 1); + assert_eq!(active["tasks"][0]["task_id"], termux); + + let package = task_list_json( + &runtime, + &json!({ "package_id": "android/org.fdroid.fdroid" }).to_string(), + ) + .unwrap(); + assert_eq!(package["tasks"].as_array().unwrap().len(), 1); + assert_eq!(package["tasks"][0]["task_id"], fdroid); + + let removed = task_clean_json(&mut runtime, "{}").unwrap(); + assert_eq!(removed["tasks"].as_array().unwrap().len(), 1); + assert_eq!(runtime.task(&fdroid).unwrap_err().code(), "task.not_found"); + assert_eq!( + runtime.task(&termux).unwrap().status, + RuntimeTaskStatus::Running + ); + } + + #[test] + fn json_clean_rejects_unknown_mode_without_mutating_tasks() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan_without_install("generic/example")); + runtime.start_task(&task_id).unwrap(); + runtime.complete_download(&task_id).unwrap(); + + let error = + task_clean_json(&mut runtime, &json!({ "mode": "all" }).to_string()).unwrap_err(); + + assert_eq!(error.code(), "runtime.invalid_request"); + assert_eq!( + runtime.task(&task_id).unwrap().status, + RuntimeTaskStatus::Completed + ); + } + + fn submit_plan(runtime: &mut GetterRuntime, plan: SealedActionPlan) -> String { + let action = runtime.issue_action(plan); + runtime.submit_action(&action.action_id).unwrap().task_id + } + + fn plan(package_id: &str) -> SealedActionPlan { + SealedActionPlan { + package_id: package_id.parse().unwrap(), + actions: vec![ + UpdateAction::Download { + url: "https://example.invalid/app.apk".to_owned(), + file_name: "app.apk".to_owned(), + }, + UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned(), + }, + ], + lua_object: lua_object(package_id), + } + } + + fn plan_without_install(package_id: &str) -> SealedActionPlan { + SealedActionPlan { + package_id: package_id.parse().unwrap(), + actions: vec![UpdateAction::Download { + url: "https://example.invalid/archive.zip".to_owned(), + file_name: "archive.zip".to_owned(), + }], + lua_object: lua_object(package_id), + } + } + + fn lua_object(package_id: &str) -> PackageVersionLuaObject { + PackageVersionLuaObject { + object_id: format!("lua:{package_id}"), + dependency_digest: "sha256:test".to_owned(), + } + } +} From c4f2c281611d64ba4af41ceafd76142246ae847c Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 11:09:30 +0800 Subject: [PATCH 21/72] feat(operations): issue actions from update checks --- crates/getter-operations/src/runtime.rs | 118 +++++++++++++++++++++++- 1 file changed, 115 insertions(+), 3 deletions(-) diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 0d6e11d..5c44bff 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -8,9 +8,10 @@ use getter_core::{ runtime::{ - GetterRuntime, IssuedAction, RuntimeError, SealedActionPlan, TaskCleanMode, TaskSnapshot, - UserResult, + GetterRuntime, IssuedAction, PackageVersionLuaObject, RuntimeError, SealedActionPlan, + TaskCleanMode, TaskSnapshot, UserResult, }, + update::{run_offline_update_check, OfflineUpdateCheckError, OfflineUpdateCheckFixture}, PackageId, }; use serde::Deserialize; @@ -22,6 +23,8 @@ pub enum RuntimeOperationError { InvalidRequest(String), #[error("runtime error: {0}")] Runtime(#[from] RuntimeError), + #[error("update check failed: {0}")] + UpdateCheck(#[from] OfflineUpdateCheckError), #[error("runtime response serialization failed: {0}")] Serialize(String), } @@ -31,6 +34,7 @@ impl RuntimeOperationError { match self { Self::InvalidRequest(_) => "runtime.invalid_request", Self::Runtime(error) => error.code(), + Self::UpdateCheck(_) => "update.check_error", Self::Serialize(_) => "runtime.serialize_error", } } @@ -39,6 +43,7 @@ impl RuntimeOperationError { match self { Self::InvalidRequest(_) => "Getter runtime request is invalid", Self::Runtime(_) => "Getter runtime operation failed", + Self::UpdateCheck(_) => "Getter update check failed", Self::Serialize(_) => "Getter runtime response serialization failed", } } @@ -47,6 +52,7 @@ impl RuntimeOperationError { match self { Self::InvalidRequest(detail) | Self::Serialize(detail) => Some(detail.clone()), Self::Runtime(error) => Some(error.to_string()), + Self::UpdateCheck(error) => Some(error.to_string()), } } } @@ -55,6 +61,34 @@ pub fn issue_action(runtime: &mut GetterRuntime, plan: SealedActionPlan) -> Valu issued_action_json(runtime.issue_action(plan)) } +pub fn issue_action_from_offline_update_check_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: OfflineUpdateActionRequest = parse_request(request_json)?; + let update = run_offline_update_check(request.fixture)?; + let action = if update.actions.is_empty() { + None + } else { + Some( + runtime.issue_action(SealedActionPlan { + package_id: update.package_id.clone(), + actions: update.actions.clone(), + lua_object: PackageVersionLuaObject { + object_id: format!("offline-update:{}", update.package_id), + dependency_digest: request + .dependency_digest + .unwrap_or_else(|| format!("offline-update:{}", update.package_id)), + }, + }), + ) + }; + Ok(json!({ + "update": update, + "action": action.map(issued_action_json), + })) +} + pub fn submit_action_json( runtime: &mut GetterRuntime, request_json: &str, @@ -194,6 +228,13 @@ fn tasks_json(tasks: Vec) -> Result Ok(json!({ "tasks": tasks })) } +#[derive(Debug, Deserialize)] +struct OfflineUpdateActionRequest { + fixture: OfflineUpdateCheckFixture, + #[serde(default)] + dependency_digest: Option, +} + #[derive(Debug, Deserialize)] struct SubmitActionRequest { action_id: String, @@ -250,7 +291,51 @@ impl CleanTasksRequest { #[cfg(test)] mod tests { use super::*; - use getter_core::{runtime::PackageVersionLuaObject, runtime::RuntimeTaskStatus, UpdateAction}; + use getter_core::{ + runtime::RuntimeTaskStatus, update::OFFLINE_UPDATE_CHECK_FORMAT, + update::OFFLINE_UPDATE_CHECK_VERSION, UpdateAction, UpdateArtifact, UpdateCandidate, + }; + + #[test] + fn offline_update_check_issues_getter_owned_action_id() { + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_offline_update_check_json( + &mut runtime, + &json!({ + "fixture": update_fixture("android/org.fdroid.fdroid", Some("1.0.0"), vec!["1.2.0"]), + "dependency_digest": "sha256:fixture" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["update"]["status"], "update_available"); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + assert_eq!(submitted["package_id"], "android/org.fdroid.fdroid"); + assert_eq!(submitted["status"], "queued"); + } + + #[test] + fn offline_update_check_without_update_does_not_issue_action() { + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_offline_update_check_json( + &mut runtime, + &json!({ + "fixture": update_fixture("android/org.fdroid.fdroid", Some("1.2.0"), vec!["1.2.0"]), + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["update"]["status"], "up_to_date"); + assert!(issued["action"].is_null()); + assert_eq!(runtime.tasks().len(), 0); + } #[test] fn json_submit_and_user_result_round_trip_consumes_action() { @@ -336,6 +421,33 @@ mod tests { runtime.submit_action(&action.action_id).unwrap().task_id } + fn update_fixture( + package_id: &str, + installed_version: Option<&str>, + versions: Vec<&str>, + ) -> OfflineUpdateCheckFixture { + OfflineUpdateCheckFixture { + format: OFFLINE_UPDATE_CHECK_FORMAT.to_owned(), + version: OFFLINE_UPDATE_CHECK_VERSION, + package_id: package_id.parse().unwrap(), + installed_version: installed_version.map(str::to_owned), + ignored_version: None, + candidates: versions + .into_iter() + .map(|version| UpdateCandidate { + version: version.to_owned(), + channel: None, + source: None, + artifacts: vec![UpdateArtifact { + name: "app.apk".to_owned(), + url: "https://example.invalid/app.apk".to_owned(), + file_name: Some("app.apk".to_owned()), + }], + }) + .collect(), + } + } + fn plan(package_id: &str) -> SealedActionPlan { SealedActionPlan { package_id: package_id.parse().unwrap(), From 604a5f6a49e26ca895f26dceae655ffb404e5033 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 11:20:00 +0800 Subject: [PATCH 22/72] feat(core): accept static update candidates --- crates/getter-core/src/lib.rs | 7 +++ crates/getter-core/src/lua.rs | 92 ++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 96cdb95..b1d759e 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -251,6 +251,13 @@ pub struct ResolvedPackage { pub permissions: PackagePermissions, #[serde(default)] pub source_priority: Vec, + /// Static/offline update candidates declared by package Lua. + /// + /// This is a first mock-provider bridge toward ADR-0011 action issuance: + /// getter still owns selection and action planning, while live provider + /// execution remains a later runtime slice. + #[serde(default)] + pub updates: Vec, } /// Installed target matched by a package, such as an Android package name. diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index a31107d..79bfc61 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -1,7 +1,10 @@ //! Minimal Lua package-file evaluation and Rust validation boundary. use crate::repository::RepositoryLayout; -use crate::{InstalledTarget, PackageId, PackagePermissions, ResolvedPackage}; +use crate::{ + InstalledTarget, PackageId, PackagePermissions, ResolvedPackage, UpdateArtifact, + UpdateCandidate, +}; use mlua::{Lua, Table, Value}; use serde_json::{Map, Number, Value as JsonValue}; use std::fs; @@ -331,6 +334,7 @@ fn validate_package_json( let permissions = parse_permissions(path, object.get("permissions"))?; let source_priority = parse_string_array(path, "source_priority", object.get("source_priority"))?; + let updates = parse_update_candidates(path, object.get("updates"))?; Ok(ResolvedPackage { id, @@ -339,6 +343,7 @@ fn validate_package_json( installed, permissions, source_priority, + updates, }) } @@ -394,6 +399,69 @@ fn parse_installed_targets( .collect() } +fn parse_update_candidates( + path: &Path, + value: Option<&JsonValue>, +) -> Result, LuaPackageError> { + let Some(value) = value else { + return Ok(Vec::new()); + }; + let array = value.as_array().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "field 'updates' must be an array".to_owned(), + })?; + array + .iter() + .map(|item| { + let object = item.as_object().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "updates entries must be objects".to_owned(), + })?; + let artifacts = parse_update_artifacts(path, object.get("artifacts"))?; + Ok(UpdateCandidate { + version: required_string(path, object, "version")?.to_owned(), + channel: optional_string(object, "channel"), + source: optional_string(object, "source"), + artifacts, + }) + }) + .collect() +} + +fn parse_update_artifacts( + path: &Path, + value: Option<&JsonValue>, +) -> Result, LuaPackageError> { + let Some(value) = value else { + return Ok(Vec::new()); + }; + let array = value.as_array().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "field 'artifacts' must be an array".to_owned(), + })?; + array + .iter() + .map(|item| { + let object = item.as_object().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "artifact entries must be objects".to_owned(), + })?; + Ok(UpdateArtifact { + name: required_string(path, object, "name")?.to_owned(), + url: required_string(path, object, "url")?.to_owned(), + file_name: optional_string(object, "file_name"), + }) + }) + .collect() +} + +fn optional_string(object: &Map, field: &str) -> Option { + object + .get(field) + .and_then(JsonValue::as_str) + .map(str::to_owned) +} + fn parse_permissions( path: &Path, value: Option<&JsonValue>, @@ -480,6 +548,20 @@ return package_def { }, permissions = { free_network = true }, source_priority = { "github", "fdroid" }, + updates = { + { + version = "1.2.0", + channel = "stable", + source = "fixture", + artifacts = { + { + name = "app.apk", + url = "https://example.invalid/app.apk", + file_name = "fdroid.apk", + }, + }, + }, + }, } "#, ) @@ -497,6 +579,14 @@ return package_def { ); assert!(package.permissions.free_network); assert_eq!(package.source_priority, vec!["github", "fdroid"]); + assert_eq!(package.updates.len(), 1); + assert_eq!(package.updates[0].version, "1.2.0"); + assert_eq!(package.updates[0].channel.as_deref(), Some("stable")); + assert_eq!(package.updates[0].source.as_deref(), Some("fixture")); + assert_eq!( + package.updates[0].artifacts[0].file_name.as_deref(), + Some("fdroid.apk") + ); } #[test] From 1738c03f58450095bc075fb7adf75e4a57262854 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 11:47:24 +0800 Subject: [PATCH 23/72] feat(operations): issue actions from registered packages --- Cargo.lock | 1 + Cargo.toml | 2 +- crates/getter-core/Cargo.toml | 2 +- crates/getter-core/src/lua.rs | 11 +- crates/getter-operations/Cargo.toml | 7 + crates/getter-operations/src/runtime.rs | 258 ++++++++++++++++++++++++ 6 files changed, 277 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fce5fc5..e033e93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,6 +568,7 @@ dependencies = [ "getter-storage", "serde", "serde_json", + "tempfile", "thiserror 1.0.69", ] diff --git a/Cargo.toml b/Cargo.toml index 9347d01..ba37ca3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ edition = "2021" [features] default = ["cli", "domain"] domain = ["dep:getter-core", "dep:getter-storage", "dep:getter-operations"] -lua = ["getter-core/lua"] +lua = ["getter-core/lua", "getter-operations/lua"] cli = ["dep:getter-cli"] rustls-platform-verifier = ["dep:rustls-platform-verifier"] rustls-platform-verifier-android = ["rustls-platform-verifier", "rustls-platform-verifier/jni"] diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 93325e9..187fcf6 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -8,7 +8,7 @@ default = ["lua"] lua = ["dep:mlua"] [dependencies] -mlua = { version = "0.10", features = ["lua54", "vendored"], optional = true } +mlua = { version = "0.10", features = ["luajit", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 79bfc61..d461539 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -106,7 +106,7 @@ fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Res } fn disable_native_module_searchers(package: &Table) -> mlua::Result<()> { - let searchers: Table = package.get("searchers")?; + let searchers = package_searchers(package)?; let len = searchers.raw_len(); for index in 4..=len { searchers.raw_set(index, Value::Nil)?; @@ -128,7 +128,7 @@ fn remove_unsafe_globals(lua: &Lua) -> mlua::Result<()> { } fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> mlua::Result<()> { - let searchers: Table = package.get("searchers")?; + let searchers = package_searchers(package)?; let searcher = lua.create_function(move |lua, module: String| { let Some(module) = module.strip_prefix("lib.") else { return lua @@ -171,6 +171,13 @@ fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> searchers.raw_set(2, searcher) } +fn package_searchers(package: &Table) -> mlua::Result { + match package.get::("searchers")? { + Value::Table(searchers) => Ok(searchers), + _ => package.get("loaders"), + } +} + fn module_to_relative_path(module: &str) -> Option { let mut path = PathBuf::new(); for part in module.split('.') { diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index a44e0b6..a36ba74 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -3,9 +3,16 @@ name = "getter-operations" version.workspace = true edition.workspace = true +[features] +default = [] +lua = ["getter-core/lua"] + [dependencies] getter-core = { path = "../getter-core", default-features = false } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" + +[dev-dependencies] +tempfile = "3" diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 5c44bff..9887f15 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -6,6 +6,11 @@ //! persisted task state or duplicating task/control semantics in Flutter or //! Android glue. +#[cfg(feature = "lua")] +use getter_core::{ + lua::evaluate_package_file, + repository::{package_cache_key, RepositoryLayout, RepositoryLoadError}, +}; use getter_core::{ runtime::{ GetterRuntime, IssuedAction, PackageVersionLuaObject, RuntimeError, SealedActionPlan, @@ -14,8 +19,14 @@ use getter_core::{ update::{run_offline_update_check, OfflineUpdateCheckError, OfflineUpdateCheckFixture}, PackageId, }; +#[cfg(feature = "lua")] +use getter_core::{update::check_updates_offline, update::UpdateSelectionPolicy, RepositoryId}; +#[cfg(feature = "lua")] +use getter_storage::{MainDb, StorageError}; use serde::Deserialize; use serde_json::{json, Value}; +#[cfg(feature = "lua")] +use std::path::PathBuf; #[derive(Debug, thiserror::Error)] pub enum RuntimeOperationError { @@ -25,6 +36,15 @@ pub enum RuntimeOperationError { Runtime(#[from] RuntimeError), #[error("update check failed: {0}")] UpdateCheck(#[from] OfflineUpdateCheckError), + #[cfg(feature = "lua")] + #[error("storage operation failed: {0}")] + Storage(#[from] StorageError), + #[cfg(feature = "lua")] + #[error("repository operation failed: {0}")] + Repository(#[from] RepositoryLoadError), + #[cfg(feature = "lua")] + #[error("package evaluation failed: {0}")] + PackageEval(String), #[error("runtime response serialization failed: {0}")] Serialize(String), } @@ -35,6 +55,12 @@ impl RuntimeOperationError { Self::InvalidRequest(_) => "runtime.invalid_request", Self::Runtime(error) => error.code(), Self::UpdateCheck(_) => "update.check_error", + #[cfg(feature = "lua")] + Self::Storage(_) => "storage.error", + #[cfg(feature = "lua")] + Self::Repository(_) => "repository.error", + #[cfg(feature = "lua")] + Self::PackageEval(_) => "package.eval_error", Self::Serialize(_) => "runtime.serialize_error", } } @@ -44,6 +70,12 @@ impl RuntimeOperationError { Self::InvalidRequest(_) => "Getter runtime request is invalid", Self::Runtime(_) => "Getter runtime operation failed", Self::UpdateCheck(_) => "Getter update check failed", + #[cfg(feature = "lua")] + Self::Storage(_) => "Getter storage operation failed", + #[cfg(feature = "lua")] + Self::Repository(_) => "Getter repository operation failed", + #[cfg(feature = "lua")] + Self::PackageEval(_) => "Getter package evaluation failed", Self::Serialize(_) => "Getter runtime response serialization failed", } } @@ -53,6 +85,12 @@ impl RuntimeOperationError { Self::InvalidRequest(detail) | Self::Serialize(detail) => Some(detail.clone()), Self::Runtime(error) => Some(error.to_string()), Self::UpdateCheck(error) => Some(error.to_string()), + #[cfg(feature = "lua")] + Self::Storage(error) => Some(error.to_string()), + #[cfg(feature = "lua")] + Self::Repository(error) => Some(error.to_string()), + #[cfg(feature = "lua")] + Self::PackageEval(detail) => Some(detail.clone()), } } } @@ -89,6 +127,41 @@ pub fn issue_action_from_offline_update_check_json( })) } +#[cfg(feature = "lua")] +pub fn issue_action_from_registered_package_json( + runtime: &mut GetterRuntime, + db: &MainDb, + request_json: &str, +) -> Result { + let request: RegisteredPackageUpdateActionRequest = parse_request(request_json)?; + let (package, dependency_digest) = evaluate_registered_package(db, &request)?; + let update = check_updates_offline( + package.id.clone(), + request.installed_version, + package.updates.clone(), + UpdateSelectionPolicy { + ignored_version: request.ignored_version, + }, + )?; + let action = if update.actions.is_empty() { + None + } else { + Some(runtime.issue_action(SealedActionPlan { + package_id: update.package_id.clone(), + actions: update.actions.clone(), + lua_object: PackageVersionLuaObject { + object_id: format!("package-update:{}", update.package_id), + dependency_digest, + }, + })) + }; + Ok(json!({ + "package": package, + "update": update, + "action": action.map(issued_action_json), + })) +} + pub fn submit_action_json( runtime: &mut GetterRuntime, request_json: &str, @@ -235,6 +308,18 @@ struct OfflineUpdateActionRequest { dependency_digest: Option, } +#[cfg(feature = "lua")] +#[derive(Debug, Deserialize)] +struct RegisteredPackageUpdateActionRequest { + package_id: PackageId, + #[serde(default)] + repository_id: Option, + #[serde(default)] + installed_version: Option, + #[serde(default)] + ignored_version: Option, +} + #[derive(Debug, Deserialize)] struct SubmitActionRequest { action_id: String, @@ -275,6 +360,60 @@ struct CleanTasksRequest { mode: Option, } +#[cfg(feature = "lua")] +fn evaluate_registered_package( + db: &MainDb, + request: &RegisteredPackageUpdateActionRequest, +) -> Result<(getter_core::ResolvedPackage, String), RuntimeOperationError> { + let repositories = db.repositories()?; + let mut missing_path = None; + for repository in repositories { + if request + .repository_id + .as_ref() + .is_some_and(|requested| requested != &repository.id) + { + continue; + } + let Some(root) = repository.path.as_ref() else { + missing_path = Some(repository.id.to_string()); + continue; + }; + let root = PathBuf::from(root); + let layout = RepositoryLayout::load(&root)?; + let Some(package_file) = layout.package_file(&request.package_id) else { + continue; + }; + let package = evaluate_package_file(&layout, &package_file.path) + .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; + let cache_key = package_cache_key(&layout, package_file)?; + let dependency_digest = format!( + "repo:{}:package:{}:hash:{}", + cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash + ); + return Ok((package, dependency_digest)); + } + let detail = if let Some(repository_id) = request.repository_id.as_ref() { + format!( + "package '{}' was not found in registered repository '{}'{}", + request.package_id, + repository_id, + missing_path + .map(|id| format!("; repository '{id}' has no path")) + .unwrap_or_default() + ) + } else { + format!( + "package '{}' was not found in any registered repository{}", + request.package_id, + missing_path + .map(|id| format!("; repository '{id}' has no path")) + .unwrap_or_default() + ) + }; + Err(RuntimeOperationError::PackageEval(detail)) +} + impl CleanTasksRequest { fn mode(self) -> Result { match self.mode.as_deref().unwrap_or("default") { @@ -291,10 +430,91 @@ impl CleanTasksRequest { #[cfg(test)] mod tests { use super::*; + #[cfg(feature = "lua")] + use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; + #[cfg(feature = "lua")] + use getter_core::RepositoryPriority; use getter_core::{ runtime::RuntimeTaskStatus, update::OFFLINE_UPDATE_CHECK_FORMAT, update::OFFLINE_UPDATE_CHECK_VERSION, UpdateAction, UpdateArtifact, UpdateCandidate, }; + #[cfg(feature = "lua")] + use std::fs; + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_issues_action_from_lua_static_updates() { + let temp = tempfile::tempdir().unwrap(); + let repo_root = temp.path().join("repo"); + write_static_update_repo(&repo_root); + let db = MainDb::open_in_memory().unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::new(0), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_root), + None, + ) + .unwrap(); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + &db, + &json!({ + "package_id": "android/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["package"]["repository"], "official"); + assert_eq!(issued["update"]["status"], "update_available"); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + assert_eq!(submitted["package_id"], "android/org.fdroid.fdroid"); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_without_update_does_not_issue_action() { + let temp = tempfile::tempdir().unwrap(); + let repo_root = temp.path().join("repo"); + write_static_update_repo(&repo_root); + let db = MainDb::open_in_memory().unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::new(0), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_root), + None, + ) + .unwrap(); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + &db, + &json!({ + "package_id": "android/org.fdroid.fdroid", + "installed_version": "1.2.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["update"]["status"], "up_to_date"); + assert!(issued["action"].is_null()); + } #[test] fn offline_update_check_issues_getter_owned_action_id() { @@ -421,6 +641,44 @@ mod tests { runtime.submit_action(&action.action_id).unwrap().task_id } + #[cfg(feature = "lua")] + fn write_static_update_repo(root: &std::path::Path) { + fs::create_dir_all(root.join("packages/android")).unwrap(); + fs::create_dir(root.join("lib")).unwrap(); + fs::create_dir(root.join("templates")).unwrap(); + fs::write( + root.join("repo.toml"), + r#"id = "official" +name = "Official" +priority = 0 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::write( + root.join("packages/android/org.fdroid.fdroid.lua"), + r#" +return package_def { + id = "android/org.fdroid.fdroid", + name = "F-Droid", + updates = { + { + version = "1.2.0", + artifacts = { + { + name = "app.apk", + url = "https://example.invalid/app.apk", + file_name = "app.apk", + }, + }, + }, + }, +} +"#, + ) + .unwrap(); + } + fn update_fixture( package_id: &str, installed_version: Option<&str>, From 9ae7e5680188b2ef87bc052d2e55d5375116c0ce Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 12:43:02 +0800 Subject: [PATCH 24/72] feat(storage): rename ignored version to pin version --- crates/getter-cli/src/lib.rs | 155 ++++++++++++++++-- crates/getter-cli/tests/bdd_cli.rs | 61 +++++-- .../tests/features/cli/update_check.feature | 12 +- .../tests/features/cli/version_pin.feature | 12 ++ crates/getter-core/src/update.rs | 110 +++++-------- crates/getter-operations/src/legacy_room.rs | 27 ++- crates/getter-operations/src/runtime.rs | 8 +- crates/getter-storage/src/legacy_room.rs | 7 +- crates/getter-storage/src/lib.rs | 142 +++++++++++++--- 9 files changed, 398 insertions(+), 136 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/version_pin.feature diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 3562bf1..ed72425 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -64,6 +64,13 @@ pub enum CliCommand { repo_id: Option, }, StorageValidate, + VersionPin { + package_id: PackageId, + version: String, + }, + VersionUnpin { + package_id: PackageId, + }, UpdateCheck { fixture: PathBuf, }, @@ -380,6 +387,17 @@ where [domain, command] if domain == "storage" && command == "validate" => { CliCommand::StorageValidate } + [domain, command, package_id, version] if domain == "version" && command == "pin" => { + CliCommand::VersionPin { + package_id: parse_package_id(package_id)?, + version: version.clone(), + } + } + [domain, command, package_id] if domain == "version" && command == "unpin" => { + CliCommand::VersionUnpin { + package_id: parse_package_id(package_id)?, + } + } [domain, command, flag, fixture] if domain == "update" && command == "check" && flag == "--fixture" => { @@ -567,6 +585,19 @@ fn execute(invocation: CliInvocation) -> Result { "cache_db": cache_db_path(&invocation.data_dir), })) } + CliCommand::VersionPin { + package_id, + version, + } => { + let db = open_main_db(&invocation.data_dir)?; + let package = db.set_tracked_package_pin_version(&package_id, Some(&version))?; + Ok(json!({ "package": tracked_package_json(package) })) + } + CliCommand::VersionUnpin { package_id } => { + let db = open_main_db(&invocation.data_dir)?; + let package = db.set_tracked_package_pin_version(&package_id, None)?; + Ok(json!({ "package": tracked_package_json(package) })) + } CliCommand::UpdateCheck { fixture } => { open_initialized_storage(&invocation.data_dir)?; let fixture = read_update_check_fixture(&fixture)?; @@ -962,19 +993,18 @@ fn package_json(package: getter_core::ResolvedPackage) -> Result) -> Vec { - packages - .into_iter() - .map(|package| { - json!({ - "id": package.package_id.to_string(), - "enabled": package.enabled, - "favorite": package.favorite, - "ignored_version": package.ignored_version, - "repository_id": package.repository_id.map(|id| id.to_string()), - "package_resolution": package.package_resolution.as_str(), - }) - }) - .collect() + packages.into_iter().map(tracked_package_json).collect() +} + +fn tracked_package_json(package: StoredTrackedPackage) -> Value { + json!({ + "id": package.package_id.to_string(), + "enabled": package.enabled, + "favorite": package.favorite, + "pin_version": package.pin_version, + "repository_id": package.repository_id.map(|id| id.to_string()), + "package_resolution": package.package_resolution.as_str(), + }) } fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<(), CliError> { @@ -988,7 +1018,7 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( common_conversion_available: app.common_conversion_available, }, Some(&LegacyExtraAppRecord { - ignored_version: app.ignored_version.clone(), + ignored_version: app.pin_version.clone(), favorite: app.favorite, }), ) @@ -1000,6 +1030,7 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( "ok": true, "source": "legacy-room-bundle", "imported_records": bundle.apps.len(), + "notices": [migration_pin_version_notice()], }) .to_string(); db.import_tracked_packages_with_migration_record( @@ -1013,6 +1044,13 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( Ok(()) } +fn migration_pin_version_notice() -> Value { + json!({ + "code": "migration.renamed_ignored_version_to_pin_version", + "message": "Legacy ignored version state was preserved as pin_version", + }) +} + fn tracked_package_upsert( mapping: getter_storage::legacy_room::LegacyAppMapping, ) -> TrackedPackageUpsert { @@ -1020,7 +1058,7 @@ fn tracked_package_upsert( package_id: mapping.package_id, enabled: true, favorite: mapping.user_state.favorite, - ignored_version: mapping.user_state.ignored_version, + pin_version: mapping.user_state.pin_version, repository_id: None, package_resolution: stored_resolution(mapping.package_resolution), } @@ -1103,6 +1141,7 @@ fn create_migration_report_with_source_counts( imported_records, tracked_records, warnings, + notices: migration_report_notices(code), source_counts, }; let bytes = serde_json::to_vec_pretty(&report) @@ -1116,6 +1155,14 @@ fn report_file_name(code: &str) -> String { format!("{}.json", code.replace('.', "-")) } +fn migration_report_notices(code: &str) -> Vec { + if code == "migration.imported" { + vec![migration_pin_version_notice()] + } else { + Vec::new() + } +} + #[derive(Debug, Serialize)] struct MigrationReport<'a> { ok: bool, @@ -1126,6 +1173,8 @@ struct MigrationReport<'a> { imported_records: u64, tracked_records: u64, warnings: &'a [Value], + #[serde(skip_serializing_if = "Vec::is_empty")] + notices: Vec, #[serde(skip_serializing_if = "Option::is_none")] source_counts: Option<&'a Value>, } @@ -1165,7 +1214,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|update check --fixture |task submit --request |task run |task list|task cancel |task events --after --limit |task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |task submit --request |task run |task list|task cancel |task events --after --limit |task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1184,8 +1233,8 @@ struct LegacyBundleApp { official_package_available: bool, #[serde(default)] common_conversion_available: bool, - #[serde(default)] - ignored_version: Option, + #[serde(default, alias = "ignored_version")] + pin_version: Option, #[serde(default)] favorite: bool, } @@ -1218,6 +1267,8 @@ impl CliCommand { Self::RepoValidate { .. } => "repo validate", Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", + Self::VersionPin { .. } => "version pin", + Self::VersionUnpin { .. } => "version unpin", Self::UpdateCheck { .. } => "update check", Self::TaskSubmit { .. } => "task submit", Self::TaskRun { .. } => "task run", @@ -1249,6 +1300,43 @@ mod tests { assert_eq!(invocation.command, CliCommand::AppList); } + #[test] + fn parses_version_pin_and_unpin_commands() { + let pin = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "version", + "pin", + "android/org.fdroid.fdroid", + "1.2.3", + ]) + .unwrap(); + assert_eq!( + pin.command, + CliCommand::VersionPin { + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + version: "1.2.3".to_owned(), + } + ); + + let unpin = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "version", + "unpin", + "android/org.fdroid.fdroid", + ]) + .unwrap(); + assert_eq!( + unpin.command, + CliCommand::VersionUnpin { + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + } + ); + } + #[test] fn run_init_creates_sqlite_database_files_and_json_envelope() { let temp = tempfile::tempdir().unwrap(); @@ -1269,6 +1357,37 @@ mod tests { assert_eq!(json["command"], "init"); } + #[test] + fn run_version_pin_and_unpin_mutates_tracked_package_pin_version() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + + let pin = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "version".to_owned(), + "pin".to_owned(), + "android/org.fdroid.fdroid".to_owned(), + "1.2.3".to_owned(), + ]); + assert_eq!(pin.exit_code, ExitCode::Success); + let pin_json: Value = serde_json::from_str(&pin.stdout).unwrap(); + assert_eq!(pin_json["data"]["package"]["pin_version"], "1.2.3"); + + let unpin = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "version".to_owned(), + "unpin".to_owned(), + "android/org.fdroid.fdroid".to_owned(), + ]); + assert_eq!(unpin.exit_code, ExitCode::Success); + let unpin_json: Value = serde_json::from_str(&unpin.stdout).unwrap(); + assert!(unpin_json["data"]["package"]["pin_version"].is_null()); + } + #[test] fn malformed_bundle_report_does_not_create_imported_state() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index d0230ea..733827f 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -205,20 +205,20 @@ fn offline_update_fixture_with_installed_version( } #[given( - expr = "an offline update fixture for package {string} installed version {string} ignored version {string} with candidate versions {string}" + expr = "an offline update fixture for package {string} installed version {string} pin version {string} with candidate versions {string}" )] -fn offline_update_fixture_with_ignored_version( +fn offline_update_fixture_with_pin_version( world: &mut CliWorld, package_id: String, installed_version: String, - ignored_version: String, + pin_version: String, versions: String, ) { write_offline_update_fixture( world, package_id, Some(installed_version), - Some(ignored_version), + Some(pin_version), versions, ); } @@ -542,6 +542,26 @@ fn run_getter_update_check(world: &mut CliWorld) { world.json = None; } +#[when(expr = "I run getter version pin for package {string} version {string}")] +fn run_getter_version_pin(world: &mut CliWorld, package_id: String, version: String) { + let output = run_getter( + world, + ["version".to_owned(), "pin".to_owned(), package_id, version], + ); + world.output = Some(output); + world.json = None; +} + +#[when(expr = "I run getter version unpin for package {string}")] +fn run_getter_version_unpin(world: &mut CliWorld, package_id: String) { + let output = run_getter( + world, + ["version".to_owned(), "unpin".to_owned(), package_id], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter task submit for that request")] fn run_getter_task_submit(world: &mut CliWorld) { let request = world.task_request.as_ref().expect("task request exists"); @@ -1074,6 +1094,18 @@ fn output_contains_named_package(world: &mut CliWorld, package_id: String, packa assert_eq!(json["data"]["package"]["name"], package_name); } +#[then(expr = "the pinned package version is {string}")] +fn pinned_package_version_is(world: &mut CliWorld, version: String) { + let json = current_json(world); + assert_eq!(json["data"]["package"]["pin_version"], version); +} + +#[then("the package is unpinned")] +fn package_is_unpinned(world: &mut CliWorld) { + let json = current_json(world); + assert!(json["data"]["package"]["pin_version"].is_null()); +} + #[then(expr = "the update check status is {string}")] fn update_check_status_is(world: &mut CliWorld, status: String) { let json = current_json(world); @@ -1343,6 +1375,7 @@ fn direct_migration_report_stays_sanitized(world: &mut CliWorld) { assert_eq!(report_json["source_counts"]["hub_rows"], 1); assert_eq!(report_json["source_counts"]["extra_hub_rows"], 1); assert_report_has_drop_warnings(&report_json); + assert_report_has_pin_version_notice(&report_json); } #[then("the output reports the legacy Room migration was already completed")] @@ -1398,6 +1431,7 @@ fn direct_migration_report_list_stays_sanitized(world: &mut CliWorld) { assert_eq!(imported["source_counts"]["hub_rows"], 1); assert_eq!(imported["source_counts"]["extra_hub_rows"], 1); assert_report_has_drop_warnings(imported); + assert_report_has_pin_version_notice(imported); } #[then(expr = "the app list contains imported package {string}")] @@ -1411,7 +1445,7 @@ fn app_list_contains_imported_package(world: &mut CliWorld, package_id: String) .find(|app| app["id"].as_str() == Some(package_id.as_str())) .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); assert_eq!(app["favorite"], true); - assert_eq!(app["ignored_version"], "1.20.0"); + assert_eq!(app["pin_version"], "1.20.0"); assert_eq!(app["package_resolution"], "official_repository_package"); world.output = Some(output); world.json = Some(json); @@ -1428,7 +1462,7 @@ fn app_list_contains_directly_imported_package(world: &mut CliWorld, package_id: .find(|app| app["id"].as_str() == Some(package_id.as_str())) .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); assert_eq!(app["favorite"], true); - assert_eq!(app["ignored_version"], "1.20.0"); + assert_eq!(app["pin_version"], "1.20.0"); assert_eq!(app["package_resolution"], "missing_package_definition"); world.output = Some(output); world.json = Some(json); @@ -1504,7 +1538,7 @@ fn write_offline_update_fixture( world: &mut CliWorld, package_id: String, installed_version: Option, - ignored_version: Option, + pin_version: Option, versions: String, ) { let candidates: Vec = versions @@ -1530,7 +1564,7 @@ fn write_offline_update_fixture( world, package_id, installed_version, - ignored_version, + pin_version, candidates, ); } @@ -1539,7 +1573,7 @@ fn write_offline_update_fixture_with_candidates( world: &mut CliWorld, package_id: String, installed_version: Option, - ignored_version: Option, + pin_version: Option, candidates: Vec, ) { let temp = world.temp.as_ref().expect("tempdir exists"); @@ -1551,7 +1585,7 @@ fn write_offline_update_fixture_with_candidates( "version": 1, "package_id": package_id, "installed_version": installed_version, - "ignored_version": ignored_version, + "pin_version": pin_version, "candidates": candidates, })) .expect("fixture serializes"), @@ -1635,6 +1669,13 @@ fn assert_report_has_drop_warnings(report: &Value) { .any(|warning| warning["code"].as_str() == Some("legacy.dropped_extra_hub_rows"))); } +fn assert_report_has_pin_version_notice(report: &Value) { + let notices = report["notices"].as_array().expect("notices array"); + assert!(notices.iter().any(|notice| { + notice["code"].as_str() == Some("migration.renamed_ignored_version_to_pin_version") + })); +} + fn assert_sanitized_direct_room_report_text(text: &str) { for forbidden in [ "UA_DIRECT_DB_SENTINEL_SECRET", diff --git a/crates/getter-cli/tests/features/cli/update_check.feature b/crates/getter-cli/tests/features/cli/update_check.feature index e5d4828..9ad2f95 100644 --- a/crates/getter-cli/tests/features/cli/update_check.feature +++ b/crates/getter-cli/tests/features/cli/update_check.feature @@ -19,22 +19,22 @@ Feature: Offline update check And the update check status is "up_to_date" And the update check has no selected update - Scenario: User checks an offline fixture where the latest update is ignored + Scenario: User checks an offline fixture where pin_version overrides the local baseline Given an initialized getter data directory - And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" ignored version "1.2.0" with candidate versions "1.1.0,1.2.0" + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" pin version "1.2.0" with candidate versions "1.1.0,1.2.0,1.3.0" When I run getter update check for that fixture Then the command succeeds And the output is valid JSON And the update check status is "update_available" - And the selected update version is "1.1.0" + And the selected update version is "1.3.0" - Scenario: User checks an offline fixture where the only update is ignored + Scenario: User checks an offline fixture where pin_version makes the package up to date Given an initialized getter data directory - And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" ignored version "1.2.0" with candidate versions "1.2.0" + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" pin version "1.2.0" with candidate versions "1.2.0" When I run getter update check for that fixture Then the command succeeds And the output is valid JSON - And the update check status is "ignored" + And the update check status is "up_to_date" And the update check has no selected update Scenario: User checks an offline fixture without an installed version diff --git a/crates/getter-cli/tests/features/cli/version_pin.feature b/crates/getter-cli/tests/features/cli/version_pin.feature new file mode 100644 index 0000000..fb18337 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/version_pin.feature @@ -0,0 +1,12 @@ +@getter-cli @version +Feature: Version pin state + Scenario: User pins and unpins a package version baseline + Given an initialized getter data directory + When I run getter version pin for package "android/org.fdroid.fdroid" version "1.2.3" + Then the command succeeds + And the output is valid JSON + And the pinned package version is "1.2.3" + When I run getter version unpin for package "android/org.fdroid.fdroid" + Then the command succeeds + And the output is valid JSON + And the package is unpinned diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs index 72acfa6..09952ef 100644 --- a/crates/getter-core/src/update.rs +++ b/crates/getter-core/src/update.rs @@ -12,9 +12,9 @@ pub const OFFLINE_UPDATE_CHECK_VERSION: u32 = 1; /// User state that affects update selection. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct UpdateSelectionPolicy { - /// Candidate version the user chose to ignore/mark as skipped. - #[serde(default)] - pub ignored_version: Option, + /// User-selected local version override used as the comparison baseline. + #[serde(default, alias = "ignored_version")] + pub pin_version: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -24,8 +24,8 @@ pub struct OfflineUpdateCheckFixture { pub package_id: PackageId, #[serde(default)] pub installed_version: Option, - #[serde(default)] - pub ignored_version: Option, + #[serde(default, alias = "ignored_version")] + pub pin_version: Option, #[serde(default)] pub candidates: Vec, } @@ -36,6 +36,8 @@ pub struct OfflineUpdateCheckResult { pub package_id: PackageId, #[serde(default)] pub installed_version: Option, + #[serde(default)] + pub effective_local_version: Option, pub policy: UpdateSelectionPolicy, pub status: UpdateCheckStatus, #[serde(default)] @@ -49,7 +51,6 @@ pub enum UpdateCheckStatus { UpdateAvailable, UpToDate, NoCandidates, - Ignored, } #[derive(Debug, thiserror::Error)] @@ -76,7 +77,7 @@ pub fn run_offline_update_check( } let policy = UpdateSelectionPolicy { - ignored_version: fixture.ignored_version, + pin_version: fixture.pin_version, }; check_updates_offline( fixture.package_id, @@ -92,18 +93,17 @@ pub fn check_updates_offline( candidates: Vec, policy: UpdateSelectionPolicy, ) -> Result { + let effective_local_version = policy + .pin_version + .clone() + .or_else(|| installed_version.clone()); let selected = select_update( package_id.clone(), - installed_version.as_deref(), - &candidates, - &policy, - ); - let status = update_check_status( - selected.as_ref(), - installed_version.as_deref(), + effective_local_version.as_deref(), &candidates, &policy, ); + let status = update_check_status(selected.as_ref(), &candidates); let actions = match selected.as_ref() { Some(selected) => { if selected.artifact.is_none() { @@ -120,6 +120,7 @@ pub fn check_updates_offline( network_required: false, package_id, installed_version, + effective_local_version, policy, status, selected, @@ -163,38 +164,17 @@ fn installer_for_package_kind(kind: PackageKind) -> &'static str { fn update_check_status( selected: Option<&SelectedUpdate>, - installed_version: Option<&str>, candidates: &[UpdateCandidate], - policy: &UpdateSelectionPolicy, ) -> UpdateCheckStatus { if candidates.is_empty() { UpdateCheckStatus::NoCandidates } else if selected.is_some() { UpdateCheckStatus::UpdateAvailable - } else if ignored_candidate_would_have_been_update(installed_version, candidates, policy) { - UpdateCheckStatus::Ignored } else { UpdateCheckStatus::UpToDate } } -fn ignored_candidate_would_have_been_update( - installed_version: Option<&str>, - candidates: &[UpdateCandidate], - policy: &UpdateSelectionPolicy, -) -> bool { - let Some(ignored) = policy.ignored_version.as_deref() else { - return false; - }; - - candidates.iter().any(|candidate| { - compare_versions(&candidate.version, ignored) == Ordering::Equal - && installed_version.is_none_or(|installed| { - compare_versions(&candidate.version, installed) == Ordering::Greater - }) - }) -} - /// Compare human-facing version strings using a deterministic token ordering. /// /// This intentionally starts small: digit runs compare numerically, ASCII text @@ -236,8 +216,10 @@ pub fn compare_versions(left: &str, right: &str) -> Ordering { /// Select the best update candidate for a package. /// /// The selected candidate is the highest version that is newer than the -/// installed version and not equal to the user's ignored version. If no -/// installed version is known, the highest non-ignored candidate is selected. +/// effective local baseline. The effective baseline is normally the observed +/// installed version; callers pass `pin_version` instead when the user has set +/// a baseline override. If no baseline is known, the highest candidate is +/// selected. pub fn select_update( package_id: PackageId, installed_version: Option<&str>, @@ -261,13 +243,7 @@ fn is_selectable( installed_version: Option<&str>, policy: &UpdateSelectionPolicy, ) -> bool { - if policy - .ignored_version - .as_deref() - .is_some_and(|ignored| compare_versions(&candidate.version, ignored) == Ordering::Equal) - { - return false; - } + let _ = policy; match installed_version { Some(installed) => compare_versions(&candidate.version, installed) == Ordering::Greater, @@ -400,18 +376,21 @@ mod tests { } #[test] - fn respects_ignored_version() { - let selected = select_update( + fn pin_version_overrides_comparison_baseline() { + let result = check_updates_offline( "android/org.fdroid.fdroid".parse().unwrap(), - Some("1.0.0"), - &[candidate("1.1.0"), candidate("1.2.0")], - &UpdateSelectionPolicy { - ignored_version: Some("1.2.0".to_owned()), + Some("1.0.0".to_owned()), + vec![candidate("1.1.0"), candidate("1.2.0")], + UpdateSelectionPolicy { + pin_version: Some("1.2.0".to_owned()), }, ) .unwrap(); - assert_eq!(selected.candidate.version, "1.1.0"); + assert_eq!(result.status, UpdateCheckStatus::UpToDate); + assert_eq!(result.installed_version.as_deref(), Some("1.0.0")); + assert_eq!(result.effective_local_version.as_deref(), Some("1.2.0")); + assert!(result.selected.is_none()); } #[test] @@ -449,7 +428,7 @@ mod tests { version: OFFLINE_UPDATE_CHECK_VERSION, package_id: "android/org.fdroid.fdroid".parse().unwrap(), installed_version: Some("1.0.0".to_owned()), - ignored_version: None, + pin_version: None, candidates: vec![candidate("1.0.1"), candidate("1.2.0")], }) .unwrap(); @@ -487,35 +466,20 @@ mod tests { } #[test] - fn offline_update_check_reports_ignored_when_only_update_is_ignored() { + fn offline_update_check_uses_pin_version_as_baseline() { let result = check_updates_offline( "android/org.fdroid.fdroid".parse().unwrap(), Some("1.0.0".to_owned()), - vec![candidate("1.2.0")], - UpdateSelectionPolicy { - ignored_version: Some("1.2.0".to_owned()), - }, - ) - .unwrap(); - - assert_eq!(result.status, UpdateCheckStatus::Ignored); - assert!(result.selected.is_none()); - } - - #[test] - fn offline_update_check_falls_back_below_ignored_latest() { - let result = check_updates_offline( - "android/org.fdroid.fdroid".parse().unwrap(), - Some("1.0.0".to_owned()), - vec![candidate("1.1.0"), candidate("1.2.0")], + vec![candidate("1.1.0"), candidate("1.2.0"), candidate("1.3.0")], UpdateSelectionPolicy { - ignored_version: Some("1.2.0".to_owned()), + pin_version: Some("1.2.0".to_owned()), }, ) .unwrap(); assert_eq!(result.status, UpdateCheckStatus::UpdateAvailable); - assert_eq!(result.selected.as_ref().unwrap().candidate.version, "1.1.0"); + assert_eq!(result.selected.as_ref().unwrap().candidate.version, "1.3.0"); + assert_eq!(result.effective_local_version.as_deref(), Some("1.2.0")); } #[test] @@ -561,7 +525,7 @@ mod tests { version: OFFLINE_UPDATE_CHECK_VERSION, package_id: "android/org.fdroid.fdroid".parse().unwrap(), installed_version: None, - ignored_version: None, + pin_version: None, candidates: Vec::new(), }) .unwrap_err(); diff --git a/crates/getter-operations/src/legacy_room.rs b/crates/getter-operations/src/legacy_room.rs index 40c7c34..352205e 100644 --- a/crates/getter-operations/src/legacy_room.rs +++ b/crates/getter-operations/src/legacy_room.rs @@ -174,6 +174,7 @@ fn import_legacy_room_db( "imported_records": import.apps.len(), "source_counts": source_counts_json(import), "warnings": import_warnings_json(&import.warnings), + "notices": [migration_pin_version_notice()], }) .to_string(); db.import_tracked_packages_with_migration_record( @@ -194,7 +195,7 @@ fn tracked_package_upsert( package_id: mapping.package_id, enabled: true, favorite: mapping.user_state.favorite, - ignored_version: mapping.user_state.ignored_version, + pin_version: mapping.user_state.pin_version, repository_id: None, package_resolution: stored_resolution(mapping.package_resolution), } @@ -249,7 +250,7 @@ fn tracked_packages_json(packages: Vec) -> Vec { "id": package.package_id.to_string(), "enabled": package.enabled, "favorite": package.favorite, - "ignored_version": package.ignored_version, + "pin_version": package.pin_version, "repository_id": package.repository_id.map(|id| id.to_string()), "package_resolution": package.package_resolution.as_str(), }) @@ -266,6 +267,13 @@ fn source_counts_json(import: &LegacyRoomDbImport) -> Value { }) } +fn migration_pin_version_notice() -> Value { + json!({ + "code": "migration.renamed_ignored_version_to_pin_version", + "message": "Legacy ignored version state was preserved as pin_version", + }) +} + fn import_warnings_json(warnings: &[LegacyRoomImportWarning]) -> Vec { warnings .iter() @@ -310,6 +318,7 @@ fn create_migration_report_with_source_counts( imported_records, tracked_records, warnings, + notices: migration_report_notices(code), source_counts, }; let bytes = serde_json::to_vec_pretty(&report).map_err(|source| { @@ -325,6 +334,14 @@ fn report_file_name(code: &str) -> String { format!("{}.json", code.replace('.', "-")) } +fn migration_report_notices(code: &str) -> Vec { + if code == "migration.imported" { + vec![migration_pin_version_notice()] + } else { + Vec::new() + } +} + fn list_migration_reports(data_dir: &Path) -> LegacyRoomOperationResult> { let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); if !reports_dir.exists() { @@ -377,6 +394,10 @@ fn list_migration_reports(data_dir: &Path) -> LegacyRoomOperationResult { imported_records: u64, tracked_records: u64, warnings: &'a [Value], + #[serde(skip_serializing_if = "Vec::is_empty")] + notices: Vec, #[serde(skip_serializing_if = "Option::is_none")] source_counts: Option<&'a Value>, } diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 9887f15..f6bfa38 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -140,7 +140,7 @@ pub fn issue_action_from_registered_package_json( request.installed_version, package.updates.clone(), UpdateSelectionPolicy { - ignored_version: request.ignored_version, + pin_version: request.pin_version, }, )?; let action = if update.actions.is_empty() { @@ -316,8 +316,8 @@ struct RegisteredPackageUpdateActionRequest { repository_id: Option, #[serde(default)] installed_version: Option, - #[serde(default)] - ignored_version: Option, + #[serde(default, alias = "ignored_version")] + pin_version: Option, } #[derive(Debug, Deserialize)] @@ -689,7 +689,7 @@ return package_def { version: OFFLINE_UPDATE_CHECK_VERSION, package_id: package_id.parse().unwrap(), installed_version: installed_version.map(str::to_owned), - ignored_version: None, + pin_version: None, candidates: versions .into_iter() .map(|version| UpdateCandidate { diff --git a/crates/getter-storage/src/legacy_room.rs b/crates/getter-storage/src/legacy_room.rs index 711ea1f..cf225f7 100644 --- a/crates/getter-storage/src/legacy_room.rs +++ b/crates/getter-storage/src/legacy_room.rs @@ -51,7 +51,7 @@ pub enum LegacyPackageResolution { #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct LegacyUserStateMapping { - pub ignored_version: Option, + pub pin_version: Option, pub favorite: bool, } @@ -157,10 +157,9 @@ pub fn map_legacy_app( _ => Vec::new(), }; let user_state = LegacyUserStateMapping { - ignored_version: extra.and_then(|extra| extra.ignored_version.clone()), + pin_version: extra.and_then(|extra| extra.ignored_version.clone()), favorite: extra.is_some_and(|extra| extra.favorite), }; - Ok(LegacyAppMapping { package_id, package_resolution, @@ -506,7 +505,7 @@ mod tests { ) .unwrap(); - assert_eq!(mapping.user_state.ignored_version.as_deref(), Some("1.2.3")); + assert_eq!(mapping.user_state.pin_version.as_deref(), Some("1.2.3")); assert!(mapping.user_state.favorite); } diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index 23b0e8a..2127cbc 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -35,6 +35,8 @@ pub enum StorageError { InvalidTaskTransition { task_id: String, reason: String }, #[error("invalid task request: {0}")] InvalidTaskRequest(String), + #[error("storage invariant failed: {0}")] + Invariant(String), #[error("JSON error: {0}")] Json(#[from] serde_json::Error), } @@ -85,7 +87,7 @@ CREATE TABLE IF NOT EXISTS tracked_packages ( package_id TEXT PRIMARY KEY, enabled INTEGER NOT NULL DEFAULT 1, favorite INTEGER NOT NULL DEFAULT 0, - ignored_version TEXT, + pin_version TEXT, repository_id TEXT, package_resolution TEXT NOT NULL DEFAULT 'missing_package_definition', FOREIGN KEY(repository_id) REFERENCES repositories(id) @@ -136,7 +138,8 @@ CREATE TABLE IF NOT EXISTS install_handoffs ( ); "#, )?; - self.ensure_column("tracked_packages", "ignored_version", "TEXT")?; + self.ensure_column("tracked_packages", "pin_version", "TEXT")?; + self.migrate_ignored_version_to_pin_version()?; self.ensure_column( "tracked_packages", "package_resolution", @@ -168,6 +171,22 @@ CREATE TABLE IF NOT EXISTS install_handoffs ( Ok(()) } + fn migrate_ignored_version_to_pin_version(&self) -> Result<(), StorageError> { + let columns = self.table_columns("tracked_packages")?; + if columns.iter().any(|existing| existing == "ignored_version") { + self.conn.execute( + r#" +UPDATE tracked_packages +SET pin_version = ignored_version +WHERE pin_version IS NULL + AND ignored_version IS NOT NULL +"#, + [], + )?; + } + Ok(()) + } + fn table_columns(&self, table: &'static str) -> Result, StorageError> { let mut stmt = self.conn.prepare(&format!("PRAGMA table_info({table})"))?; let rows = stmt.query_map([], |row| row.get::<_, String>(1))?; @@ -244,6 +263,46 @@ ON CONFLICT(id) DO UPDATE SET Ok(()) } + pub fn set_tracked_package_pin_version( + &self, + package_id: &PackageId, + pin_version: Option<&str>, + ) -> Result { + self.conn.execute( + r#" +INSERT INTO tracked_packages( + package_id, + enabled, + favorite, + pin_version, + repository_id, + package_resolution +) +VALUES (?1, 1, 0, ?2, NULL, ?3) +ON CONFLICT(package_id) DO UPDATE SET + pin_version = excluded.pin_version +"#, + params![ + package_id.to_string(), + pin_version, + StoredPackageResolution::MissingPackageDefinition.as_str(), + ], + )?; + self.tracked_package(package_id)?.ok_or_else(|| { + StorageError::Invariant("tracked package upsert did not create a row".to_owned()) + }) + } + + pub fn tracked_package( + &self, + package_id: &PackageId, + ) -> Result, StorageError> { + Ok(self + .tracked_packages()? + .into_iter() + .find(|package| &package.package_id == package_id)) + } + pub fn upsert_generated_tracked_package_preserving_user_state( &self, package_id: &PackageId, @@ -255,7 +314,7 @@ INSERT INTO tracked_packages( package_id, enabled, favorite, - ignored_version, + pin_version, repository_id, package_resolution ) @@ -329,7 +388,7 @@ WHERE package_id = ?1 pub fn tracked_packages(&self) -> Result, StorageError> { let mut stmt = self.conn.prepare( r#" -SELECT package_id, enabled, favorite, ignored_version, repository_id, package_resolution +SELECT package_id, enabled, favorite, pin_version, repository_id, package_resolution FROM tracked_packages ORDER BY package_id ASC "#, @@ -346,12 +405,12 @@ ORDER BY package_id ASC })?; let mut packages = Vec::new(); for row in rows { - let (package_id, enabled, favorite, ignored_version, repository_id, resolution) = row?; + let (package_id, enabled, favorite, pin_version, repository_id, resolution) = row?; packages.push(StoredTrackedPackage { package_id: PackageId::from_str(&package_id)?, enabled: enabled != 0, favorite: favorite != 0, - ignored_version, + pin_version, repository_id: repository_id.map(RepositoryId::new).transpose()?, package_resolution: StoredPackageResolution::from_str(&resolution)?, }); @@ -842,7 +901,7 @@ pub struct TrackedPackageUpsert { pub package_id: PackageId, pub enabled: bool, pub favorite: bool, - pub ignored_version: Option, + pub pin_version: Option, pub repository_id: Option, pub package_resolution: StoredPackageResolution, } @@ -852,7 +911,7 @@ pub struct StoredTrackedPackage { pub package_id: PackageId, pub enabled: bool, pub favorite: bool, - pub ignored_version: Option, + pub pin_version: Option, pub repository_id: Option, pub package_resolution: StoredPackageResolution, } @@ -957,7 +1016,7 @@ INSERT INTO tracked_packages( package_id, enabled, favorite, - ignored_version, + pin_version, repository_id, package_resolution ) @@ -965,7 +1024,7 @@ VALUES (?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT(package_id) DO UPDATE SET enabled = excluded.enabled, favorite = excluded.favorite, - ignored_version = excluded.ignored_version, + pin_version = excluded.pin_version, repository_id = excluded.repository_id, package_resolution = excluded.package_resolution "#, @@ -973,7 +1032,7 @@ ON CONFLICT(package_id) DO UPDATE SET package.package_id.to_string(), bool_to_i64(package.enabled), bool_to_i64(package.favorite), - package.ignored_version.as_deref(), + package.pin_version.as_deref(), package.repository_id.as_ref().map(RepositoryId::as_str), package.package_resolution.as_str(), ], @@ -1105,7 +1164,7 @@ mod tests { package_id: "android/org.fdroid.fdroid".parse().unwrap(), enabled: true, favorite: true, - ignored_version: Some("1.2.3".to_owned()), + pin_version: Some("1.2.3".to_owned()), repository_id: None, package_resolution: StoredPackageResolution::OfficialRepositoryPackage, }) @@ -1119,13 +1178,58 @@ mod tests { ); assert!(packages[0].enabled); assert!(packages[0].favorite); - assert_eq!(packages[0].ignored_version.as_deref(), Some("1.2.3")); + assert_eq!(packages[0].pin_version.as_deref(), Some("1.2.3")); assert_eq!( packages[0].package_resolution, StoredPackageResolution::OfficialRepositoryPackage ); } + #[test] + fn main_db_pins_and_unpins_tracked_package_version() { + let db = MainDb::open_in_memory().unwrap(); + let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + + let pinned = db + .set_tracked_package_pin_version(&package_id, Some("1.2.3")) + .unwrap(); + assert_eq!(pinned.pin_version.as_deref(), Some("1.2.3")); + assert_eq!( + pinned.package_resolution, + StoredPackageResolution::MissingPackageDefinition + ); + + let unpinned = db + .set_tracked_package_pin_version(&package_id, None) + .unwrap(); + assert_eq!(unpinned.pin_version, None); + } + + #[test] + fn main_db_migrates_legacy_ignored_version_column_to_pin_version() { + let conn = Connection::open_in_memory().unwrap(); + conn.execute_batch( + r#" +CREATE TABLE tracked_packages ( + package_id TEXT PRIMARY KEY, + enabled INTEGER NOT NULL DEFAULT 1, + favorite INTEGER NOT NULL DEFAULT 0, + ignored_version TEXT, + repository_id TEXT, + package_resolution TEXT NOT NULL DEFAULT 'missing_package_definition' +); +INSERT INTO tracked_packages(package_id, ignored_version) +VALUES ('android/org.fdroid.fdroid', '1.2.3'); +"#, + ) + .unwrap(); + let db = MainDb { conn }; + db.migrate().unwrap(); + + let packages = db.tracked_packages().unwrap(); + assert_eq!(packages[0].pin_version.as_deref(), Some("1.2.3")); + } + #[test] fn generated_tracking_preserves_existing_user_state_on_conflict() { let db = MainDb::open_in_memory().unwrap(); @@ -1135,7 +1239,7 @@ mod tests { package_id: package_id.clone(), enabled: false, favorite: true, - ignored_version: Some("9.9.9".to_owned()), + pin_version: Some("9.9.9".to_owned()), repository_id: None, package_resolution: StoredPackageResolution::OfficialRepositoryPackage, }) @@ -1148,7 +1252,7 @@ mod tests { assert_eq!(packages.len(), 1); assert!(!packages[0].enabled); assert!(packages[0].favorite); - assert_eq!(packages[0].ignored_version.as_deref(), Some("9.9.9")); + assert_eq!(packages[0].pin_version.as_deref(), Some("9.9.9")); assert_eq!(packages[0].repository_id, None); assert_eq!( packages[0].package_resolution, @@ -1165,7 +1269,7 @@ mod tests { package_id: package_id.clone(), enabled: false, favorite: true, - ignored_version: Some("9.9.9".to_owned()), + pin_version: Some("9.9.9".to_owned()), repository_id: None, package_resolution: StoredPackageResolution::MissingPackageDefinition, }) @@ -1178,7 +1282,7 @@ mod tests { assert_eq!(packages.len(), 1); assert!(!packages[0].enabled); assert!(packages[0].favorite); - assert_eq!(packages[0].ignored_version.as_deref(), Some("9.9.9")); + assert_eq!(packages[0].pin_version.as_deref(), Some("9.9.9")); assert_eq!(packages[0].repository_id.as_ref(), Some(&local_autogen)); assert_eq!( packages[0].package_resolution, @@ -1195,7 +1299,7 @@ mod tests { package_id: package_id.clone(), enabled: true, favorite: true, - ignored_version: Some("9.9.9".to_owned()), + pin_version: Some("9.9.9".to_owned()), repository_id: None, package_resolution: StoredPackageResolution::OfficialRepositoryPackage, }) @@ -1210,7 +1314,7 @@ mod tests { package_id: package_id.clone(), enabled: true, favorite: true, - ignored_version: Some("9.9.9".to_owned()), + pin_version: Some("9.9.9".to_owned()), repository_id: None, package_resolution: StoredPackageResolution::MissingPackageDefinition, }) From ae5b00ccf0989ee1b0b69521bb508d4136c2bcb4 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 13:41:37 +0800 Subject: [PATCH 25/72] feat(cli): add runtime script debug tasks --- crates/getter-cli/src/lib.rs | 507 ++++++++++++++++-- crates/getter-cli/tests/bdd_cli.rs | 154 +++++- .../tests/features/cli/task_lifecycle.feature | 52 +- 3 files changed, 621 insertions(+), 92 deletions(-) diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index ed72425..d8317fe 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -8,6 +8,7 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; +use getter_core::runtime::{GetterRuntime, SealedActionPlan}; use getter_core::task::{ DownloadTaskRequest, InstallHandoffStatus, TaskEventPage, DOWNLOAD_REQUEST_FORMAT, DOWNLOAD_REQUEST_VERSION, @@ -19,6 +20,7 @@ use getter_downloader::{ }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; +use getter_operations::runtime as runtime_operations; use getter_storage::legacy_room::{ map_legacy_app, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, }; @@ -74,24 +76,27 @@ pub enum CliCommand { UpdateCheck { fixture: PathBuf, }, - TaskSubmit { + DebugFakeTaskSubmit { request: PathBuf, }, - TaskRun { + DebugFakeTaskRun { task_id: String, }, - TaskList, - TaskCancel { + DebugFakeTaskList, + DebugFakeTaskCancel { task_id: String, }, - TaskEvents { + DebugFakeTaskEvents { after: u64, limit: usize, }, - TaskInstallResult { + DebugFakeTaskInstallResult { handoff_id: String, status: InstallHandoffStatus, }, + RuntimeScript { + script: PathBuf, + }, AutogenInstalledPreview { inventory: PathBuf, }, @@ -145,6 +150,8 @@ pub enum CliError { Update(String), #[error("download task error: {0}")] Download(String), + #[error("runtime error: {0}")] + Runtime(String), #[error("autogen error: {0}")] Autogen(String), #[error("Legacy Room export bundle is invalid")] @@ -162,9 +169,11 @@ impl CliError { match self { Self::Usage(_) => ExitCode::Usage, Self::Storage(_) => ExitCode::Storage, - Self::Repository(_) | Self::PackageEval(_) | Self::Update(_) | Self::Autogen(_) => { - ExitCode::GenericFailure - } + Self::Repository(_) + | Self::PackageEval(_) + | Self::Update(_) + | Self::Runtime(_) + | Self::Autogen(_) => ExitCode::GenericFailure, Self::Download(_) => ExitCode::Download, Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } @@ -181,6 +190,7 @@ impl CliError { Self::PackageEval(_) => "package.eval_error", Self::Update(_) => "update.check_error", Self::Download(_) => "download.task_error", + Self::Runtime(_) => "runtime.error", Self::Autogen(_) => "autogen.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", @@ -197,6 +207,7 @@ impl CliError { Self::PackageEval(_) => "Getter package evaluation failed", Self::Update(_) => "Getter update check failed", Self::Download(_) => "Getter download task operation failed", + Self::Runtime(_) => "Getter runtime operation failed", Self::Autogen(_) => "Getter autogen operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { @@ -215,6 +226,7 @@ impl CliError { | Self::PackageEval(detail) | Self::Update(detail) | Self::Download(detail) + | Self::Runtime(detail) | Self::Autogen(detail) => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } @@ -235,6 +247,7 @@ impl CliError { | Self::PackageEval(_) | Self::Update(_) | Self::Download(_) + | Self::Runtime(_) | Self::Autogen(_) => None, } } @@ -405,41 +418,65 @@ where fixture: PathBuf::from(fixture), } } - [domain, command, flag, request] - if domain == "task" && command == "submit" && flag == "--request" => + [domain, subject, command, flag, request] + if domain == "debug" + && subject == "fake-task" + && command == "submit" + && flag == "--request" => { - CliCommand::TaskSubmit { + CliCommand::DebugFakeTaskSubmit { request: PathBuf::from(request), } } - [domain, command, task_id] if domain == "task" && command == "run" => CliCommand::TaskRun { - task_id: task_id.clone(), - }, - [domain, command] if domain == "task" && command == "list" => CliCommand::TaskList, - [domain, command, task_id] if domain == "task" && command == "cancel" => { - CliCommand::TaskCancel { + [domain, subject, command, task_id] + if domain == "debug" && subject == "fake-task" && command == "run" => + { + CliCommand::DebugFakeTaskRun { task_id: task_id.clone(), } } - [domain, command, after_flag, after, limit_flag, limit] - if domain == "task" + [domain, subject, command] + if domain == "debug" && subject == "fake-task" && command == "list" => + { + CliCommand::DebugFakeTaskList + } + [domain, subject, command, task_id] + if domain == "debug" && subject == "fake-task" && command == "cancel" => + { + CliCommand::DebugFakeTaskCancel { + task_id: task_id.clone(), + } + } + [domain, subject, command, after_flag, after, limit_flag, limit] + if domain == "debug" + && subject == "fake-task" && command == "events" && after_flag == "--after" && limit_flag == "--limit" => { - CliCommand::TaskEvents { + CliCommand::DebugFakeTaskEvents { after: parse_u64(after, "--after")?, limit: parse_positive_usize(limit, "--limit")?, } } - [domain, command, handoff_id, status_flag, status] - if domain == "task" && command == "install-result" && status_flag == "--status" => + [domain, subject, command, handoff_id, status_flag, status] + if domain == "debug" + && subject == "fake-task" + && command == "install-result" + && status_flag == "--status" => { - CliCommand::TaskInstallResult { + CliCommand::DebugFakeTaskInstallResult { handoff_id: handoff_id.clone(), status: parse_install_handoff_status(status)?, } } + [domain, command, flag, script] + if domain == "runtime" && command == "script" && flag == "--script" => + { + CliCommand::RuntimeScript { + script: PathBuf::from(script), + } + } [domain, subject, action, flag, inventory] if domain == "autogen" && subject == "installed" @@ -608,46 +645,49 @@ fn execute(invocation: CliInvocation) -> Result { CliError::Update(format!("failed to serialize update check: {source}")) }) } - CliCommand::TaskSubmit { request } => { + CliCommand::DebugFakeTaskSubmit { request } => { let db = open_main_db(&invocation.data_dir)?; let request = read_download_task_request(&request)?; serde_json::to_value(submit_fake_download_task(&db, request).map_err(|source| { - CliError::Download(format!("offline task submit failed: {source}")) + CliError::Download(format!("offline fake task submit failed: {source}")) })?) .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) } - CliCommand::TaskRun { task_id } => { + CliCommand::DebugFakeTaskRun { task_id } => { let db = open_main_db(&invocation.data_dir)?; serde_json::to_value(run_fake_download_task(&db, &task_id).map_err(|source| { - CliError::Download(format!("offline task run failed: {source}")) + CliError::Download(format!("offline fake task run failed: {source}")) })?) .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) } - CliCommand::TaskList => { + CliCommand::DebugFakeTaskList => { let db = open_main_db(&invocation.data_dir)?; Ok(json!({ "tasks": db.download_tasks()? })) } - CliCommand::TaskCancel { task_id } => { + CliCommand::DebugFakeTaskCancel { task_id } => { let db = open_main_db(&invocation.data_dir)?; serde_json::to_value(cancel_download_task(&db, &task_id).map_err(|source| { - CliError::Download(format!("offline task cancel failed: {source}")) + CliError::Download(format!("offline fake task cancel failed: {source}")) })?) .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) } - CliCommand::TaskEvents { after, limit } => { + CliCommand::DebugFakeTaskEvents { after, limit } => { let db = open_main_db(&invocation.data_dir)?; let events: TaskEventPage = db.task_events_after(after, limit)?; serde_json::to_value(events).map_err(|source| { - CliError::Download(format!("failed to serialize task events: {source}")) + CliError::Download(format!("failed to serialize fake task events: {source}")) }) } - CliCommand::TaskInstallResult { handoff_id, status } => { + CliCommand::DebugFakeTaskInstallResult { handoff_id, status } => { let db = open_main_db(&invocation.data_dir)?; serde_json::to_value(record_install_result(&db, &handoff_id, status).map_err( - |source| CliError::Download(format!("offline install result failed: {source}")), + |source| { + CliError::Download(format!("offline fake install result failed: {source}")) + }, )?) .map_err(|source| CliError::Download(format!("failed to serialize handoff: {source}"))) } + CliCommand::RuntimeScript { script } => run_runtime_script(&script), CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; @@ -954,6 +994,227 @@ fn read_download_task_request(path: &Path) -> Result, +} + +#[derive(Debug, Deserialize)] +struct RuntimeScriptStep { + operation: String, + #[serde(default)] + payload: Value, + #[serde(default)] + plan: Option, +} + +fn run_runtime_script(path: &Path) -> Result { + let bytes = fs::read(path) + .map_err(|source| CliError::Runtime(format!("failed to read runtime script: {source}")))?; + let script: RuntimeScript = serde_json::from_slice(&bytes).map_err(|source| { + CliError::Runtime(format!("failed to parse runtime script JSON: {source}")) + })?; + let mut runtime = GetterRuntime::new(); + let mut context = RuntimeScriptContext::default(); + let mut outputs = Vec::new(); + for step in script.steps { + let data = execute_runtime_script_step(&mut runtime, &mut context, step)?; + outputs.push(data); + } + Ok(json!({ "steps": outputs })) +} + +#[derive(Default)] +struct RuntimeScriptContext { + last_action_id: Option, + last_task_id: Option, +} + +fn execute_runtime_script_step( + runtime: &mut GetterRuntime, + context: &mut RuntimeScriptContext, + step: RuntimeScriptStep, +) -> Result { + let operation = step.operation.as_str(); + let data = match operation { + "issue_action" => { + let plan = match step.plan { + Some(plan) => plan, + None => serde_json::from_value(step.payload).map_err(|source| { + CliError::Runtime(format!("failed to parse issue_action plan: {source}")) + })?, + }; + runtime_operations::issue_action(runtime, plan) + } + "submit_action" => runtime_json_operation( + runtime, + runtime_operations::submit_action_json, + default_action_payload(context, step.payload)?, + )?, + "task_get" => runtime_json_query_operation( + runtime, + runtime_operations::task_get_json, + default_task_payload(context, step.payload)?, + )?, + "task_list" => runtime_json_query_operation( + runtime, + runtime_operations::task_list_json, + empty_object_payload(step.payload), + )?, + "task_start" => runtime_json_operation( + runtime, + runtime_operations::task_start_json, + default_task_payload(context, step.payload)?, + )?, + "task_download_progress" => runtime_json_operation( + runtime, + runtime_operations::task_download_progress_json, + default_task_payload(context, step.payload)?, + )?, + "task_complete_download" => runtime_json_operation( + runtime, + runtime_operations::task_complete_download_json, + default_task_payload(context, step.payload)?, + )?, + "task_pause" => runtime_json_operation( + runtime, + runtime_operations::task_pause_json, + default_task_payload(context, step.payload)?, + )?, + "task_resume" => runtime_json_operation( + runtime, + runtime_operations::task_resume_json, + default_task_payload(context, step.payload)?, + )?, + "task_user_result" => runtime_json_operation( + runtime, + runtime_operations::task_user_result_json, + default_task_payload(context, step.payload)?, + )?, + "task_cancel" => runtime_json_operation( + runtime, + runtime_operations::task_cancel_json, + default_task_payload(context, step.payload)?, + )?, + "task_retry" => runtime_json_operation( + runtime, + runtime_operations::task_retry_json, + default_task_payload(context, step.payload)?, + )?, + "task_remove" => runtime_json_operation( + runtime, + runtime_operations::task_remove_json, + default_task_payload(context, step.payload)?, + )?, + "task_clean" => runtime_json_operation( + runtime, + runtime_operations::task_clean_json, + empty_object_payload(step.payload), + )?, + other => { + return Err(CliError::Runtime(format!( + "unsupported runtime script operation '{other}'" + ))) + } + }; + remember_runtime_script_ids(context, &data); + Ok(json!({ "operation": operation, "data": data })) +} + +fn runtime_json_operation( + runtime: &mut GetterRuntime, + operation: fn( + &mut GetterRuntime, + &str, + ) -> Result, + payload: Value, +) -> Result { + let payload = serde_json::to_string(&payload).map_err(|source| { + CliError::Runtime(format!("failed to serialize runtime request: {source}")) + })?; + operation(runtime, &payload).map_err(|source| CliError::Runtime(source.to_string())) +} + +fn runtime_json_query_operation( + runtime: &GetterRuntime, + operation: fn(&GetterRuntime, &str) -> Result, + payload: Value, +) -> Result { + let payload = serde_json::to_string(&payload).map_err(|source| { + CliError::Runtime(format!("failed to serialize runtime request: {source}")) + })?; + operation(runtime, &payload).map_err(|source| CliError::Runtime(source.to_string())) +} + +fn empty_object_payload(payload: Value) -> Value { + if payload.is_null() { + json!({}) + } else { + payload + } +} + +fn default_action_payload( + context: &RuntimeScriptContext, + payload: Value, +) -> Result { + if payload.is_null() { + let action_id = context.last_action_id.as_ref().ok_or_else(|| { + CliError::Runtime("runtime script has no previous action_id".to_owned()) + })?; + return Ok(json!({ "action_id": action_id })); + } + replace_runtime_script_tokens(context, payload) +} + +fn default_task_payload(context: &RuntimeScriptContext, payload: Value) -> Result { + if payload.is_null() { + let task_id = context.last_task_id.as_ref().ok_or_else(|| { + CliError::Runtime("runtime script has no previous task_id".to_owned()) + })?; + return Ok(json!({ "task_id": task_id })); + } + replace_runtime_script_tokens(context, payload) +} + +fn replace_runtime_script_tokens( + context: &RuntimeScriptContext, + payload: Value, +) -> Result { + match payload { + Value::String(value) if value == "$last_action_id" => { + Ok(Value::String(context.last_action_id.clone().ok_or_else( + || CliError::Runtime("runtime script has no previous action_id".to_owned()), + )?)) + } + Value::String(value) if value == "$last_task_id" => { + Ok(Value::String(context.last_task_id.clone().ok_or_else( + || CliError::Runtime("runtime script has no previous task_id".to_owned()), + )?)) + } + Value::Array(values) => values + .into_iter() + .map(|value| replace_runtime_script_tokens(context, value)) + .collect::, _>>() + .map(Value::Array), + Value::Object(values) => values + .into_iter() + .map(|(key, value)| Ok((key, replace_runtime_script_tokens(context, value)?))) + .collect::, CliError>>() + .map(Value::Object), + other => Ok(other), + } +} + +fn remember_runtime_script_ids(context: &mut RuntimeScriptContext, data: &Value) { + if let Some(action_id) = data.get("action_id").and_then(Value::as_str) { + context.last_action_id = Some(action_id.to_owned()); + } + if let Some(task_id) = data.get("task_id").and_then(Value::as_str) { + context.last_task_id = Some(task_id.to_owned()); + } +} + fn repo_path(repo: &StoredRepository) -> Result { repo.path .as_ref() @@ -1214,7 +1475,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |task submit --request |task run |task list|task cancel |task events --after --limit |task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1270,12 +1531,13 @@ impl CliCommand { Self::VersionPin { .. } => "version pin", Self::VersionUnpin { .. } => "version unpin", Self::UpdateCheck { .. } => "update check", - Self::TaskSubmit { .. } => "task submit", - Self::TaskRun { .. } => "task run", - Self::TaskList => "task list", - Self::TaskCancel { .. } => "task cancel", - Self::TaskEvents { .. } => "task events", - Self::TaskInstallResult { .. } => "task install-result", + Self::DebugFakeTaskSubmit { .. } => "debug fake-task submit", + Self::DebugFakeTaskRun { .. } => "debug fake-task run", + Self::DebugFakeTaskList => "debug fake-task list", + Self::DebugFakeTaskCancel { .. } => "debug fake-task cancel", + Self::DebugFakeTaskEvents { .. } => "debug fake-task events", + Self::DebugFakeTaskInstallResult { .. } => "debug fake-task install-result", + Self::RuntimeScript { .. } => "runtime script", Self::AutogenInstalledPreview { .. } => "autogen installed preview", Self::AutogenInstalledApply { .. } => "autogen installed apply", Self::AutogenCleanupPreview { .. } => "autogen cleanup preview", @@ -1337,6 +1599,167 @@ mod tests { ); } + #[test] + fn old_task_namespace_is_not_public_cli_surface() { + let output = run(["getter", "--data-dir", "/tmp/ua-getter", "task", "list"]); + assert_eq!(output.exit_code, ExitCode::Usage); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["error"]["code"], "cli.usage"); + } + + #[test] + fn parses_runtime_script_command() { + let parsed = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "runtime", + "script", + "--script", + "/tmp/runtime-script.json", + ]) + .unwrap(); + assert_eq!( + parsed.command, + CliCommand::RuntimeScript { + script: PathBuf::from("/tmp/runtime-script.json"), + } + ); + } + + #[test] + fn run_runtime_script_exercises_in_memory_task_remove_and_clean() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + let script = temp.path().join("runtime-script.json"); + fs::write( + &script, + serde_json::to_vec_pretty(&json!({ + "steps": [ + { + "operation": "issue_action", + "plan": { + "package_id": "android/org.fdroid.fdroid", + "actions": [ + { + "type": "download", + "url": "https://example.invalid/app.apk", + "file_name": "app.apk" + } + ], + "lua_object": { + "object_id": "debug:android/org.fdroid.fdroid", + "dependency_digest": "debug-digest" + } + } + }, + { "operation": "submit_action" }, + { "operation": "task_start" }, + { + "operation": "task_download_progress", + "payload": { + "task_id": "$last_task_id", + "current_bits": 10, + "total_bits": 20 + } + }, + { "operation": "task_complete_download" }, + { "operation": "task_remove" }, + { "operation": "task_clean", "payload": { "mode": "all_inactive" } } + ] + })) + .unwrap(), + ) + .unwrap(); + + let output = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "runtime".to_owned(), + "script".to_owned(), + "--script".to_owned(), + script.to_string_lossy().to_string(), + ]); + + assert_eq!(output.exit_code, ExitCode::Success); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["command"], "runtime script"); + let steps = json["data"]["steps"].as_array().unwrap(); + assert_eq!(steps[0]["data"]["action_id"], "action-1"); + assert_eq!(steps[1]["data"]["task_id"], "task-1"); + assert_eq!(steps[4]["data"]["status"], "completed"); + assert_eq!(steps[5]["data"]["task_id"], "task-1"); + assert_eq!(steps[6]["data"]["tasks"].as_array().unwrap().len(), 0); + } + + #[test] + fn runtime_script_does_not_preserve_tasks_across_cli_invocations() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + let create_script = temp.path().join("create-runtime-task.json"); + fs::write( + &create_script, + serde_json::to_vec_pretty(&json!({ + "steps": [ + { + "operation": "issue_action", + "plan": { + "package_id": "android/org.fdroid.fdroid", + "actions": [], + "lua_object": { + "object_id": "debug:android/org.fdroid.fdroid", + "dependency_digest": "debug-digest" + } + } + }, + { "operation": "submit_action" } + ] + })) + .unwrap(), + ) + .unwrap(); + let list_script = temp.path().join("list-runtime-tasks.json"); + fs::write( + &list_script, + serde_json::to_vec_pretty(&json!({ + "steps": [{ "operation": "task_list" }] + })) + .unwrap(), + ) + .unwrap(); + + let create = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "runtime".to_owned(), + "script".to_owned(), + "--script".to_owned(), + create_script.to_string_lossy().to_string(), + ]); + assert_eq!(create.exit_code, ExitCode::Success); + + let list = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "runtime".to_owned(), + "script".to_owned(), + "--script".to_owned(), + list_script.to_string_lossy().to_string(), + ]); + assert_eq!(list.exit_code, ExitCode::Success); + let json: Value = serde_json::from_str(&list.stdout).unwrap(); + assert_eq!( + json["data"]["steps"][0]["data"]["tasks"] + .as_array() + .unwrap() + .len(), + 0 + ); + } + #[test] fn run_init_creates_sqlite_database_files_and_json_envelope() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 733827f..c721b01 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -16,6 +16,7 @@ struct CliWorld { autogen_preview: Option, update_fixture: Option, task_request: Option, + runtime_script: Option, remembered_task_id: Option, remembered_event_cursor: Option, remembered_handoff_id: Option, @@ -303,6 +304,44 @@ fn malformed_offline_download_request(world: &mut CliWorld) { world.task_request = Some(request); } +#[given("a runtime script that submits completes removes and cleans a task")] +fn runtime_script_submits_completes_removes_and_cleans_task(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let script = temp.path().join("runtime-script.json"); + fs::write( + &script, + serde_json::to_vec_pretty(&serde_json::json!({ + "steps": [ + { + "operation": "issue_action", + "plan": { + "package_id": "android/org.fdroid.fdroid", + "actions": [ + { + "type": "download", + "url": "https://example.invalid/app.apk", + "file_name": "app.apk" + } + ], + "lua_object": { + "object_id": "debug:android/org.fdroid.fdroid", + "dependency_digest": "debug-digest" + } + } + }, + { "operation": "submit_action" }, + { "operation": "task_start" }, + { "operation": "task_complete_download" }, + { "operation": "task_remove" }, + { "operation": "task_clean", "payload": { "mode": "all_inactive" } } + ] + })) + .expect("runtime script serializes"), + ) + .expect("write runtime script"); + world.runtime_script = Some(script); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -562,13 +601,14 @@ fn run_getter_version_unpin(world: &mut CliWorld, package_id: String) { world.json = None; } -#[when("I run getter task submit for that request")] -fn run_getter_task_submit(world: &mut CliWorld) { +#[when("I run getter debug fake-task submit for that request")] +fn run_getter_debug_fake_task_submit(world: &mut CliWorld) { let request = world.task_request.as_ref().expect("task request exists"); let output = run_getter( world, [ - "task".to_owned(), + "debug".to_owned(), + "fake-task".to_owned(), "submit".to_owned(), "--request".to_owned(), request.to_string_lossy().to_string(), @@ -578,43 +618,67 @@ fn run_getter_task_submit(world: &mut CliWorld) { world.json = None; } -#[when("I run getter task list")] -fn run_getter_task_list(world: &mut CliWorld) { - let output = run_getter(world, ["task".to_owned(), "list".to_owned()]); +#[when("I run getter debug fake-task list")] +fn run_getter_debug_fake_task_list(world: &mut CliWorld) { + let output = run_getter( + world, + [ + "debug".to_owned(), + "fake-task".to_owned(), + "list".to_owned(), + ], + ); world.output = Some(output); world.json = None; } -#[when("I run getter task cancel for the remembered task")] -fn run_getter_task_cancel(world: &mut CliWorld) { +#[when("I run getter debug fake-task cancel for the remembered task")] +fn run_getter_debug_fake_task_cancel(world: &mut CliWorld) { let task_id = world .remembered_task_id .as_ref() .expect("remembered task id exists") .clone(); - let output = run_getter(world, ["task".to_owned(), "cancel".to_owned(), task_id]); + let output = run_getter( + world, + [ + "debug".to_owned(), + "fake-task".to_owned(), + "cancel".to_owned(), + task_id, + ], + ); world.output = Some(output); world.json = None; } -#[when("I run getter task run for the remembered task")] -fn run_getter_task_run(world: &mut CliWorld) { +#[when("I run getter debug fake-task run for the remembered task")] +fn run_getter_debug_fake_task_run(world: &mut CliWorld) { let task_id = world .remembered_task_id .as_ref() .expect("remembered task id exists") .clone(); - let output = run_getter(world, ["task".to_owned(), "run".to_owned(), task_id]); + let output = run_getter( + world, + [ + "debug".to_owned(), + "fake-task".to_owned(), + "run".to_owned(), + task_id, + ], + ); world.output = Some(output); world.json = None; } -#[when(expr = "I run getter task events after {int} limit {int}")] -fn run_getter_task_events_after_limit(world: &mut CliWorld, after: u64, limit: u64) { +#[when(expr = "I run getter debug fake-task events after {int} limit {int}")] +fn run_getter_debug_fake_task_events_after_limit(world: &mut CliWorld, after: u64, limit: u64) { let output = run_getter( world, [ - "task".to_owned(), + "debug".to_owned(), + "fake-task".to_owned(), "events".to_owned(), "--after".to_owned(), after.to_string(), @@ -626,16 +690,16 @@ fn run_getter_task_events_after_limit(world: &mut CliWorld, after: u64, limit: u world.json = None; } -#[when(expr = "I run getter task events after the remembered cursor limit {int}")] -fn run_getter_task_events_after_remembered_cursor(world: &mut CliWorld, limit: u64) { +#[when(expr = "I run getter debug fake-task events after the remembered cursor limit {int}")] +fn run_getter_debug_fake_task_events_after_remembered_cursor(world: &mut CliWorld, limit: u64) { let after = world .remembered_event_cursor .expect("remembered event cursor exists"); - run_getter_task_events_after_limit(world, after, limit); + run_getter_debug_fake_task_events_after_limit(world, after, limit); } -#[when(expr = "I run getter task install-result {string} for the remembered handoff")] -fn run_getter_task_install_result(world: &mut CliWorld, status: String) { +#[when(expr = "I run getter debug fake-task install-result {string} for the remembered handoff")] +fn run_getter_debug_fake_task_install_result(world: &mut CliWorld, status: String) { let handoff_id = world .remembered_handoff_id .as_ref() @@ -644,7 +708,8 @@ fn run_getter_task_install_result(world: &mut CliWorld, status: String) { let output = run_getter( world, [ - "task".to_owned(), + "debug".to_owned(), + "fake-task".to_owned(), "install-result".to_owned(), handoff_id, "--status".to_owned(), @@ -655,6 +720,25 @@ fn run_getter_task_install_result(world: &mut CliWorld, status: String) { world.json = None; } +#[when("I run getter runtime script for that script")] +fn run_getter_runtime_script(world: &mut CliWorld) { + let script = world + .runtime_script + .as_ref() + .expect("runtime script exists"); + let output = run_getter( + world, + [ + "runtime".to_owned(), + "script".to_owned(), + "--script".to_owned(), + script.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen installed preview for that inventory")] fn run_getter_autogen_installed_preview(world: &mut CliWorld) { let inventory = world.inventory.as_ref().expect("inventory exists"); @@ -892,7 +976,7 @@ fn output_contains_empty_repository_list(world: &mut CliWorld) { #[then("I remember the submitted task id")] fn remember_submitted_task_id(world: &mut CliWorld) { let json = current_json(world); - assert_eq!(json["command"], "task submit"); + assert_eq!(json["command"], "debug fake-task submit"); let task_id = json["data"]["task"]["id"] .as_str() .expect("task id should be a string") @@ -900,6 +984,20 @@ fn remember_submitted_task_id(world: &mut CliWorld) { world.remembered_task_id = Some(task_id); } +#[then("the runtime script output removes the completed task")] +fn runtime_script_output_removes_completed_task(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["command"], "runtime script"); + let steps = json["data"]["steps"].as_array().expect("runtime steps"); + assert_eq!(steps[0]["data"]["action_id"], "action-1"); + assert_eq!(steps[1]["data"]["task_id"], "task-1"); + assert_eq!(steps[3]["data"]["status"], "completed"); + assert_eq!(steps[4]["operation"], "task_remove"); + assert_eq!(steps[4]["data"]["task_id"], "task-1"); + assert_eq!(steps[5]["operation"], "task_clean"); + assert_eq!(steps[5]["data"]["tasks"].as_array().unwrap().len(), 0); +} + #[then(expr = "the task list contains the remembered task with status {string}")] fn task_list_contains_remembered_task_with_status(world: &mut CliWorld, status: String) { let task_id = world @@ -908,7 +1006,7 @@ fn task_list_contains_remembered_task_with_status(world: &mut CliWorld, status: .expect("remembered task id exists") .clone(); let json = current_json(world); - assert_eq!(json["command"], "task list"); + assert_eq!(json["command"], "debug fake-task list"); let tasks = json["data"]["tasks"].as_array().expect("tasks array"); let task = tasks .iter() @@ -920,7 +1018,7 @@ fn task_list_contains_remembered_task_with_status(world: &mut CliWorld, status: #[then(expr = "the task cancel result has status {string} and changed true")] fn task_cancel_result_changed_true(world: &mut CliWorld, status: String) { let json = current_json(world); - assert_eq!(json["command"], "task cancel"); + assert_eq!(json["command"], "debug fake-task cancel"); assert_eq!(json["data"]["status"], status); assert_eq!(json["data"]["changed"], true); } @@ -928,7 +1026,7 @@ fn task_cancel_result_changed_true(world: &mut CliWorld, status: String) { #[then(expr = "the task cancel result has status {string} and changed false")] fn task_cancel_result_changed_false(world: &mut CliWorld, status: String) { let json = current_json(world); - assert_eq!(json["command"], "task cancel"); + assert_eq!(json["command"], "debug fake-task cancel"); assert_eq!(json["data"]["status"], status); assert_eq!(json["data"]["changed"], false); } @@ -940,7 +1038,7 @@ fn task_run_result_has_status_and_install_handoff( handoff_status: String, ) { let json = current_json(world); - assert_eq!(json["command"], "task run"); + assert_eq!(json["command"], "debug fake-task run"); assert_eq!(json["data"]["task"]["status"], status); assert_eq!(json["data"]["install_handoff"]["status"], handoff_status); } @@ -948,7 +1046,7 @@ fn task_run_result_has_status_and_install_handoff( #[then(expr = "the task events output contains {int} events and has more events")] fn task_events_output_contains_events_and_has_more(world: &mut CliWorld, count: usize) { let json = current_json(world); - assert_eq!(json["command"], "task events"); + assert_eq!(json["command"], "debug fake-task events"); assert_eq!(json["data"]["events"].as_array().unwrap().len(), count); assert_eq!(json["data"]["has_more"], true); } @@ -989,7 +1087,7 @@ fn remember_install_handoff_id(world: &mut CliWorld) { #[then(expr = "the install result output has status {string}")] fn install_result_output_has_status(world: &mut CliWorld, status: String) { let json = current_json(world); - assert_eq!(json["command"], "task install-result"); + assert_eq!(json["command"], "debug fake-task install-result"); assert_eq!(json["data"]["handoff"]["status"], status); } diff --git a/crates/getter-cli/tests/features/cli/task_lifecycle.feature b/crates/getter-cli/tests/features/cli/task_lifecycle.feature index 6927548..34f71f9 100644 --- a/crates/getter-cli/tests/features/cli/task_lifecycle.feature +++ b/crates/getter-cli/tests/features/cli/task_lifecycle.feature @@ -1,89 +1,97 @@ -@getter-cli @task -Feature: Offline task lifecycle +@getter-cli @debug-fake-task +Feature: Offline fake task lifecycle + Scenario: Runtime script exercises ADR-0011 in-memory remove and clean + Given an initialized getter data directory + And a runtime script that submits completes removes and cleans a task + When I run getter runtime script for that script + Then the command succeeds + And the output is valid JSON + And the runtime script output removes the completed task + Scenario: User submits and lists an offline fake download task Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And the output is valid JSON And I remember the submitted task id - When I run getter task list + When I run getter debug fake-task list Then the command succeeds And the task list contains the remembered task with status "queued" Scenario: User cancels a queued offline task Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And I remember the submitted task id - When I run getter task cancel for the remembered task + When I run getter debug fake-task cancel for the remembered task Then the command succeeds And the task cancel result has status "canceled" and changed true - When I run getter task cancel for the remembered task + When I run getter debug fake-task cancel for the remembered task Then the command succeeds And the task cancel result has status "canceled" and changed false Scenario: User cannot cancel a succeeded offline task Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And I remember the submitted task id - When I run getter task run for the remembered task + When I run getter debug fake-task run for the remembered task Then the command succeeds And the task run result has status "succeeded" and install handoff "requested" - When I run getter task cancel for the remembered task + When I run getter debug fake-task cancel for the remembered task Then the command fails with a download task error Scenario: User polls offline task events with cursor and limit Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And I remember the submitted task id - When I run getter task run for the remembered task + When I run getter debug fake-task run for the remembered task Then the command succeeds - When I run getter task events after 0 limit 2 + When I run getter debug fake-task events after 0 limit 2 Then the command succeeds And the task events output contains 2 events and has more events And I remember the next event cursor - When I run getter task events after the remembered cursor limit 10 + When I run getter debug fake-task events after the remembered cursor limit 10 Then the command succeeds And the task events output contains event "install_handoff_requested" Scenario: User records an offline install handoff result Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And I remember the submitted task id - When I run getter task run for the remembered task + When I run getter debug fake-task run for the remembered task Then the command succeeds And I remember the install handoff id - When I run getter task install-result "succeeded" for the remembered handoff + When I run getter debug fake-task install-result "succeeded" for the remembered handoff Then the command succeeds And the install result output has status "succeeded" Scenario: User cannot record getter-created requested state as an install result Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And I remember the submitted task id - When I run getter task run for the remembered task + When I run getter debug fake-task run for the remembered task Then the command succeeds And I remember the install handoff id - When I run getter task install-result "requested" for the remembered handoff + When I run getter debug fake-task install-result "requested" for the remembered handoff Then the command fails with a CLI usage error Scenario: User cannot poll task events with a zero limit Given an initialized getter data directory - When I run getter task events after 0 limit 0 + When I run getter debug fake-task events after 0 limit 0 Then the command fails with a CLI usage error Scenario: User receives structured errors for malformed task requests Given an initialized getter data directory And a malformed offline download request - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command fails with a download task error From 1cde4f4bf01c5f703724f4ca62cc89496886eedd Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 13:55:29 +0800 Subject: [PATCH 26/72] feat(providers): wrap static updates as mock provider --- Cargo.lock | 1 + crates/getter-operations/Cargo.toml | 1 + crates/getter-operations/src/runtime.rs | 5 ++- crates/getter-providers/Cargo.toml | 2 +- crates/getter-providers/src/lib.rs | 54 ++++++++++++++++++++++++- 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e033e93..eefb4be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -565,6 +565,7 @@ name = "getter-operations" version = "0.1.0" dependencies = [ "getter-core", + "getter-providers", "getter-storage", "serde", "serde_json", diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index a36ba74..c81da6b 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -9,6 +9,7 @@ lua = ["getter-core/lua"] [dependencies] getter-core = { path = "../getter-core", default-features = false } +getter-providers = { path = "../getter-providers" } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index f6bfa38..1392322 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -22,6 +22,8 @@ use getter_core::{ #[cfg(feature = "lua")] use getter_core::{update::check_updates_offline, update::UpdateSelectionPolicy, RepositoryId}; #[cfg(feature = "lua")] +use getter_providers::StaticPackageUpdatesProvider; +#[cfg(feature = "lua")] use getter_storage::{MainDb, StorageError}; use serde::Deserialize; use serde_json::{json, Value}; @@ -135,10 +137,11 @@ pub fn issue_action_from_registered_package_json( ) -> Result { let request: RegisteredPackageUpdateActionRequest = parse_request(request_json)?; let (package, dependency_digest) = evaluate_registered_package(db, &request)?; + let candidates = StaticPackageUpdatesProvider.check_updates(&package); let update = check_updates_offline( package.id.clone(), request.installed_version, - package.updates.clone(), + candidates, UpdateSelectionPolicy { pin_version: request.pin_version, }, diff --git a/crates/getter-providers/Cargo.toml b/crates/getter-providers/Cargo.toml index 5a44be9..f5ae23c 100644 --- a/crates/getter-providers/Cargo.toml +++ b/crates/getter-providers/Cargo.toml @@ -4,4 +4,4 @@ version.workspace = true edition.workspace = true [dependencies] -getter-core = { path = "../getter-core" } +getter-core = { path = "../getter-core", default-features = false } diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index 35371aa..ee714b9 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -1,3 +1,55 @@ -//! getter-providers rewrite crate skeleton. +//! Provider executor scaffolding for the UpgradeAll getter rewrite. +//! +//! Real network/provider execution is intentionally deferred to a later ADR. +//! The first Phase D bridge uses package-declared static update candidates as a +//! mock provider so the rest of the runtime can exercise getter-owned update +//! selection and opaque action issuance without direct network side effects. pub use getter_core as core; + +use getter_core::{ResolvedPackage, UpdateCandidate}; + +/// Mock provider that returns the static `updates` candidates materialized from +/// a resolved Lua package table. +/// +/// This is development scaffolding, not the final live provider model. Keeping +/// it behind a provider-shaped boundary prevents operation code from treating +/// `package.updates` as the product update-check architecture. +#[derive(Debug, Default, Clone, Copy)] +pub struct StaticPackageUpdatesProvider; + +impl StaticPackageUpdatesProvider { + pub fn check_updates(self, package: &ResolvedPackage) -> Vec { + package.updates.clone() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::PackagePermissions; + + #[test] + fn static_provider_returns_package_declared_update_candidates() { + let package = ResolvedPackage { + id: "android/org.fdroid.fdroid".parse().unwrap(), + name: "F-Droid".to_owned(), + repository: "official".parse().unwrap(), + installed: Vec::new(), + permissions: PackagePermissions::default(), + source_priority: Vec::new(), + updates: vec![UpdateCandidate { + version: "1.2.0".to_owned(), + channel: Some("stable".to_owned()), + source: Some("fixture".to_owned()), + artifacts: Vec::new(), + }], + }; + + let candidates = StaticPackageUpdatesProvider.check_updates(&package); + + assert_eq!(candidates.len(), 1); + assert_eq!(candidates[0].version, "1.2.0"); + assert_eq!(candidates[0].source.as_deref(), Some("fixture")); + } +} From 60a65158aed40e14f0dea17427b4a92ec1e43818 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 18:09:36 +0800 Subject: [PATCH 27/72] feat(operations): expose read model operations --- crates/getter-operations/src/lib.rs | 1 + crates/getter-operations/src/read_model.rs | 325 +++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 crates/getter-operations/src/read_model.rs diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 6d75c88..4bd9337 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -7,4 +7,5 @@ pub mod autogen; pub mod legacy_room; +pub mod read_model; pub mod runtime; diff --git a/crates/getter-operations/src/read_model.rs b/crates/getter-operations/src/read_model.rs new file mode 100644 index 0000000..898864b --- /dev/null +++ b/crates/getter-operations/src/read_model.rs @@ -0,0 +1,325 @@ +//! Getter-owned read-model JSON operations for product UI snapshots. +//! +//! Flutter and Android bridge code should request these operations instead of +//! fabricating app/repository state in the UI layer. The operations read the +//! getter storage/repository/Lua model and return DTO-shaped JSON for adapters +//! to parse and render. + +#[cfg(feature = "lua")] +use getter_core::lua::evaluate_package_file; +#[cfg(feature = "lua")] +use getter_core::repository::{RepositoryLayout, RepositoryLoadError}; +#[cfg(feature = "lua")] +use getter_core::{PackageId, RepositoryId}; +use getter_storage::{MainDb, StorageError, StoredRepository, StoredTrackedPackage}; +#[cfg(feature = "lua")] +use serde::Deserialize; +use serde_json::{json, Value}; +use std::path::{Path, PathBuf}; + +const MAIN_DB_FILE: &str = "main.db"; + +#[derive(Debug, thiserror::Error)] +pub enum ReadModelOperationError { + #[error("invalid read-model request: {0}")] + InvalidRequest(String), + #[error("storage operation failed: {0}")] + Storage(#[from] StorageError), + #[cfg(feature = "lua")] + #[error("repository operation failed: {0}")] + Repository(#[from] RepositoryLoadError), + #[cfg(feature = "lua")] + #[error("package evaluation failed: {0}")] + PackageEval(String), + #[error("read-model response serialization failed: {0}")] + Serialize(String), +} + +impl ReadModelOperationError { + pub fn code(&self) -> &'static str { + match self { + Self::InvalidRequest(_) => "read_model.invalid_request", + Self::Storage(_) => "storage.error", + #[cfg(feature = "lua")] + Self::Repository(_) => "repository.error", + #[cfg(feature = "lua")] + Self::PackageEval(_) => "package.eval_error", + Self::Serialize(_) => "read_model.serialize_error", + } + } + + pub fn message(&self) -> &'static str { + match self { + Self::InvalidRequest(_) => "Getter read-model request is invalid", + Self::Storage(_) => "Getter storage operation failed", + #[cfg(feature = "lua")] + Self::Repository(_) => "Getter repository operation failed", + #[cfg(feature = "lua")] + Self::PackageEval(_) => "Getter package evaluation failed", + Self::Serialize(_) => "Getter read-model response serialization failed", + } + } + + pub fn detail(&self) -> Option { + match self { + Self::InvalidRequest(detail) | Self::Serialize(detail) => Some(detail.clone()), + Self::Storage(error) => Some(error.to_string()), + #[cfg(feature = "lua")] + Self::Repository(error) => Some(error.to_string()), + #[cfg(feature = "lua")] + Self::PackageEval(detail) => Some(detail.clone()), + } + } +} + +pub fn repository_list_json(data_dir: &Path) -> Result { + let db = open_main_db(data_dir)?; + Ok(json!({ + "repositories": db + .repositories()? + .into_iter() + .map(repository_json) + .collect::>(), + })) +} + +pub fn tracked_package_list_json(data_dir: &Path) -> Result { + let db = open_main_db(data_dir)?; + Ok(json!({ + "packages": db + .tracked_packages()? + .into_iter() + .map(tracked_package_json) + .collect::>(), + })) +} + +#[cfg(feature = "lua")] +pub fn package_eval_json( + data_dir: &Path, + request_json: &str, +) -> Result { + let request: PackageEvalRequest = parse_request(request_json)?; + let db = open_main_db(data_dir)?; + let package = match request.repository_id { + Some(repository_id) => { + evaluate_package_from_repo(&db, &repository_id, &request.package_id)? + } + None => evaluate_highest_priority_package(&db, &request.package_id)?, + }; + let package = serde_json::to_value(package) + .map_err(|source| ReadModelOperationError::Serialize(source.to_string()))?; + Ok(json!({ "package": package })) +} + +#[cfg(feature = "lua")] +#[derive(Debug, Deserialize)] +struct PackageEvalRequest { + package_id: PackageId, + #[serde(default)] + repository_id: Option, +} + +#[cfg(feature = "lua")] +fn parse_request(request_json: &str) -> Result +where + T: for<'de> Deserialize<'de>, +{ + serde_json::from_str(request_json) + .map_err(|source| ReadModelOperationError::InvalidRequest(source.to_string())) +} + +fn open_main_db(data_dir: &Path) -> Result { + Ok(MainDb::open(main_db_path(data_dir))?) +} + +#[cfg(feature = "lua")] +fn evaluate_package_from_repo( + db: &MainDb, + repo_id: &RepositoryId, + package_id: &PackageId, +) -> Result { + let repo = find_repository(db, repo_id)?; + let path = repo_path(&repo)?; + let layout = RepositoryLayout::load(&path)?; + let package_file = layout.package_file(package_id).ok_or_else(|| { + ReadModelOperationError::PackageEval(format!( + "package '{}' was not found in repository '{}'", + package_id, repo_id + )) + })?; + evaluate_package_file(&layout, &package_file.path) + .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())) +} + +#[cfg(feature = "lua")] +fn evaluate_highest_priority_package( + db: &MainDb, + package_id: &PackageId, +) -> Result { + for repo in db.repositories()? { + let path = repo_path(&repo)?; + let layout = RepositoryLayout::load(&path)?; + if let Some(package_file) = layout.package_file(package_id) { + return evaluate_package_file(&layout, &package_file.path) + .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())); + } + } + Err(ReadModelOperationError::PackageEval(format!( + "package '{package_id}' was not found in any registered repository" + ))) +} + +#[cfg(feature = "lua")] +fn find_repository( + db: &MainDb, + id: &RepositoryId, +) -> Result { + db.repositories()? + .into_iter() + .find(|repo| &repo.id == id) + .ok_or_else(|| { + ReadModelOperationError::PackageEval(format!("repository '{id}' is not registered")) + }) +} + +#[cfg(feature = "lua")] +fn repo_path(repo: &StoredRepository) -> Result { + repo.path.as_ref().map(PathBuf::from).ok_or_else(|| { + ReadModelOperationError::PackageEval(format!("repository '{}' has no path", repo.id)) + }) +} + +fn repository_json(repo: StoredRepository) -> Value { + json!({ + "id": repo.id.as_str(), + "name": repo.name, + "priority": repo.priority.value(), + "api_version": repo.api_version, + "path": repo.path, + "revision": repo.revision, + }) +} + +fn tracked_package_json(package: StoredTrackedPackage) -> Value { + json!({ + "id": package.package_id.to_string(), + "enabled": package.enabled, + "favorite": package.favorite, + "pin_version": package.pin_version, + "repository_id": package.repository_id.map(|id| id.to_string()), + "package_resolution": package.package_resolution.as_str(), + }) +} + +fn main_db_path(data_dir: &Path) -> PathBuf { + data_dir.join(MAIN_DB_FILE) +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; + use getter_core::{RepositoryId, RepositoryPriority}; + use getter_storage::{StoredPackageResolution, TrackedPackageUpsert}; + #[cfg(feature = "lua")] + use std::fs; + use tempfile::tempdir; + + #[test] + fn repository_and_tracked_package_lists_use_getter_storage_shapes() { + let temp = tempdir().unwrap(); + let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); + let repo_id: RepositoryId = "official".parse().unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: repo_id.clone(), + name: "Official".to_owned(), + priority: RepositoryPriority::new(10), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(Path::new("/tmp/official")), + Some("rev-1"), + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + enabled: true, + favorite: true, + pin_version: Some("1.2.3".to_owned()), + repository_id: Some(repo_id), + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + let repositories = repository_list_json(temp.path()).unwrap(); + assert_eq!(repositories["repositories"][0]["id"], "official"); + assert_eq!(repositories["repositories"][0]["name"], "Official"); + assert_eq!(repositories["repositories"][0]["priority"], 10); + assert_eq!(repositories["repositories"][0]["revision"], "rev-1"); + + let packages = tracked_package_list_json(temp.path()).unwrap(); + assert_eq!(packages["packages"][0]["id"], "android/org.fdroid.fdroid"); + assert_eq!(packages["packages"][0]["favorite"], true); + assert_eq!(packages["packages"][0]["pin_version"], "1.2.3"); + assert_eq!(packages["packages"][0]["repository_id"], "official"); + assert_eq!( + packages["packages"][0]["package_resolution"], + "official_repository_package" + ); + } + + #[cfg(feature = "lua")] + #[test] + fn package_eval_reads_registered_lua_repository() { + let temp = tempdir().unwrap(); + let repo_path = temp.path().join("repo"); + fs::create_dir_all(repo_path.join("packages/android")).unwrap(); + fs::create_dir_all(repo_path.join("lib")).unwrap(); + fs::create_dir_all(repo_path.join("templates")).unwrap(); + fs::write( + repo_path.join("repo.toml"), + r#"id = "official" +name = "Official" +priority = 10 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::write( + repo_path.join("packages/android/org.fdroid.fdroid.lua"), + r#"return package_def { + id = "android/org.fdroid.fdroid", + name = "F-Droid", + installed = { + { kind = "android_package", package_name = "org.fdroid.fdroid" }, + }, + permissions = { free_network = true }, +} +"#, + ) + .unwrap(); + + let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::new(10), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + ) + .unwrap(); + + let result = package_eval_json( + temp.path(), + r#"{"package_id":"android/org.fdroid.fdroid","repository_id":"official"}"#, + ) + .unwrap(); + assert_eq!(result["package"]["id"], "android/org.fdroid.fdroid"); + assert_eq!(result["package"]["name"], "F-Droid"); + assert_eq!(result["package"]["permissions"]["free_network"], true); + } +} From 3d268807a1a23e38789434a7e0a0659692ae8897 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Fri, 26 Jun 2026 18:26:34 +0800 Subject: [PATCH 28/72] feat(repository): load ADR-0012 data-dir config --- Cargo.lock | 7 + crates/getter-cli/src/lib.rs | 54 ++- crates/getter-cli/tests/bdd_cli.rs | 60 +-- .../features/cli/autogen_installed.feature | 10 +- crates/getter-core/Cargo.toml | 1 + crates/getter-core/src/autogen.rs | 62 +-- crates/getter-core/src/diagnostics.rs | 29 ++ crates/getter-core/src/lib.rs | 13 +- crates/getter-core/src/repository.rs | 388 +++++++++++++++++- crates/getter-operations/src/autogen.rs | 284 ++++++++++--- crates/getter-storage/src/lib.rs | 30 +- 11 files changed, 792 insertions(+), 146 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eefb4be..23480e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -536,6 +536,7 @@ dependencies = [ name = "getter-core" version = "0.1.0" dependencies = [ + "json_comments", "mlua", "serde", "serde_json", @@ -788,6 +789,12 @@ dependencies = [ "syn", ] +[[package]] +name = "json_comments" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbbfed4e59ba9750e15ba154fdfd9329cee16ff3df539c2666b70f58cc32105" + [[package]] name = "libc" version = "0.2.186" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index d8317fe..020e1f9 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -7,7 +7,9 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; -use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; +use getter_core::repository::{ + GetterDataDirLayout, RepositoryLayout, RepositoryMetadata, REPOSITORY_ROOT_METADATA_FILE, +}; use getter_core::runtime::{GetterRuntime, SealedActionPlan}; use getter_core::task::{ DownloadTaskRequest, InstallHandoffStatus, TaskEventPage, DOWNLOAD_REQUEST_FORMAT, @@ -37,6 +39,18 @@ const MAIN_DB_FILE: &str = "main.db"; const CACHE_DB_FILE: &str = "cache.db"; const MIGRATION_REPORTS_DIR: &str = "migration-reports"; const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; +const REPOSITORY_ROOT_METADATA_STARTER: &str = r#"{ + "version": 1, + // Autogen writes to "autogen" by default. Uncomment and change this + // if generated packages should target another existing repository alias. + // "generated_repository": "autogen", + "priority": { + "local": 100, + "official": 0, + "autogen": -1 + } +} +"#; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliInvocation { @@ -264,6 +278,9 @@ impl From for CliError { match value { AutogenOperationError::Storage(source) => Self::Storage(source.to_string()), AutogenOperationError::Repository(detail) => Self::Repository(detail), + AutogenOperationError::MissingGeneratedRepository { .. } => { + Self::Autogen(value.to_string()) + } AutogenOperationError::Autogen(detail) => Self::Autogen(detail), } } @@ -547,10 +564,14 @@ fn execute(invocation: CliInvocation) -> Result { match invocation.command { CliCommand::Init => { initialize_storage(&invocation.data_dir)?; + let layout = initialize_data_dir_layout(&invocation.data_dir)?; Ok(json!({ - "data_dir": invocation.data_dir, - "main_db": main_db_path(&invocation.data_dir), - "cache_db": cache_db_path(&invocation.data_dir), + "data_dir": layout.root, + "main_db": layout.main_db, + "cache_db": layout.cache_db, + "repo": layout.repository_root, + "rc": layout.runtime_config_root, + "repo_metadata": layout.repository_root.join(REPOSITORY_ROOT_METADATA_FILE), })) } CliCommand::AppList => { @@ -691,8 +712,9 @@ fn execute(invocation: CliInvocation) -> Result { CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; - let plan = autogen::build_local_autogen_plan(&db, &inventory)?; - Ok(autogen::installed_preview_json(&invocation.data_dir, &plan)) + let plan = + autogen::build_installed_autogen_plan(&invocation.data_dir, &db, &inventory)?; + autogen::installed_preview_json(&invocation.data_dir, &plan).map_err(CliError::from) } CliCommand::AutogenInstalledApply { preview, @@ -807,6 +829,26 @@ fn initialize_storage(data_dir: &Path) -> Result<(), CliError> { Ok(()) } +fn initialize_data_dir_layout(data_dir: &Path) -> Result { + let layout = GetterDataDirLayout::new(data_dir); + fs::create_dir_all(&layout.repository_root).map_err(|source| { + CliError::Storage(format!("failed to create repository root: {source}")) + })?; + fs::create_dir_all(&layout.runtime_config_root).map_err(|source| { + CliError::Storage(format!("failed to create runtime config root: {source}")) + })?; + let metadata_path = layout.repository_root.join(REPOSITORY_ROOT_METADATA_FILE); + if !metadata_path.exists() { + fs::write(&metadata_path, REPOSITORY_ROOT_METADATA_STARTER).map_err(|source| { + CliError::Storage(format!( + "failed to write repository root metadata '{}': {source}", + metadata_path.display() + )) + })?; + } + Ok(layout) +} + fn open_initialized_storage(data_dir: &Path) -> Result<(), CliError> { initialize_storage(data_dir) } diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index c721b01..073bb63 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -167,8 +167,8 @@ fn tampered_autogen_cleanup_preview_for_package(world: &mut CliWorld, package_id &preview, serde_json::to_vec_pretty(&serde_json::json!({ "operation": "cleanup.preview", - "target_repo_id": "local_autogen", - "target_repo_path": local_autogen_repo_path(world), + "target_repo_id": "autogen", + "target_repo_path": autogen_repo_path(world), "summary": { "candidate_count": 1, "skipped_count": 0, @@ -815,9 +815,9 @@ fn run_getter_autogen_cleanup_apply_accept_all(world: &mut CliWorld) { world.json = None; } -#[when("I run getter repo validate for local_autogen")] -fn run_getter_repo_validate_for_local_autogen(world: &mut CliWorld) { - let repo_path = local_autogen_repo_path(world); +#[when("I run getter repo validate for autogen")] +fn run_getter_repo_validate_for_autogen(world: &mut CliWorld) { + let repo_path = autogen_repo_path(world); let output = run_getter( world, [ @@ -830,8 +830,8 @@ fn run_getter_repo_validate_for_local_autogen(world: &mut CliWorld) { world.json = None; } -#[when(expr = "I run getter package eval for package {string} from local_autogen")] -fn run_getter_package_eval_from_local_autogen(world: &mut CliWorld, package_id: String) { +#[when(expr = "I run getter package eval for package {string} from autogen")] +fn run_getter_package_eval_from_autogen(world: &mut CliWorld, package_id: String) { let output = run_getter( world, [ @@ -839,7 +839,7 @@ fn run_getter_package_eval_from_local_autogen(world: &mut CliWorld, package_id: "eval".to_owned(), package_id, "--repo".to_owned(), - "local_autogen".to_owned(), + "autogen".to_owned(), ], ); world.output = Some(output); @@ -944,6 +944,13 @@ fn getter_data_directory_is_usable(world: &mut CliWorld) { let data_dir = world.data_dir.as_ref().expect("data dir exists"); assert!(data_dir.join("main.db").is_file(), "main.db should exist"); assert!(data_dir.join("cache.db").is_file(), "cache.db should exist"); + assert!(data_dir.join("repo").is_dir(), "repo root should exist"); + assert!(data_dir.join("rc").is_dir(), "rc root should exist"); + let repo_metadata = data_dir.join("repo/metadata.jsonc"); + assert!(repo_metadata.is_file(), "repo metadata should exist"); + let metadata = fs::read_to_string(repo_metadata).expect("repo metadata readable"); + assert!(metadata.contains("\"autogen\": -1")); + assert!(metadata.contains("// \"generated_repository\": \"autogen\"")); let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); assert_success(&output); @@ -1259,11 +1266,11 @@ fn autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) ); } -#[then("the local_autogen repository has not been written")] -fn local_autogen_repository_has_not_been_written(world: &mut CliWorld) { +#[then("the autogen repository has not been written")] +fn autogen_repository_has_not_been_written(world: &mut CliWorld) { assert!( - !local_autogen_repo_path(world).exists(), - "preview must not create local_autogen" + !autogen_repo_path(world).exists(), + "preview must not create autogen repository" ); } @@ -1283,12 +1290,12 @@ fn save_autogen_preview_to_file(world: &mut CliWorld) { world.autogen_preview = Some(preview); } -#[then(expr = "the local_autogen repository contains generated package {string}")] -fn local_autogen_repository_contains_generated_package(world: &mut CliWorld, package_id: String) { - let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); +#[then(expr = "the autogen repository contains generated package {string}")] +fn autogen_repository_contains_generated_package(world: &mut CliWorld, package_id: String) { + let path = autogen_repo_path(world).join(package_relative_path(&package_id)); assert!(path.is_file(), "generated package should exist: {path:?}"); let content = fs::read_to_string(&path).expect("generated package readable"); - assert!(content.contains("@generated by UpgradeAll getter local_autogen")); + assert!(content.contains("@generated by UpgradeAll getter autogen")); } #[then(expr = "the app list contains autogen tracked package {string}")] @@ -1301,7 +1308,7 @@ fn app_list_contains_autogen_tracked_package(world: &mut CliWorld, package_id: S .iter() .find(|app| app["id"].as_str() == Some(package_id.as_str())) .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); - assert_eq!(app["repository_id"], "local_autogen"); + assert_eq!(app["repository_id"], "autogen"); assert_eq!(app["package_resolution"], "generate_local_package"); } @@ -1341,12 +1348,9 @@ fn autogen_cleanup_preview_contains_delete_candidate(world: &mut CliWorld, packa assert_eq!(candidate["action"], "delete"); } -#[then(expr = "the local_autogen repository does not contain generated package {string}")] -fn local_autogen_repository_does_not_contain_generated_package( - world: &mut CliWorld, - package_id: String, -) { - let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); +#[then(expr = "the autogen repository does not contain generated package {string}")] +fn autogen_repository_does_not_contain_generated_package(world: &mut CliWorld, package_id: String) { + let path = autogen_repo_path(world).join(package_relative_path(&package_id)); assert!( !path.exists(), "generated package should be deleted: {path:?}" @@ -1371,7 +1375,7 @@ fn replace_generated_autogen_package_with_user_edited_content( world: &mut CliWorld, package_id: String, ) { - let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); + let path = autogen_repo_path(world).join(package_relative_path(&package_id)); assert!(path.is_file(), "generated package should exist before edit"); fs::write( &path, @@ -1389,7 +1393,7 @@ fn local_repository_contains_preserved_package(world: &mut CliWorld, package_id: .data_dir .as_ref() .expect("data dir exists") - .join("repositories") + .join("repo") .join("local") .join(package_relative_path(&package_id)); assert!( @@ -1572,13 +1576,13 @@ fn current_json(world: &mut CliWorld) -> &Value { .get_or_insert_with(|| parse_stdout(world.output.as_ref().expect("command output exists"))) } -fn local_autogen_repo_path(world: &CliWorld) -> PathBuf { +fn autogen_repo_path(world: &CliWorld) -> PathBuf { world .data_dir .as_ref() .expect("data dir exists") - .join("repositories") - .join("local_autogen") + .join("repo") + .join("autogen") } fn package_relative_path(package_id: &str) -> PathBuf { diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index fc3c16a..5bbb8c9 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -7,7 +7,7 @@ Feature: Installed app autogen Then the command succeeds And the output is valid JSON And the autogen preview contains candidate "android/com.example.autogen" - And the local_autogen repository has not been written + And the autogen repository has not been written Scenario: User applies installed app autogen and evaluates the generated fallback package Given an initialized getter data directory @@ -17,11 +17,11 @@ Feature: Installed app autogen And I save the autogen preview to a file When I run getter autogen installed apply for that preview with accept-all Then the command succeeds - And the local_autogen repository contains generated package "android/com.example.autogen" + And the autogen repository contains generated package "android/com.example.autogen" And the app list contains autogen tracked package "android/com.example.autogen" - When I run getter repo validate for local_autogen + When I run getter repo validate for autogen Then the output reports a valid repository without network - When I run getter package eval for package "android/com.example.autogen" from local_autogen + When I run getter package eval for package "android/com.example.autogen" from autogen Then the output contains package "android/com.example.autogen" named "Example Autogen" Scenario: Higher-priority repositories suppress installed app autogen candidates @@ -49,7 +49,7 @@ Feature: Installed app autogen And I save the autogen preview to a file When I run getter autogen cleanup apply for that preview with accept-all Then the command succeeds - And the local_autogen repository does not contain generated package "android/com.example.old" + And the autogen repository does not contain generated package "android/com.example.old" And the app list does not contain package "android/com.example.old" Scenario: Cleanup rejects tampered previews for non-autogen tracked packages diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 187fcf6..4eac396 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -8,6 +8,7 @@ default = ["lua"] lua = ["dep:mlua"] [dependencies] +json_comments = "0.2" mlua = { version = "0.10", features = ["luajit", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index 2001ac5..a3bfe8e 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -15,9 +15,9 @@ pub const INSTALLED_INVENTORY_VERSION: u32 = 1; pub const AUTOGEN_MANIFEST_VERSION: u32 = 1; pub const LOCAL_REPOSITORY_ID: &str = "local"; pub const LOCAL_REPOSITORY_NAME: &str = "Local"; -pub const LOCAL_AUTOGEN_REPOSITORY_ID: &str = "local_autogen"; -pub const LOCAL_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Local Autogen"; -pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter local_autogen"; +pub const DEFAULT_AUTOGEN_REPOSITORY_ID: &str = "autogen"; +pub const DEFAULT_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Autogen"; +pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter autogen"; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct InstalledInventory { @@ -107,8 +107,8 @@ impl AutogenManifest { pub fn from_candidates(candidates: &[AutogenCandidate]) -> Self { Self { version: AUTOGEN_MANIFEST_VERSION, - repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID) - .expect("local_autogen is a valid repository id"), + repository_id: RepositoryId::new(DEFAULT_AUTOGEN_REPOSITORY_ID) + .expect("autogen is a valid repository id"), packages: candidates .iter() .map(|candidate| AutogenManifestEntry { @@ -146,13 +146,13 @@ pub enum AutogenError { PackageId(#[from] crate::PackageIdError), } -/// Build a deterministic local_autogen preview from installed inventory. +/// Build a deterministic installed-autogen preview from installed inventory. /// /// `covered_packages` contains package ids already supplied by repositories with -/// priority higher than `local_autogen`. Those packages are skipped so generated -/// fallback files do not compete with user-authored `local` or upstream package -/// files. -pub fn plan_local_autogen( +/// priority higher than the generated repository. Those packages are skipped so +/// generated fallback files do not compete with user-authored `local` or +/// upstream package files. +pub fn plan_installed_autogen( inventory: &InstalledInventory, covered_packages: &HashMap, ) -> Result { @@ -186,10 +186,10 @@ pub fn plan_local_autogen( skipped.sort_by_key(|skip| skip.package_id.to_string()); Ok(AutogenPlan { - repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID) - .expect("local_autogen is a valid repository id"), - repository_name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), - repository_priority: RepositoryPriority::LOCAL_AUTOGEN, + repository_id: RepositoryId::new(DEFAULT_AUTOGEN_REPOSITORY_ID) + .expect("autogen is a valid repository id"), + repository_name: DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned(), + repository_priority: RepositoryPriority::GENERATED_FALLBACK, candidates, skipped, }) @@ -210,11 +210,11 @@ pub fn validate_installed_inventory(inventory: &InstalledInventory) -> Result<() Ok(()) } -pub fn local_autogen_repo_toml() -> String { +pub fn default_autogen_repo_toml() -> String { repo_toml( - LOCAL_AUTOGEN_REPOSITORY_ID, - LOCAL_AUTOGEN_REPOSITORY_NAME, - RepositoryPriority::LOCAL_AUTOGEN, + DEFAULT_AUTOGEN_REPOSITORY_ID, + DEFAULT_AUTOGEN_REPOSITORY_NAME, + RepositoryPriority::GENERATED_FALLBACK, ) } @@ -385,10 +385,13 @@ mod tests { version_code: Some(1020000), }]); - let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + let plan = plan_installed_autogen(&inventory, &HashMap::new()).unwrap(); - assert_eq!(plan.repository_id.as_str(), LOCAL_AUTOGEN_REPOSITORY_ID); - assert_eq!(plan.repository_priority, RepositoryPriority::LOCAL_AUTOGEN); + assert_eq!(plan.repository_id.as_str(), DEFAULT_AUTOGEN_REPOSITORY_ID); + assert_eq!( + plan.repository_priority, + RepositoryPriority::GENERATED_FALLBACK + ); assert_eq!(plan.candidates.len(), 1); let candidate = &plan.candidates[0]; assert_eq!( @@ -416,7 +419,7 @@ mod tests { version_code: None, }]); - let plan = plan_local_autogen(&inventory, &covered).unwrap(); + let plan = plan_installed_autogen(&inventory, &covered).unwrap(); assert!(plan.candidates.is_empty()); assert_eq!(plan.skipped.len(), 1); @@ -450,7 +453,7 @@ mod tests { }, ]); - let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + let plan = plan_installed_autogen(&inventory, &HashMap::new()).unwrap(); assert_eq!(plan.candidates.len(), 1); assert_eq!(plan.skipped.len(), 1); @@ -469,7 +472,7 @@ mod tests { }; assert!(matches!( - plan_local_autogen(&inventory, &HashMap::new()), + plan_installed_autogen(&inventory, &HashMap::new()), Err(AutogenError::UnsupportedInventoryFormat(_)) )); } @@ -482,12 +485,15 @@ mod tests { version_name: None, version_code: None, }]); - let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + let plan = plan_installed_autogen(&inventory, &HashMap::new()).unwrap(); let manifest = AutogenManifest::from_candidates(&plan.candidates); assert_eq!(manifest.version, AUTOGEN_MANIFEST_VERSION); - assert_eq!(manifest.repository_id.as_str(), LOCAL_AUTOGEN_REPOSITORY_ID); + assert_eq!( + manifest.repository_id.as_str(), + DEFAULT_AUTOGEN_REPOSITORY_ID + ); assert_eq!(manifest.packages.len(), 1); assert_eq!( manifest.packages[0].content_hash, @@ -500,7 +506,7 @@ mod tests { fn generated_lua_evaluates_through_package_boundary() { let temp = tempfile::tempdir().unwrap(); let repo = temp.path(); - fs::write(repo.join("repo.toml"), local_autogen_repo_toml()).unwrap(); + fs::write(repo.join("repo.toml"), default_autogen_repo_toml()).unwrap(); fs::create_dir_all(repo.join("packages/android")).unwrap(); fs::create_dir(repo.join("lib")).unwrap(); fs::create_dir(repo.join("templates")).unwrap(); @@ -510,7 +516,7 @@ mod tests { version_name: None, version_code: None, }]); - let candidate = plan_local_autogen(&inventory, &HashMap::new()) + let candidate = plan_installed_autogen(&inventory, &HashMap::new()) .unwrap() .candidates .remove(0); diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index 62ed870..d338055 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -87,6 +87,35 @@ pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationR fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDiagnostic { let (code, path, message) = match error { + RepositoryLoadError::ReadRootMetadata { path, source } => ( + "repository.read_root_metadata", + path, + format!("failed to read repository root metadata: {source}"), + ), + RepositoryLoadError::ParseRootMetadata { path, source } => ( + "repository.parse_root_metadata", + path, + format!("failed to parse repository root metadata: {source}"), + ), + RepositoryLoadError::UnsupportedRootMetadataVersion { + path, + found, + expected, + } => ( + "repository.unsupported_root_metadata_version", + path, + format!("unsupported repository root metadata version {found}; expected {expected}"), + ), + RepositoryLoadError::ReadRepositoryRoot { path, source } => ( + "repository.read_root", + path, + format!("failed to read repository root: {source}"), + ), + RepositoryLoadError::MissingGeneratedRepository { alias, path } => ( + "repository.missing_generated_repository", + path, + format!("configured generated repository '{alias}' does not exist"), + ), RepositoryLoadError::ReadRepoToml { path, source } => ( "repository.read_repo_toml", path, diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index b1d759e..f02c4b0 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -146,7 +146,7 @@ impl From for String { } /// Repository identifier such as `official`, `community`, `local`, or -/// `local_autogen`. +/// `autogen`. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] pub struct RepositoryId(String); @@ -206,7 +206,7 @@ pub struct RepositoryPriority(i32); impl RepositoryPriority { pub const LOCAL: Self = Self(100); pub const DEFAULT: Self = Self(0); - pub const LOCAL_AUTOGEN: Self = Self(-1); + pub const GENERATED_FALLBACK: Self = Self(-1); pub const fn new(value: i32) -> Self { Self(value) @@ -401,18 +401,15 @@ mod tests { #[test] fn repository_priority_higher_number_wins() { assert!(RepositoryPriority::LOCAL > RepositoryPriority::DEFAULT); - assert!(RepositoryPriority::DEFAULT > RepositoryPriority::LOCAL_AUTOGEN); + assert!(RepositoryPriority::DEFAULT > RepositoryPriority::GENERATED_FALLBACK); assert_eq!(RepositoryPriority::LOCAL.value(), 100); assert_eq!(RepositoryPriority::DEFAULT.value(), 0); - assert_eq!(RepositoryPriority::LOCAL_AUTOGEN.value(), -1); + assert_eq!(RepositoryPriority::GENERATED_FALLBACK.value(), -1); } #[test] fn repository_id_accepts_named_repositories() { - assert_eq!( - RepositoryId::new("local_autogen").unwrap().as_str(), - "local_autogen" - ); + assert_eq!(RepositoryId::new("autogen").unwrap().as_str(), "autogen"); assert_eq!( RepositoryId::new("community.1").unwrap().to_string(), "community.1" diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index c383acc..95b7fe8 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -2,10 +2,19 @@ use crate::{PackageId, PackageIdError, RepositoryId, RepositoryIdError, RepositoryPriority}; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; pub const REPO_API_VERSION_V1: &str = "getter.repo.v1"; +pub const MAIN_DB_FILE: &str = "main.db"; +pub const CACHE_DB_FILE: &str = "cache.db"; +pub const REPOSITORY_ROOT_DIR: &str = "repo"; +pub const RUNTIME_CONFIG_DIR: &str = "rc"; +pub const REPOSITORY_ROOT_METADATA_FILE: &str = "metadata.jsonc"; +pub const REPOSITORY_ROOT_METADATA_VERSION: u32 = 1; +pub const LOCAL_REPOSITORY_ALIAS: &str = "local"; +pub const DEFAULT_GENERATED_REPOSITORY_ALIAS: &str = "autogen"; #[derive(Debug, Clone, PartialEq, Eq)] pub struct RepositoryLayout { @@ -25,6 +34,200 @@ pub struct RepositoryMetadata { pub api_version: String, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GetterDataDirLayout { + pub root: PathBuf, + pub main_db: PathBuf, + pub cache_db: PathBuf, + pub repository_root: PathBuf, + pub runtime_config_root: PathBuf, +} + +impl GetterDataDirLayout { + pub fn new(root: impl AsRef) -> Self { + let root = root.as_ref().to_path_buf(); + Self { + main_db: root.join(MAIN_DB_FILE), + cache_db: root.join(CACHE_DB_FILE), + repository_root: root.join(REPOSITORY_ROOT_DIR), + runtime_config_root: root.join(RUNTIME_CONFIG_DIR), + root, + } + } + + pub fn repository_path(&self, alias: &RepositoryId) -> PathBuf { + self.repository_root.join(alias.as_str()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryRootConfig { + pub generated_repository: RepositoryId, + priority: HashMap, +} + +impl RepositoryRootConfig { + pub fn load(repository_root: impl AsRef) -> Result { + let path = repository_root.as_ref().join(REPOSITORY_ROOT_METADATA_FILE); + if !path.exists() { + return Ok(Self::default()); + } + let bytes = fs::read(&path).map_err(|source| RepositoryLoadError::ReadRootMetadata { + path: path.clone(), + source, + })?; + let raw: RawRepositoryRootConfig = serde_json::from_reader( + json_comments::StripComments::new(bytes.as_slice()), + ) + .map_err(|source| RepositoryLoadError::ParseRootMetadata { + path: path.clone(), + source, + })?; + if raw.version != REPOSITORY_ROOT_METADATA_VERSION { + return Err(RepositoryLoadError::UnsupportedRootMetadataVersion { + path, + found: raw.version, + expected: REPOSITORY_ROOT_METADATA_VERSION, + }); + } + let priority = raw + .priority + .into_iter() + .map(|(alias, value)| (alias, RepositoryPriority::new(value))) + .collect(); + Ok(Self { + generated_repository: RepositoryId::new( + raw.generated_repository + .unwrap_or_else(|| DEFAULT_GENERATED_REPOSITORY_ALIAS.to_owned()), + )?, + priority, + }) + } + + pub fn priority_for(&self, alias: &RepositoryId) -> RepositoryPriority { + self.priority + .get(alias.as_str()) + .copied() + .unwrap_or_else(|| default_repository_priority(alias.as_str())) + } +} + +impl Default for RepositoryRootConfig { + fn default() -> Self { + Self { + generated_repository: RepositoryId::new(DEFAULT_GENERATED_REPOSITORY_ALIAS) + .expect("default generated repository alias is valid"), + priority: HashMap::new(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryAliasEntry { + pub alias: RepositoryId, + pub path: PathBuf, + pub priority: RepositoryPriority, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryRootLayout { + pub root: PathBuf, + pub config: RepositoryRootConfig, + pub repositories: Vec, +} + +impl RepositoryRootLayout { + pub fn load(root: impl AsRef) -> Result { + let root = root.as_ref().to_path_buf(); + let config = RepositoryRootConfig::load(&root)?; + let mut repositories = Vec::new(); + match fs::read_dir(&root) { + Ok(entries) => { + for entry in entries { + let entry = + entry.map_err(|source| RepositoryLoadError::ReadRepositoryRoot { + path: root.clone(), + source, + })?; + let file_type = entry.file_type().map_err(|source| { + RepositoryLoadError::ReadRepositoryRoot { + path: root.clone(), + source, + } + })?; + if !file_type.is_dir() { + continue; + } + let alias_text = entry.file_name().to_string_lossy().into_owned(); + let alias = RepositoryId::new(alias_text)?; + let priority = config.priority_for(&alias); + repositories.push(RepositoryAliasEntry { + alias, + path: entry.path(), + priority, + }); + } + } + Err(source) if source.kind() == std::io::ErrorKind::NotFound => {} + Err(source) => { + return Err(RepositoryLoadError::ReadRepositoryRoot { + path: root.clone(), + source, + }) + } + } + repositories.sort_by(|left, right| { + right + .priority + .cmp(&left.priority) + .then_with(|| left.alias.as_str().cmp(right.alias.as_str())) + }); + Ok(Self { + root, + config, + repositories, + }) + } +} + +pub fn default_repository_priority(alias: &str) -> RepositoryPriority { + match alias { + LOCAL_REPOSITORY_ALIAS => RepositoryPriority::LOCAL, + DEFAULT_GENERATED_REPOSITORY_ALIAS => RepositoryPriority::GENERATED_FALLBACK, + _ => RepositoryPriority::DEFAULT, + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum GeneratedRepositoryTarget { + CreateDefault { alias: RepositoryId, path: PathBuf }, + Existing { alias: RepositoryId, path: PathBuf }, +} + +pub fn generated_repository_target( + data_dir: impl AsRef, +) -> Result { + let layout = GetterDataDirLayout::new(data_dir); + let config = RepositoryRootConfig::load(&layout.repository_root)?; + let path = layout.repository_path(&config.generated_repository); + if config.generated_repository.as_str() == DEFAULT_GENERATED_REPOSITORY_ALIAS { + Ok(GeneratedRepositoryTarget::CreateDefault { + alias: config.generated_repository, + path, + }) + } else if path.is_dir() { + Ok(GeneratedRepositoryTarget::Existing { + alias: config.generated_repository, + path, + }) + } else { + Err(RepositoryLoadError::MissingGeneratedRepository { + alias: config.generated_repository, + path, + }) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct PackageFile { pub id: PackageId, @@ -39,8 +242,43 @@ pub struct RepositoryPackageCacheKey { pub package_file_hash: String, } +#[derive(Debug, Deserialize)] +struct RawRepositoryRootConfig { + version: u32, + #[serde(default)] + generated_repository: Option, + #[serde(default)] + priority: HashMap, +} + #[derive(Debug, thiserror::Error)] pub enum RepositoryLoadError { + #[error("failed to read repository root metadata at {path}: {source}")] + ReadRootMetadata { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("failed to parse repository root metadata at {path}: {source}")] + ParseRootMetadata { + path: PathBuf, + #[source] + source: serde_json::Error, + }, + #[error("unsupported repository root metadata version {found} at {path}; expected {expected}")] + UnsupportedRootMetadataVersion { + path: PathBuf, + found: u32, + expected: u32, + }, + #[error("failed to read repository root {path}: {source}")] + ReadRepositoryRoot { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("configured generated repository '{alias}' does not exist at {path}")] + MissingGeneratedRepository { alias: RepositoryId, path: PathBuf }, #[error("failed to read repo.toml at {path}: {source}")] ReadRepoToml { path: PathBuf, @@ -280,6 +518,154 @@ mod tests { use super::*; use std::io::Write; + #[test] + fn data_dir_layout_uses_repo_and_rc_roots() { + let layout = GetterDataDirLayout::new("/tmp/ua-getter"); + + assert_eq!(layout.main_db, PathBuf::from("/tmp/ua-getter/main.db")); + assert_eq!(layout.cache_db, PathBuf::from("/tmp/ua-getter/cache.db")); + assert_eq!(layout.repository_root, PathBuf::from("/tmp/ua-getter/repo")); + assert_eq!( + layout.runtime_config_root, + PathBuf::from("/tmp/ua-getter/rc") + ); + } + + #[test] + fn missing_root_metadata_uses_default_priorities_and_generated_target() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path().join("repo"); + fs::create_dir_all(root.join("community")).unwrap(); + fs::create_dir_all(root.join("local")).unwrap(); + fs::create_dir_all(root.join("autogen")).unwrap(); + + let layout = RepositoryRootLayout::load(&root).unwrap(); + + assert_eq!( + layout.config.generated_repository.as_str(), + DEFAULT_GENERATED_REPOSITORY_ALIAS + ); + assert_eq!( + layout + .repositories + .iter() + .map(|repo| (repo.alias.as_str().to_owned(), repo.priority.value())) + .collect::>(), + vec![ + ("local".to_owned(), 100), + ("community".to_owned(), 0), + ("autogen".to_owned(), -1), + ] + ); + } + + #[test] + fn root_metadata_priority_map_is_lookup_only_for_existing_aliases() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path().join("repo"); + fs::create_dir_all(root.join("official")).unwrap(); + fs::write( + root.join(REPOSITORY_ROOT_METADATA_FILE), + r#"{ + "version": 1, + // This entry must not create or sort a missing repository. + "generated_repository": "autogen", + "priority": { + "missing": 500, + "not an alias": 400, + "official": 7 + } +} +"#, + ) + .unwrap(); + + let layout = RepositoryRootLayout::load(&root).unwrap(); + + assert_eq!(layout.repositories.len(), 1); + assert_eq!(layout.repositories[0].alias.as_str(), "official"); + assert_eq!(layout.repositories[0].priority.value(), 7); + assert_eq!( + layout.config.generated_repository.as_str(), + DEFAULT_GENERATED_REPOSITORY_ALIAS + ); + } + + #[test] + fn malformed_root_metadata_is_an_error_instead_of_fallback() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path().join("repo"); + fs::create_dir_all(&root).unwrap(); + fs::write(root.join(REPOSITORY_ROOT_METADATA_FILE), "{not-json").unwrap(); + + assert!(matches!( + RepositoryRootLayout::load(&root), + Err(RepositoryLoadError::ParseRootMetadata { .. }) + )); + } + + #[test] + fn generated_repository_target_creates_only_default_alias() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + + let target = generated_repository_target(data_dir).unwrap(); + + assert!(matches!( + target, + GeneratedRepositoryTarget::CreateDefault { ref alias, ref path } + if alias.as_str() == DEFAULT_GENERATED_REPOSITORY_ALIAS + && path == &data_dir.join("repo").join(DEFAULT_GENERATED_REPOSITORY_ALIAS) + )); + } + + #[test] + fn generated_repository_target_rejects_missing_custom_alias() { + let temp = tempfile::tempdir().unwrap(); + let repo_root = temp.path().join("repo"); + fs::create_dir_all(&repo_root).unwrap(); + fs::write( + repo_root.join(REPOSITORY_ROOT_METADATA_FILE), + r#"{ + "version": 1, + "generated_repository": "generated", + "priority": {} +} +"#, + ) + .unwrap(); + + assert!(matches!( + generated_repository_target(temp.path()), + Err(RepositoryLoadError::MissingGeneratedRepository { ref alias, .. }) + if alias.as_str() == "generated" + )); + } + + #[test] + fn generated_repository_target_accepts_existing_custom_alias() { + let temp = tempfile::tempdir().unwrap(); + let repo_root = temp.path().join("repo"); + fs::create_dir_all(repo_root.join("generated")).unwrap(); + fs::write( + repo_root.join(REPOSITORY_ROOT_METADATA_FILE), + r#"{ + "version": 1, + "generated_repository": "generated" +} +"#, + ) + .unwrap(); + + let target = generated_repository_target(temp.path()).unwrap(); + + assert!(matches!( + target, + GeneratedRepositoryTarget::Existing { ref alias, ref path } + if alias.as_str() == "generated" && path == &repo_root.join("generated") + )); + } + #[test] fn derives_package_id_from_lua_path() { let root = PathBuf::from("repo/packages"); @@ -362,7 +748,7 @@ api_version = "getter.repo.v1" #[test] fn highest_priority_selects_larger_number() { let priorities = [ - RepositoryPriority::LOCAL_AUTOGEN, + RepositoryPriority::GENERATED_FALLBACK, RepositoryPriority::DEFAULT, RepositoryPriority::LOCAL, ]; diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs index caab4aa..faa06a9 100644 --- a/crates/getter-operations/src/autogen.rs +++ b/crates/getter-operations/src/autogen.rs @@ -1,18 +1,20 @@ //! Getter-owned installed-inventory autogen operations. //! //! The CLI and native bridge both call this module so there is one implementation -//! of `local_autogen` preview/apply semantics. Platform layers provide installed +//! of installed-autogen preview/apply semantics. Platform layers provide installed //! inventory facts; this module decides generated package ids, repository //! coverage, file writes, manifest updates, preservation behavior, and tracked //! state updates. use getter_core::autogen::{ - content_hash, local_autogen_repo_toml, local_repo_toml, plan_local_autogen, AutogenManifest, - AutogenManifestEntry, AutogenPlan, AutogenSkipReason, InstalledInventory, - LOCAL_AUTOGEN_REPOSITORY_ID, LOCAL_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, - LOCAL_REPOSITORY_NAME, + content_hash, local_repo_toml, plan_installed_autogen, AutogenManifest, AutogenManifestEntry, + AutogenPlan, AutogenSkipReason, InstalledInventory, DEFAULT_AUTOGEN_REPOSITORY_ID, + DEFAULT_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, LOCAL_REPOSITORY_NAME, +}; +use getter_core::repository::{ + generated_repository_target, GeneratedRepositoryTarget, GetterDataDirLayout, RepositoryLayout, + RepositoryLoadError, RepositoryMetadata, RepositoryRootConfig, REPO_API_VERSION_V1, }; -use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_storage::{MainDb, StorageError, StoredRepository}; use serde_json::{json, Value}; @@ -34,28 +36,55 @@ pub enum AutogenOperationError { Storage(#[from] StorageError), #[error("repository error: {0}")] Repository(String), + #[error("configured generated repository '{alias}' does not exist at {path}")] + MissingGeneratedRepository { alias: RepositoryId, path: PathBuf }, #[error("autogen error: {0}")] Autogen(String), } +impl From for AutogenOperationError { + fn from(value: RepositoryLoadError) -> Self { + match value { + RepositoryLoadError::MissingGeneratedRepository { alias, path } => { + Self::MissingGeneratedRepository { alias, path } + } + other => Self::Repository(other.to_string()), + } + } +} + pub type AutogenOperationResult = Result; -pub fn build_local_autogen_plan( +pub fn build_installed_autogen_plan( + data_dir: &Path, db: &MainDb, inventory: &InstalledInventory, ) -> AutogenOperationResult { - let covered = higher_priority_package_coverage(db)?; - plan_local_autogen(inventory, &covered) - .map_err(|source| AutogenOperationError::Autogen(source.to_string())) + let (target_alias, _target_path, target_priority) = generated_repository_config(data_dir)?; + let covered = higher_priority_package_coverage(db, &target_alias, target_priority)?; + let mut plan = plan_installed_autogen(inventory, &covered) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + plan.repository_id = target_alias.clone(); + plan.repository_name = if target_alias.as_str() == DEFAULT_AUTOGEN_REPOSITORY_ID { + DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned() + } else { + target_alias.to_string() + }; + plan.repository_priority = target_priority; + Ok(plan) } -pub fn installed_preview_json(data_dir: &Path, plan: &AutogenPlan) -> Value { +pub fn installed_preview_json( + data_dir: &Path, + plan: &AutogenPlan, +) -> AutogenOperationResult { + let (target_alias, target_path, _target_priority) = generated_repository_config(data_dir)?; let candidates: Vec = plan.candidates.iter().map(autogen_candidate_json).collect(); let skipped: Vec = plan.skipped.iter().map(autogen_skip_json).collect(); - json!({ + Ok(json!({ "operation": "installed.preview", - "target_repo_id": plan.repository_id.as_str(), - "target_repo_path": local_autogen_repo_path(data_dir), + "target_repo_id": target_alias.as_str(), + "target_repo_path": target_path, "summary": { "candidate_count": candidates.len(), "skipped_count": skipped.len(), @@ -65,7 +94,7 @@ pub fn installed_preview_json(data_dir: &Path, plan: &AutogenPlan) -> Value { "candidates": candidates, "skipped": skipped, "diagnostics": [], - }) + })) } pub fn cleanup_preview_json( @@ -73,11 +102,11 @@ pub fn cleanup_preview_json( db: &MainDb, inventory: &InstalledInventory, ) -> AutogenOperationResult { - let repo_path = local_autogen_repo_path(data_dir); + let (target_alias, repo_path, _target_priority) = generated_repository_config(data_dir)?; let Some(manifest) = read_autogen_manifest(&repo_path)? else { return Ok(json!({ "operation": "cleanup.preview", - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "summary": { "candidate_count": 0, "skipped_count": 0, "write_count": 0, "delete_count": 0 }, "candidates": [], @@ -85,7 +114,7 @@ pub fn cleanup_preview_json( "diagnostics": [], })); }; - let plan = build_local_autogen_plan(db, inventory)?; + let plan = build_installed_autogen_plan(data_dir, db, inventory)?; let installed_ids: BTreeSet = plan .candidates .iter() @@ -113,7 +142,7 @@ pub fn cleanup_preview_json( }); Ok(json!({ "operation": "cleanup.preview", - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "summary": { "candidate_count": candidates.len(), @@ -133,10 +162,17 @@ pub fn apply_installed_preview( preview: &Value, acceptance: &AutogenAcceptance, ) -> AutogenOperationResult { - let repo_path = local_autogen_repo_path(data_dir); - ensure_local_autogen_repository(data_dir, db)?; + let (target_alias, repo_path, target_priority) = generated_repository_config(data_dir)?; + if preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) { + return Err(AutogenOperationError::Autogen(format!( + "installed preview target_repo_id must be '{}'", + target_alias.as_str() + ))); + } + ensure_generated_repository(&repo_path, db, &target_alias, target_priority)?; let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut manifest = + read_autogen_manifest(&repo_path)?.unwrap_or_else(|| empty_autogen_manifest(&target_alias)); let mut applied = Vec::new(); let mut preserved = Vec::new(); @@ -203,10 +239,7 @@ pub fn apply_installed_preview( content_hash: expected_hash.to_owned(), }, ); - db.upsert_generated_tracked_package_preserving_user_state( - &package_id, - &RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), - )?; + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &target_alias)?; applied.push(json!({ "package_id": package_id.to_string(), "output_relative_path": relative_path, @@ -215,7 +248,7 @@ pub fn apply_installed_preview( write_autogen_manifest(&repo_path, &manifest)?; Ok(json!({ - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "applied_count": applied.len(), "applied": applied, @@ -229,17 +262,18 @@ pub fn apply_cleanup_preview( preview: &Value, acceptance: &AutogenAcceptance, ) -> AutogenOperationResult { - if preview.get("target_repo_id").and_then(Value::as_str) != Some(LOCAL_AUTOGEN_REPOSITORY_ID) { + let (target_alias, repo_path, _target_priority) = generated_repository_config(data_dir)?; + if preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) { return Err(AutogenOperationError::Autogen(format!( - "cleanup preview target_repo_id must be '{LOCAL_AUTOGEN_REPOSITORY_ID}'" + "cleanup preview target_repo_id must be '{}'", + target_alias.as_str() ))); } - let repo_path = local_autogen_repo_path(data_dir); let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut manifest = + read_autogen_manifest(&repo_path)?.unwrap_or_else(|| empty_autogen_manifest(&target_alias)); let mut deleted = Vec::new(); let mut preserved = Vec::new(); - let local_autogen_id = RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"); for candidate in accepted { let package_id = preview_package_id(candidate)?; let relative_path = preview_relative_path(candidate)?; @@ -253,14 +287,14 @@ pub fn apply_cleanup_preview( })?; let manifest_entry = manifest.package(&package_id).ok_or_else(|| { AutogenOperationError::Autogen(format!( - "cleanup preview candidate {package_id} is not managed by local_autogen manifest" + "cleanup preview candidate {package_id} is not managed by autogen manifest" )) })?; if manifest_entry.relative_path != relative_path || manifest_entry.content_hash != expected_hash { return Err(AutogenOperationError::Autogen(format!( - "cleanup preview candidate {package_id} does not match local_autogen manifest" + "cleanup preview candidate {package_id} does not match autogen manifest" ))); } let target = safe_join(&repo_path, &relative_path)?; @@ -289,7 +323,7 @@ pub fn apply_cleanup_preview( manifest .packages .retain(|entry| entry.package_id != package_id); - db.delete_generated_tracked_package(&package_id, &local_autogen_id)?; + db.delete_generated_tracked_package(&package_id, &target_alias)?; deleted.push(json!({ "package_id": package_id.to_string(), "output_relative_path": relative_path, @@ -297,7 +331,7 @@ pub fn apply_cleanup_preview( } write_autogen_manifest(&repo_path, &manifest)?; Ok(json!({ - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "deleted_count": deleted.len(), "deleted": deleted, @@ -322,21 +356,36 @@ pub fn unwrap_preview_payload( Ok(payload) } -pub fn local_autogen_repo_path(data_dir: &Path) -> PathBuf { - data_dir - .join("repositories") - .join(LOCAL_AUTOGEN_REPOSITORY_ID) +pub fn default_autogen_repo_path(data_dir: &Path) -> PathBuf { + GetterDataDirLayout::new(data_dir) + .repository_path(&RepositoryId::new(DEFAULT_AUTOGEN_REPOSITORY_ID).expect("valid id")) +} + +fn generated_repository_config( + data_dir: &Path, +) -> AutogenOperationResult<(RepositoryId, PathBuf, RepositoryPriority)> { + let layout = GetterDataDirLayout::new(data_dir); + let config = RepositoryRootConfig::load(&layout.repository_root)?; + let target = generated_repository_target(data_dir)?; + let (alias, path) = match target { + GeneratedRepositoryTarget::CreateDefault { alias, path } + | GeneratedRepositoryTarget::Existing { alias, path } => (alias, path), + }; + let priority = config.priority_for(&alias); + Ok((alias, path, priority)) } fn higher_priority_package_coverage( db: &MainDb, + target_alias: &RepositoryId, + target_priority: RepositoryPriority, ) -> AutogenOperationResult> { let mut covered = HashMap::new(); for repo in db.repositories()? { - if repo.id.as_str() == LOCAL_AUTOGEN_REPOSITORY_ID { + if &repo.id == target_alias { continue; } - if repo.priority <= RepositoryPriority::LOCAL_AUTOGEN { + if repo.priority <= target_priority { continue; } let Some(path) = repo.path.as_ref() else { @@ -431,23 +480,41 @@ fn preview_relative_path(candidate: &Value) -> AutogenOperationResult { }) } -fn ensure_local_autogen_repository( - data_dir: &Path, +fn ensure_generated_repository( + repo_path: &Path, db: &MainDb, -) -> AutogenOperationResult { - let repo_path = local_autogen_repo_path(data_dir); - ensure_repository_layout(&repo_path, &local_autogen_repo_toml())?; + alias: &RepositoryId, + priority: RepositoryPriority, +) -> AutogenOperationResult<()> { + ensure_repository_layout(repo_path, &generated_repo_toml(alias, priority))?; db.upsert_repository( &RepositoryMetadata { - id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), - name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), - priority: RepositoryPriority::LOCAL_AUTOGEN, + id: alias.clone(), + name: generated_repository_name(alias), + priority, api_version: REPO_API_VERSION_V1.to_owned(), }, - Some(&repo_path), + Some(repo_path), None, )?; - Ok(repo_path) + Ok(()) +} + +fn generated_repo_toml(alias: &RepositoryId, priority: RepositoryPriority) -> String { + format!( + "id = \"{}\"\nname = \"{}\"\npriority = {}\napi_version = \"{REPO_API_VERSION_V1}\"\n", + alias.as_str(), + generated_repository_name(alias), + priority.value() + ) +} + +fn generated_repository_name(alias: &RepositoryId) -> String { + if alias.as_str() == DEFAULT_AUTOGEN_REPOSITORY_ID { + DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned() + } else { + alias.to_string() + } } fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> AutogenOperationResult { @@ -458,7 +525,8 @@ fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> AutogenOperationResu return Ok(repo_path); } - let repo_path = data_dir.join("repositories").join(LOCAL_REPOSITORY_ID); + let repo_path = GetterDataDirLayout::new(data_dir) + .repository_path(&RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id")); ensure_repository_layout(&repo_path, &local_repo_toml())?; db.upsert_repository( &RepositoryMetadata { @@ -601,10 +669,10 @@ fn write_autogen_manifest( }) } -fn empty_autogen_manifest() -> AutogenManifest { +fn empty_autogen_manifest(repository_id: &RepositoryId) -> AutogenManifest { AutogenManifest { version: getter_core::autogen::AUTOGEN_MANIFEST_VERSION, - repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + repository_id: repository_id.clone(), packages: Vec::new(), } } @@ -635,3 +703,109 @@ fn safe_join(root: &Path, relative: &Path) -> AutogenOperationResult { } Ok(root.join(relative)) } + +#[cfg(test)] +mod tests { + use super::*; + use getter_storage::MainDb; + + fn test_inventory() -> InstalledInventory { + InstalledInventory::new(vec![ + getter_core::autogen::InstalledInventoryItem::AndroidPackage { + package_name: "com.example.autogen".to_owned(), + label: Some("Example Autogen".to_owned()), + version_name: None, + version_code: None, + }, + ]) + } + + #[test] + fn preview_uses_default_generated_repository_under_repo_root() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + + assert_eq!(preview["target_repo_id"], "autogen"); + assert_eq!( + preview["target_repo_path"].as_str(), + data_dir.join("repo/autogen").to_str() + ); + assert!(!data_dir.join("repo/autogen").exists()); + } + + #[test] + fn apply_creates_default_generated_repository_and_registers_it() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + + let result = + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll) + .unwrap(); + + assert_eq!(result["target_repo_id"], "autogen"); + assert!(data_dir.join("repo/autogen/repo.toml").is_file()); + assert!(data_dir + .join("repo/autogen/packages/android/com.example.autogen.lua") + .is_file()); + let repos = db.repositories().unwrap(); + assert_eq!(repos[0].id.as_str(), "autogen"); + assert_eq!(repos[0].priority.value(), -1); + } + + #[test] + fn custom_generated_repository_must_already_exist() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + fs::create_dir_all(data_dir.join("repo")).unwrap(); + fs::write( + data_dir.join("repo/metadata.jsonc"), + r#"{ "version": 1, "generated_repository": "generated" }"#, + ) + .unwrap(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + + let error = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap_err(); + + assert!(matches!( + error, + AutogenOperationError::MissingGeneratedRepository { ref alias, .. } + if alias.as_str() == "generated" + )); + } + + #[test] + fn custom_generated_repository_uses_configured_priority() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + fs::create_dir_all(data_dir.join("repo/generated")).unwrap(); + fs::write( + data_dir.join("repo/metadata.jsonc"), + r#"{ + "version": 1, + "generated_repository": "generated", + "priority": { "generated": -5 } +}"#, + ) + .unwrap(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + let result = + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll) + .unwrap(); + + assert_eq!(preview["target_repo_id"], "generated"); + assert_eq!(result["target_repo_id"], "generated"); + let repos = db.repositories().unwrap(); + assert_eq!(repos[0].id.as_str(), "generated"); + assert_eq!(repos[0].priority.value(), -5); + } +} diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index 2127cbc..e8d7b02 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -1234,7 +1234,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); fn generated_tracking_preserves_existing_user_state_on_conflict() { let db = MainDb::open_in_memory().unwrap(); let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); - let local_autogen = RepositoryId::new("local_autogen").unwrap(); + let autogen = RepositoryId::new("autogen").unwrap(); db.upsert_tracked_package(&TrackedPackageUpsert { package_id: package_id.clone(), enabled: false, @@ -1245,7 +1245,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); }) .unwrap(); - db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &autogen) .unwrap(); let packages = db.tracked_packages().unwrap(); @@ -1264,7 +1264,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); fn generated_tracking_fills_unresolved_tracking_metadata() { let db = MainDb::open_in_memory().unwrap(); let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); - let local_autogen = insert_local_autogen_repo(&db); + let autogen = insert_autogen_repo(&db); db.upsert_tracked_package(&TrackedPackageUpsert { package_id: package_id.clone(), enabled: false, @@ -1275,7 +1275,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); }) .unwrap(); - db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &autogen) .unwrap(); let packages = db.tracked_packages().unwrap(); @@ -1283,7 +1283,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); assert!(!packages[0].enabled); assert!(packages[0].favorite); assert_eq!(packages[0].pin_version.as_deref(), Some("9.9.9")); - assert_eq!(packages[0].repository_id.as_ref(), Some(&local_autogen)); + assert_eq!(packages[0].repository_id.as_ref(), Some(&autogen)); assert_eq!( packages[0].package_resolution, StoredPackageResolution::GenerateLocalPackage @@ -1294,7 +1294,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); fn generated_tracking_delete_is_guarded_by_repo_and_resolution() { let db = MainDb::open_in_memory().unwrap(); let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); - let local_autogen = insert_local_autogen_repo(&db); + let autogen = insert_autogen_repo(&db); db.upsert_tracked_package(&TrackedPackageUpsert { package_id: package_id.clone(), enabled: true, @@ -1306,7 +1306,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); .unwrap(); assert!(!db - .delete_generated_tracked_package(&package_id, &local_autogen) + .delete_generated_tracked_package(&package_id, &autogen) .unwrap()); assert_eq!(db.tracked_packages().unwrap().len(), 1); @@ -1319,28 +1319,28 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); package_resolution: StoredPackageResolution::MissingPackageDefinition, }) .unwrap(); - db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &autogen) .unwrap(); assert!(db - .delete_generated_tracked_package(&package_id, &local_autogen) + .delete_generated_tracked_package(&package_id, &autogen) .unwrap()); assert!(db.tracked_packages().unwrap().is_empty()); } - fn insert_local_autogen_repo(db: &MainDb) -> RepositoryId { - let local_autogen = RepositoryId::new("local_autogen").unwrap(); + fn insert_autogen_repo(db: &MainDb) -> RepositoryId { + let autogen = RepositoryId::new("autogen").unwrap(); db.upsert_repository( &RepositoryMetadata { - id: local_autogen.clone(), - name: "Local Autogen".to_owned(), - priority: RepositoryPriority::LOCAL_AUTOGEN, + id: autogen.clone(), + name: "Autogen".to_owned(), + priority: RepositoryPriority::GENERATED_FALLBACK, api_version: REPO_API_VERSION_V1.to_owned(), }, None, None, ) .unwrap(); - local_autogen + autogen } #[test] From 3388b78dc891dc487bc406d71050e919ce6e4023 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Fri, 26 Jun 2026 22:39:51 +0800 Subject: [PATCH 29/72] feat(repository): discover package directories --- crates/getter-cli/tests/bdd_cli.rs | 22 + .../tests/features/cli/repo_validate.feature | 7 + crates/getter-core/src/diagnostics.rs | 86 +++- crates/getter-core/src/repository.rs | 402 +++++++++++++++++- 4 files changed, 498 insertions(+), 19 deletions(-) diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 073bb63..8a8e950 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -347,6 +347,28 @@ fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: Str create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); } +#[given(expr = "a package-directory repository {string} with package {string}")] +fn package_directory_repository(world: &mut CliWorld, repo_id: String, package_id: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let repo_path = temp.path().join(format!("repo-{repo_id}")); + let package_dir = repo_path.join(package_id.replace('/', std::path::MAIN_SEPARATOR_STR)); + fs::create_dir_all(&package_dir).expect("create package dir"); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .expect("write package metadata"); + fs::write( + package_dir.join("1.20.0.lua"), + "#!/bin/upa-lua v1\nreturn {}", + ) + .expect("write version Lua"); + + world.fixture_repo_id = Some(repo_id); + world.fixture_repo_path = Some(repo_path); + world.fixture_package_id = Some(package_id); +} + #[given(expr = "a fixture Lua repository {string} with package {string} named {string}")] fn fixture_lua_repository_named( world: &mut CliWorld, diff --git a/crates/getter-cli/tests/features/cli/repo_validate.feature b/crates/getter-cli/tests/features/cli/repo_validate.feature index 592d4bf..9c6d1a1 100644 --- a/crates/getter-cli/tests/features/cli/repo_validate.feature +++ b/crates/getter-cli/tests/features/cli/repo_validate.feature @@ -6,6 +6,13 @@ Feature: Getter CLI repository validation Then the command succeeds And the output reports a valid repository without network + Scenario: User validates a package-directory repository offline + Given an initialized getter data directory + And a package-directory repository "official" with package "android/app/org.fdroid.fdroid" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports a valid repository without network + Scenario: User receives diagnostics for invalid Lua Given an initialized getter data directory And a fixture Lua repository "broken" with invalid Lua package "android/org.fdroid.fdroid" diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index d338055..817b310 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -4,7 +4,10 @@ //! describe what getter observed; Flutter should only render them. use crate::lua::{evaluate_package_file, LuaPackageError}; -use crate::repository::{package_cache_key, RepositoryLayout, RepositoryLoadError}; +use crate::repository::{ + package_cache_key, InvalidPackageDirectory, RepositoryLayout, RepositoryLoadError, + RepositoryPackageDirectoryLayout, +}; use crate::PackageId; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; @@ -62,6 +65,14 @@ impl RepositoryValidationReport { /// not perform provider/network checks. pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationReport { let root = path.as_ref(); + if root.join("repo.toml").is_file() { + validate_legacy_repository_path(root) + } else { + validate_package_directory_repository_path(root) + } +} + +fn validate_legacy_repository_path(root: &Path) -> RepositoryValidationReport { let layout = match RepositoryLayout::load(root) { Ok(layout) => layout, Err(error) => { @@ -85,6 +96,21 @@ pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationR RepositoryValidationReport::new(package_count, diagnostics) } +fn validate_package_directory_repository_path(root: &Path) -> RepositoryValidationReport { + let layout = match RepositoryPackageDirectoryLayout::load(root) { + Ok(layout) => layout, + Err(error) => { + return RepositoryValidationReport::new(0, vec![repository_load_diagnostic(error)]) + } + }; + let diagnostics = layout + .invalid_packages + .iter() + .map(invalid_package_directory_diagnostic) + .collect(); + RepositoryValidationReport::new(layout.packages.len(), diagnostics) +} + fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDiagnostic { let (code, path, message) = match error { RepositoryLoadError::ReadRootMetadata { path, source } => ( @@ -163,6 +189,18 @@ fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDi diagnostic(code, message, path, None, None) } +fn invalid_package_directory_diagnostic( + package: &InvalidPackageDirectory, +) -> PackageValidationDiagnostic { + diagnostic( + "package.metadata", + package.reason.clone(), + package.metadata_path.clone(), + None, + package.id.clone(), + ) +} + fn lua_diagnostic( error: LuaPackageError, fallback_package_id: Option, @@ -275,6 +313,52 @@ api_version = "getter.repo.v1" assert!(!report.network_required); } + #[test] + fn package_directory_repository_has_no_diagnostics() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .unwrap(); + fs::write( + package_dir.join("1.20.0.lua"), + "#!/bin/upa-lua v1\nreturn {}", + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + + assert!(report.valid, "{report:?}"); + assert_eq!(report.package_count, 1); + assert!(report.diagnostics.is_empty()); + assert!(!report.network_required); + } + + #[test] + fn package_directory_metadata_error_is_stable_package_diagnostic() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write(package_dir.join("metadata.jsonc"), "[]").unwrap(); + + let report = validate_repository_path(temp.path()); + + assert!(!report.valid); + assert_eq!(report.package_count, 0); + assert_eq!(report.diagnostics[0].code, "package.metadata"); + assert_eq!( + report.diagnostics[0] + .package_id + .as_ref() + .unwrap() + .to_string(), + "android/app/org.fdroid.fdroid" + ); + } + #[test] fn missing_directory_is_stable_repository_diagnostic() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index 95b7fe8..fd16ef5 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -13,6 +13,10 @@ pub const REPOSITORY_ROOT_DIR: &str = "repo"; pub const RUNTIME_CONFIG_DIR: &str = "rc"; pub const REPOSITORY_ROOT_METADATA_FILE: &str = "metadata.jsonc"; pub const REPOSITORY_ROOT_METADATA_VERSION: u32 = 1; +pub const REPOSITORY_SELF_METADATA_DIR: &str = ".metadata"; +pub const REPOSITORY_LUACLASS_DIR: &str = "luaclass"; +pub const PACKAGE_METADATA_FILE: &str = "metadata.jsonc"; +pub const LUA_SCRIPT_EXTENSION: &str = "lua"; pub const LOCAL_REPOSITORY_ALIAS: &str = "local"; pub const DEFAULT_GENERATED_REPOSITORY_ALIAS: &str = "autogen"; @@ -234,6 +238,36 @@ pub struct PackageFile { pub path: PathBuf, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryPackageDirectoryLayout { + pub root: PathBuf, + pub packages: Vec, + pub invalid_packages: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PackageDirectory { + pub id: PackageId, + pub path: PathBuf, + pub metadata_path: PathBuf, + pub version_scripts: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PackageVersionScript { + pub version: String, + pub file_name: String, + pub path: PathBuf, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InvalidPackageDirectory { + pub id: Option, + pub path: PathBuf, + pub metadata_path: PathBuf, + pub reason: String, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct RepositoryPackageCacheKey { pub repository_id: RepositoryId, @@ -322,6 +356,26 @@ pub enum RepositoryLoadError { }, } +impl RepositoryPackageDirectoryLayout { + pub fn load(root: impl AsRef) -> Result { + let root = root.as_ref().to_path_buf(); + let mut packages = Vec::new(); + let mut invalid_packages = Vec::new(); + collect_package_directories(&root, &root, &mut packages, &mut invalid_packages)?; + packages.sort_by_key(|package| package.id.to_string()); + invalid_packages.sort_by_key(|package| package.path.clone()); + Ok(Self { + root, + packages, + invalid_packages, + }) + } + + pub fn package(&self, id: &PackageId) -> Option<&PackageDirectory> { + self.packages.iter().find(|package| &package.id == id) + } +} + impl RepositoryLayout { pub fn load(root: impl AsRef) -> Result { let root = root.as_ref().to_path_buf(); @@ -403,25 +457,16 @@ fn collect_package_files( current: &Path, out: &mut Vec, ) -> Result<(), RepositoryLoadError> { - for entry in fs::read_dir(current).map_err(|source| RepositoryLoadError::ReadPackagesDir { - path: current.to_path_buf(), - source, - })? { - let entry = entry.map_err(|source| RepositoryLoadError::ReadPackagesDir { - path: current.to_path_buf(), - source, - })?; + for entry in read_dir_entries(current)? { let path = entry.path(); - let file_type = - entry - .file_type() - .map_err(|source| RepositoryLoadError::ReadPackagesDir { - path: current.to_path_buf(), - source, - })?; + let file_type = entry_file_type(&entry, current)?; if file_type.is_dir() { collect_package_files(packages_root, &path, out)?; - } else if file_type.is_file() && path.extension().is_some_and(|ext| ext == "lua") { + } else if file_type.is_file() + && path + .extension() + .is_some_and(|ext| ext == LUA_SCRIPT_EXTENSION) + { let id = package_id_from_path(packages_root, &path)?; out.push(PackageFile { id, path }); } @@ -429,6 +474,171 @@ fn collect_package_files( Ok(()) } +fn collect_package_directories( + repository_root: &Path, + current: &Path, + packages: &mut Vec, + invalid_packages: &mut Vec, +) -> Result<(), RepositoryLoadError> { + if current == repository_root.join(REPOSITORY_SELF_METADATA_DIR) + || current == repository_root.join(REPOSITORY_LUACLASS_DIR) + { + return Ok(()); + } + + let metadata_path = current.join(PACKAGE_METADATA_FILE); + if metadata_path.is_file() { + collect_package_boundary( + repository_root, + current, + &metadata_path, + packages, + invalid_packages, + )?; + return Ok(()); + } + + for entry in read_dir_entries(current)? { + let file_type = entry_file_type(&entry, current)?; + if file_type.is_dir() { + collect_package_directories( + repository_root, + &entry.path(), + packages, + invalid_packages, + )?; + } + } + Ok(()) +} + +fn collect_package_boundary( + repository_root: &Path, + package_dir: &Path, + metadata_path: &Path, + packages: &mut Vec, + invalid_packages: &mut Vec, +) -> Result<(), RepositoryLoadError> { + let id = package_id_from_package_dir(repository_root, package_dir); + if let Err(error) = parse_package_metadata(metadata_path) { + invalid_packages.push(InvalidPackageDirectory { + id: id.ok(), + path: package_dir.to_path_buf(), + metadata_path: metadata_path.to_path_buf(), + reason: error, + }); + return Ok(()); + } + let id = match id { + Ok(id) => id, + Err(error) => { + invalid_packages.push(InvalidPackageDirectory { + id: None, + path: package_dir.to_path_buf(), + metadata_path: metadata_path.to_path_buf(), + reason: error.to_string(), + }); + return Ok(()); + } + }; + packages.push(PackageDirectory { + id, + path: package_dir.to_path_buf(), + metadata_path: metadata_path.to_path_buf(), + version_scripts: discover_version_scripts(package_dir)?, + }); + Ok(()) +} + +fn parse_package_metadata(metadata_path: &Path) -> Result<(), String> { + let bytes = fs::read(metadata_path) + .map_err(|source| format!("failed to read package metadata: {source}"))?; + let value = serde_json::from_reader::<_, serde_json::Value>(json_comments::StripComments::new( + bytes.as_slice(), + )) + .map_err(|source| format!("failed to parse package metadata: {source}"))?; + if value.is_object() { + Ok(()) + } else { + Err("package metadata must be a JSON object".to_owned()) + } +} + +fn discover_version_scripts( + package_dir: &Path, +) -> Result, RepositoryLoadError> { + let mut scripts = Vec::new(); + for entry in read_dir_entries(package_dir)? { + let path = entry.path(); + let file_type = entry_file_type(&entry, package_dir)?; + if !file_type.is_file() { + continue; + } + let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else { + continue; + }; + if file_name.starts_with('.') { + continue; + } + if path + .extension() + .is_none_or(|extension| extension != LUA_SCRIPT_EXTENSION) + { + continue; + } + let Some(version) = path.file_stem().and_then(|stem| stem.to_str()) else { + continue; + }; + scripts.push(PackageVersionScript { + version: version.to_owned(), + file_name: file_name.to_owned(), + path, + }); + } + scripts.sort_by_key(|script| script.file_name.clone()); + Ok(scripts) +} + +fn read_dir_entries(path: &Path) -> Result, RepositoryLoadError> { + fs::read_dir(path) + .map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: path.to_path_buf(), + source, + })? + .collect::, _>>() + .map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: path.to_path_buf(), + source, + }) +} + +fn entry_file_type( + entry: &fs::DirEntry, + parent: &Path, +) -> Result { + entry + .file_type() + .map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: parent.to_path_buf(), + source, + }) +} + +pub fn package_id_from_package_dir( + repository_root: impl AsRef, + package_dir: impl AsRef, +) -> Result { + let repository_root = repository_root.as_ref(); + let package_dir = package_dir.as_ref(); + let relative = package_dir.strip_prefix(repository_root).map_err(|_| { + RepositoryLoadError::InvalidPackagePath { + path: package_dir.to_path_buf(), + reason: format!("path is not under {}", repository_root.display()), + } + })?; + package_id_from_relative_path(package_dir, relative) +} + pub fn package_id_from_path( packages_root: impl AsRef, path: impl AsRef, @@ -447,8 +657,14 @@ pub fn package_id_from_path( reason: "package file must have .lua extension".to_owned(), }); } - let without_extension = relative.with_extension(""); - let mut parts = without_extension.components(); + package_id_from_relative_path(path, &relative.with_extension("")) +} + +fn package_id_from_relative_path( + path: &Path, + relative: &Path, +) -> Result { + let mut parts = relative.components(); let kind = parts .next() .ok_or_else(|| RepositoryLoadError::InvalidPackagePath { @@ -674,6 +890,156 @@ mod tests { assert_eq!(id.to_string(), "android/org.fdroid.fdroid"); } + #[test] + fn derives_package_id_from_package_directory() { + let root = PathBuf::from("repo/official"); + let id = package_id_from_package_dir(&root, "repo/official/android/f-droid/magisk/hello") + .unwrap(); + assert_eq!(id.to_string(), "android/f-droid/magisk/hello"); + } + + #[test] + fn discovers_package_directories_and_direct_child_version_scripts() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + let package_dir = root.join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(package_dir.join("nested")).unwrap(); + fs::write( + package_dir.join(PACKAGE_METADATA_FILE), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + fs::write(package_dir.join("1.2.3.lua"), "return {}").unwrap(); + fs::write(package_dir.join("9999.lua"), "return {}").unwrap(); + fs::write(package_dir.join(".disabled.lua"), "return {}").unwrap(); + fs::write(package_dir.join("nested/2.0.lua"), "return {}").unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert_eq!(layout.invalid_packages, Vec::new()); + assert_eq!(layout.packages.len(), 1); + let package = &layout.packages[0]; + assert_eq!(package.id.to_string(), "android/app/org.fdroid.fdroid"); + assert_eq!(package.path, package_dir); + assert_eq!( + package.metadata_path, + package.path.join(PACKAGE_METADATA_FILE) + ); + assert_eq!( + package + .version_scripts + .iter() + .map(|script| (script.version.as_str(), script.file_name.as_str())) + .collect::>(), + vec![("1.2.3", "1.2.3.lua"), ("9999", "9999.lua")] + ); + } + + #[test] + fn package_metadata_boundary_stops_nested_discovery_even_when_invalid() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + let package_dir = root.join("android/app/broken"); + fs::create_dir_all(package_dir.join("nested/android/app/hidden")).unwrap(); + fs::write(package_dir.join(PACKAGE_METADATA_FILE), "{not-json").unwrap(); + fs::write( + package_dir.join("nested/android/app/hidden/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert!(layout.packages.is_empty()); + assert_eq!(layout.invalid_packages.len(), 1); + assert_eq!( + layout.invalid_packages[0] + .id + .as_ref() + .map(ToString::to_string), + Some("android/app/broken".to_owned()) + ); + } + + #[test] + fn repository_reserved_roots_are_not_package_discovery_roots() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::create_dir_all(root.join(".metadata/android/app/hidden")).unwrap(); + fs::write( + root.join(".metadata/android/app/hidden/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + fs::create_dir_all(root.join("luaclass/android/app/hidden")).unwrap(); + fs::write( + root.join("luaclass/android/app/hidden/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + fs::create_dir_all(root.join("android/app/visible")).unwrap(); + fs::write( + root.join("android/app/visible/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert_eq!(layout.packages.len(), 1); + assert_eq!(layout.packages[0].id.to_string(), "android/app/visible"); + } + + #[test] + fn autogen_record_alone_does_not_create_package_boundary() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::create_dir_all(root.join("android/app/generated")).unwrap(); + fs::write(root.join("android/app/generated/.autogen.jsonc"), "{}").unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert!(layout.packages.is_empty()); + assert!(layout.invalid_packages.is_empty()); + } + + #[test] + fn package_metadata_must_be_an_object() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::create_dir_all(root.join("android/app/not-object")).unwrap(); + fs::write(root.join("android/app/not-object/metadata.jsonc"), "[]").unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert!(layout.packages.is_empty()); + assert_eq!(layout.invalid_packages.len(), 1); + assert_eq!( + layout.invalid_packages[0].reason, + "package metadata must be a JSON object" + ); + } + + #[test] + fn invalid_package_path_is_reported_as_invalid_package() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::create_dir_all(root.join("invalidkind/app/example")).unwrap(); + fs::write( + root.join("invalidkind/app/example/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert!(layout.packages.is_empty()); + assert_eq!(layout.invalid_packages.len(), 1); + assert!(layout.invalid_packages[0] + .reason + .contains("unsupported package kind")); + } + #[test] fn loads_repository_layout_with_required_directories() { let temp = tempfile::tempdir().unwrap(); From 8e880e689457ae48aad3e73ccf7dc12acbaabb0e Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 00:23:38 +0800 Subject: [PATCH 30/72] feat(autogen): write installed packages as directories --- Cargo.lock | 67 ++ crates/getter-cli/src/lib.rs | 44 +- crates/getter-cli/tests/bdd_cli.rs | 130 ++- .../features/cli/autogen_installed.feature | 28 +- crates/getter-core/Cargo.toml | 1 + crates/getter-core/src/autogen.rs | 380 ++++---- crates/getter-operations/Cargo.toml | 1 + crates/getter-operations/src/autogen.rs | 840 +++++++++++------- 8 files changed, 942 insertions(+), 549 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23480e3..34622f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,6 +91,15 @@ version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bstr" version = "1.12.1" @@ -229,6 +238,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -254,6 +272,16 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "cucumber" version = "0.23.0" @@ -334,6 +362,16 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "either" version = "1.16.0" @@ -480,6 +518,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -540,6 +588,7 @@ dependencies = [ "mlua", "serde", "serde_json", + "sha2", "tempfile", "thiserror 1.0.69", "toml", @@ -568,6 +617,7 @@ dependencies = [ "getter-core", "getter-providers", "getter-storage", + "json_comments", "serde", "serde_json", "tempfile", @@ -1364,6 +1414,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "2.0.1" @@ -1625,6 +1686,12 @@ dependencies = [ "syn", ] +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + [[package]] name = "unicode-ident" version = "1.0.24" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 020e1f9..9b03e76 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -8,7 +8,8 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; use getter_core::repository::{ - GetterDataDirLayout, RepositoryLayout, RepositoryMetadata, REPOSITORY_ROOT_METADATA_FILE, + default_repository_priority, GetterDataDirLayout, RepositoryLayout, RepositoryMetadata, + RepositoryPackageDirectoryLayout, REPOSITORY_ROOT_METADATA_FILE, REPO_API_VERSION_V1, }; use getter_core::runtime::{GetterRuntime, SealedActionPlan}; use getter_core::task::{ @@ -588,17 +589,7 @@ fn execute(invocation: CliInvocation) -> Result { } CliCommand::RepoAdd { id, path, priority } => { let db = open_main_db(&invocation.data_dir)?; - let layout = load_repository_layout(&path)?; - if layout.metadata.id != id { - return Err(CliError::Repository(format!( - "repo.toml id '{}' does not match requested id '{}'", - layout.metadata.id, id - ))); - } - let metadata = RepositoryMetadata { - priority: priority.unwrap_or(layout.metadata.priority), - ..layout.metadata.clone() - }; + let metadata = load_repository_metadata(&id, &path, priority)?; db.upsert_repository(&metadata, Some(&path), None)?; Ok(json!({ "repository": repository_metadata_json(&metadata, Some(&path), None) })) } @@ -938,6 +929,35 @@ fn load_repository_layout(path: &Path) -> Result { RepositoryLayout::load(path).map_err(|source| CliError::Repository(source.to_string())) } +fn load_repository_metadata( + id: &RepositoryId, + path: &Path, + priority: Option, +) -> Result { + if path.join("repo.toml").is_file() { + let layout = load_repository_layout(path)?; + if &layout.metadata.id != id { + return Err(CliError::Repository(format!( + "repo.toml id '{}' does not match requested id '{}'", + layout.metadata.id, id + ))); + } + return Ok(RepositoryMetadata { + priority: priority.unwrap_or(layout.metadata.priority), + ..layout.metadata + }); + } + + RepositoryPackageDirectoryLayout::load(path) + .map_err(|source| CliError::Repository(source.to_string()))?; + Ok(RepositoryMetadata { + id: id.clone(), + name: id.to_string(), + priority: priority.unwrap_or_else(|| default_repository_priority(id.as_str())), + api_version: REPO_API_VERSION_V1.to_owned(), + }) +} + fn list_repositories(db: &MainDb) -> Result, CliError> { Ok(db .repositories()? diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 8a8e950..e5e2787 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -180,7 +180,7 @@ fn tampered_autogen_cleanup_preview_for_package(world: &mut CliWorld, package_id "package_id": package_id, "action": "delete", "output_relative_path": package_relative_path(&package_id), - "content_hash": "fnv1a64:0000000000000000", + "content_hash": "sha512:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "reason": "not_in_installed_inventory", } ], @@ -897,7 +897,10 @@ fn command_fails_with_autogen_error(world: &mut CliWorld) { assert_eq!(output.status.code(), Some(1)); let json = parse_stdout(output); assert_eq!(json["ok"], false); - assert_eq!(json["command"], "autogen cleanup apply"); + assert!(matches!( + json["command"].as_str(), + Some("autogen cleanup apply" | "autogen installed apply") + )); assert_eq!(json["error"]["code"], "autogen.error"); world.json = Some(json); } @@ -1315,8 +1318,16 @@ fn save_autogen_preview_to_file(world: &mut CliWorld) { #[then(expr = "the autogen repository contains generated package {string}")] fn autogen_repository_contains_generated_package(world: &mut CliWorld, package_id: String) { let path = autogen_repo_path(world).join(package_relative_path(&package_id)); - assert!(path.is_file(), "generated package should exist: {path:?}"); - let content = fs::read_to_string(&path).expect("generated package readable"); + assert!( + path.is_dir(), + "generated package directory should exist: {path:?}" + ); + assert!(path.join("metadata.jsonc").is_file()); + assert!(path.join("Manifest").is_file()); + let version_script = path.join("9999.lua"); + assert!(version_script.is_file()); + assert!(path.join(".autogen.jsonc").is_file()); + let content = fs::read_to_string(version_script).expect("generated package readable"); assert!(content.contains("@generated by UpgradeAll getter autogen")); } @@ -1374,8 +1385,15 @@ fn autogen_cleanup_preview_contains_delete_candidate(world: &mut CliWorld, packa fn autogen_repository_does_not_contain_generated_package(world: &mut CliWorld, package_id: String) { let path = autogen_repo_path(world).join(package_relative_path(&package_id)); assert!( - !path.exists(), - "generated package should be deleted: {path:?}" + path.is_dir(), + "cleanup keeps generated package directory: {path:?}" + ); + assert_eq!( + fs::read_dir(&path) + .expect("generated package dir readable") + .count(), + 0, + "generated package directory should be empty after cleanup: {path:?}" ); } @@ -1398,32 +1416,30 @@ fn replace_generated_autogen_package_with_user_edited_content( package_id: String, ) { let path = autogen_repo_path(world).join(package_relative_path(&package_id)); - assert!(path.is_file(), "generated package should exist before edit"); + let version_script = path.join("9999.lua"); + assert!( + version_script.is_file(), + "generated package version script should exist before edit" + ); fs::write( - &path, - format!( - "-- user edited\nreturn package_def {{ id = {id:?}, name = \"Edited Autogen\" }}\n", - id = package_id - ), + &version_script, + "#!/bin/upa-lua v1\n-- user edited\nreturn package_version {}\n", ) .expect("overwrite generated package with user edit"); } -#[then(expr = "local repository contains preserved package {string}")] -fn local_repository_contains_preserved_package(world: &mut CliWorld, package_id: String) { +#[then("local repository has not been written")] +fn local_repository_has_not_been_written(world: &mut CliWorld) { let local_path = world .data_dir .as_ref() .expect("data dir exists") .join("repo") - .join("local") - .join(package_relative_path(&package_id)); + .join("local"); assert!( - local_path.is_file(), - "modified autogen file should be preserved into local: {local_path:?}" + !local_path.exists(), + "autogen ownership conflicts must not preserve files into local: {local_path:?}" ); - let content = fs::read_to_string(local_path).expect("preserved local file readable"); - assert!(content.contains("Edited Autogen")); } #[then("no partially usable imported state is created")] @@ -1608,12 +1624,7 @@ fn autogen_repo_path(world: &CliWorld) -> PathBuf { } fn package_relative_path(package_id: &str) -> PathBuf { - let (kind, name) = package_id - .split_once('/') - .expect("test package id has kind/name"); - PathBuf::from("packages") - .join(kind) - .join(format!("{name}.lua")) + package_id.split('/').collect() } fn create_fixture_lua_repository( @@ -1624,23 +1635,49 @@ fn create_fixture_lua_repository( ) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); - let package_name_path = package_id - .strip_prefix("android/") - .expect("fixture package id should be android package id"); - fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); - fs::create_dir(repo_path.join("lib")).expect("create lib dir"); - fs::create_dir(repo_path.join("templates")).expect("create templates dir"); - fs::write( - repo_path.join("repo.toml"), - format!( - "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" - ), - ) - .expect("write repo.toml"); - fs::write( - repo_path.join(format!("packages/android/{package_name_path}.lua")), - format!( - r#" + if let Some(package_name_path) = package_id.strip_prefix("android/app/") { + let package_dir = repo_path.join(package_relative_path(&package_id)); + fs::create_dir_all(&package_dir).expect("create package dir"); + fs::write( + package_dir.join("metadata.jsonc"), + format!( + r#"{{ "type": "android:app", "display_name": {name:?}, "android": {{ "package_name": {package_name_path:?} }} }}"#, + name = package_name, + ), + ) + .expect("write package metadata"); + fs::write(package_dir.join("Manifest"), "").expect("write Manifest"); + fs::write( + package_dir.join("9999.lua"), + format!( + r#"#!/bin/upa-lua v1 +return package_version {{ + installed = {{ + {{ kind = "android_package", package_name = "{package_name_path}" }}, + }}, +}} +"# + ), + ) + .expect("write package Lua"); + } else { + let package_name_path = package_id + .strip_prefix("android/") + .expect("fixture package id should be android package id"); + fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); + fs::create_dir(repo_path.join("lib")).expect("create lib dir"); + fs::create_dir(repo_path.join("templates")).expect("create templates dir"); + fs::write( + repo_path.join("repo.toml"), + format!( + "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" + ), + ) + .expect("write repo.toml"); + fs::write( + repo_path.join(format!("packages/android/{package_name_path}.lua")), + format!( + r#" return package_def {{ id = "{package_id}", name = "{package_name}", @@ -1649,9 +1686,10 @@ return package_def {{ }}, }} "# - ), - ) - .expect("write package Lua"); + ), + ) + .expect("write package Lua"); + } world.fixture_repo_id = Some(repo_id); world.fixture_repo_path = Some(repo_path); diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 5bbb8c9..695dc78 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -6,10 +6,10 @@ Feature: Installed app autogen When I run getter autogen installed preview for that inventory Then the command succeeds And the output is valid JSON - And the autogen preview contains candidate "android/com.example.autogen" + And the autogen preview contains candidate "android/app/com.example.autogen" And the autogen repository has not been written - Scenario: User applies installed app autogen and evaluates the generated fallback package + Scenario: User applies installed app autogen and validates the generated fallback package directory Given an initialized getter data directory And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" When I run getter autogen installed preview for that inventory @@ -17,22 +17,20 @@ Feature: Installed app autogen And I save the autogen preview to a file When I run getter autogen installed apply for that preview with accept-all Then the command succeeds - And the autogen repository contains generated package "android/com.example.autogen" - And the app list contains autogen tracked package "android/com.example.autogen" + And the autogen repository contains generated package "android/app/com.example.autogen" + And the app list contains autogen tracked package "android/app/com.example.autogen" When I run getter repo validate for autogen Then the output reports a valid repository without network - When I run getter package eval for package "android/com.example.autogen" from autogen - Then the output contains package "android/com.example.autogen" named "Example Autogen" Scenario: Higher-priority repositories suppress installed app autogen candidates Given an initialized getter data directory - And a fixture Lua repository "official" with package "android/com.example.autogen" named "Official Example" + And a fixture Lua repository "official" with package "android/app/com.example.autogen" named "Official Example" And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" When I run getter repo add for that repository with priority 0 Then the command succeeds When I run getter autogen installed preview for that inventory Then the command succeeds - And the autogen preview skips package "android/com.example.autogen" because repository "official" covers it + And the autogen preview skips package "android/app/com.example.autogen" because repository "official" covers it Scenario: Cleanup deletes only accepted generated packages missing from installed inventory Given an initialized getter data directory @@ -45,12 +43,12 @@ Feature: Installed app autogen Given an empty installed inventory When I run getter autogen cleanup preview for that inventory Then the command succeeds - And the autogen cleanup preview contains delete candidate "android/com.example.old" + And the autogen cleanup preview contains delete candidate "android/app/com.example.old" And I save the autogen preview to a file When I run getter autogen cleanup apply for that preview with accept-all Then the command succeeds - And the autogen repository does not contain generated package "android/com.example.old" - And the app list does not contain package "android/com.example.old" + And the autogen repository does not contain generated package "android/app/com.example.old" + And the app list does not contain package "android/app/com.example.old" Scenario: Cleanup rejects tampered previews for non-autogen tracked packages Given an initialized getter data directory @@ -75,7 +73,7 @@ Feature: Installed app autogen Then the command succeeds And the app list contains imported package "android/org.fdroid.fdroid" - Scenario: Applying over modified autogen preserves the user-edited file into local + Scenario: Applying over modified autogen reports an ownership conflict Given an initialized getter data directory And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" When I run getter autogen installed preview for that inventory @@ -83,10 +81,10 @@ Feature: Installed app autogen And I save the autogen preview to a file When I run getter autogen installed apply for that preview with accept-all Then the command succeeds - And I replace generated autogen package "android/com.example.autogen" with user-edited content + And I replace generated autogen package "android/app/com.example.autogen" with user-edited content When I run getter autogen installed preview for that inventory Then the command succeeds And I save the autogen preview to a file When I run getter autogen installed apply for that preview with accept-all - Then the command succeeds - And local repository contains preserved package "android/com.example.autogen" + Then the command fails with an autogen error + And local repository has not been written diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 4eac396..4b1f9c5 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -12,6 +12,7 @@ json_comments = "0.2" mlua = { version = "0.10", features = ["luajit", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" +sha2 = "0.10" thiserror = "1" toml = "0.8" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index a3bfe8e..8a30220 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -1,20 +1,20 @@ //! Installed-inventory autogen planning for generated fallback repositories. //! //! This module is pure domain logic: platform adapters provide installed -//! inventory facts, while getter decides package ids, generated Lua shape, -//! deterministic paths, and preview DTOs. +//! inventory facts, while getter decides package ids, generated package +//! directory contents, deterministic paths, and preview DTOs. -use crate::repository::REPO_API_VERSION_V1; use crate::{InstalledTarget, PackageId, PackageKind, RepositoryId, RepositoryPriority}; use serde::{Deserialize, Serialize}; -use std::collections::{BTreeSet, HashMap}; -use std::path::PathBuf; +use sha2::{Digest, Sha512}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::path::{Path, PathBuf}; pub const INSTALLED_INVENTORY_FORMAT: &str = "upgradeall-installed-inventory"; pub const INSTALLED_INVENTORY_VERSION: u32 = 1; -pub const AUTOGEN_MANIFEST_VERSION: u32 = 1; -pub const LOCAL_REPOSITORY_ID: &str = "local"; -pub const LOCAL_REPOSITORY_NAME: &str = "Local"; +pub const AUTOGEN_RECORD_FILE: &str = ".autogen.jsonc"; +pub const AUTOGEN_RECORD_VERSION: u32 = 1; +pub const INSTALLED_AUTOGEN_GENERATOR: &str = "installed-inventory"; pub const DEFAULT_AUTOGEN_REPOSITORY_ID: &str = "autogen"; pub const DEFAULT_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Autogen"; pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter autogen"; @@ -76,11 +76,39 @@ pub struct AutogenCandidate { pub package_id: PackageId, pub name: String, pub installed: InstalledTarget, + pub relative_path: PathBuf, + pub files: Vec, + pub record: AutogenRecord, + /// Hash of the serialized `.autogen.jsonc` record content. + pub content_hash: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GeneratedPackageFile { pub relative_path: PathBuf, pub content: String, pub content_hash: String, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenRecord { + pub version: u32, + pub generator: String, + pub package_id: PackageId, + pub output_relative_path: PathBuf, + pub input: AutogenRecordInput, + /// Getter-written generated files and their content hashes. + /// `.autogen.jsonc` itself is intentionally excluded. + pub files: BTreeMap, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum AutogenRecordInput { + InstalledAndroidPackage { package_name: String }, + InstalledMagiskModule { module_id: String }, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct AutogenSkip { pub package_id: PackageId, @@ -96,44 +124,6 @@ pub enum AutogenSkipReason { CoveredByHigherPriorityRepository, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct AutogenManifest { - pub version: u32, - pub repository_id: RepositoryId, - pub packages: Vec, -} - -impl AutogenManifest { - pub fn from_candidates(candidates: &[AutogenCandidate]) -> Self { - Self { - version: AUTOGEN_MANIFEST_VERSION, - repository_id: RepositoryId::new(DEFAULT_AUTOGEN_REPOSITORY_ID) - .expect("autogen is a valid repository id"), - packages: candidates - .iter() - .map(|candidate| AutogenManifestEntry { - package_id: candidate.package_id.clone(), - relative_path: candidate.relative_path.clone(), - content_hash: candidate.content_hash.clone(), - }) - .collect(), - } - } - - pub fn package(&self, package_id: &PackageId) -> Option<&AutogenManifestEntry> { - self.packages - .iter() - .find(|entry| &entry.package_id == package_id) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct AutogenManifestEntry { - pub package_id: PackageId, - pub relative_path: PathBuf, - pub content_hash: String, -} - #[derive(Debug, thiserror::Error)] pub enum AutogenError { #[error("unsupported installed inventory format '{0}'")] @@ -144,14 +134,16 @@ pub enum AutogenError { EmptyIdentity, #[error("failed to construct package id from installed inventory: {0}")] PackageId(#[from] crate::PackageIdError), + #[error("failed to serialize generated autogen record: {0}")] + SerializeRecord(#[from] serde_json::Error), } /// Build a deterministic installed-autogen preview from installed inventory. /// /// `covered_packages` contains package ids already supplied by repositories with /// priority higher than the generated repository. Those packages are skipped so -/// generated fallback files do not compete with user-authored `local` or -/// upstream package files. +/// generated fallback directories do not compete with user-authored `local` or +/// upstream package directories. pub fn plan_installed_autogen( inventory: &InstalledInventory, covered_packages: &HashMap, @@ -210,38 +202,25 @@ pub fn validate_installed_inventory(inventory: &InstalledInventory) -> Result<() Ok(()) } -pub fn default_autogen_repo_toml() -> String { - repo_toml( - DEFAULT_AUTOGEN_REPOSITORY_ID, - DEFAULT_AUTOGEN_REPOSITORY_NAME, - RepositoryPriority::GENERATED_FALLBACK, - ) +pub fn content_hash(content: &str) -> String { + content_hash_bytes(content.as_bytes()) } -pub fn local_repo_toml() -> String { - repo_toml( - LOCAL_REPOSITORY_ID, - LOCAL_REPOSITORY_NAME, - RepositoryPriority::LOCAL, - ) +pub fn content_hash_bytes(content: &[u8]) -> String { + let mut hasher = Sha512::new(); + hasher.update(content); + format!("sha512:{:x}", hasher.finalize()) } -fn repo_toml(id: &str, name: &str, priority: RepositoryPriority) -> String { - format!( - "id = \"{id}\"\nname = \"{name}\"\npriority = {}\napi_version = \"{REPO_API_VERSION_V1}\"\n", - priority.value() - ) -} - -pub fn generated_file_is_managed(content: &str) -> bool { - content - .lines() - .next() - .is_some_and(|line| line.contains(GENERATED_MARKER)) +pub fn render_autogen_record(record: &AutogenRecord) -> Result { + serde_json::to_string_pretty(record).map(|mut content| { + content.push('\n'); + content + }) } -pub fn content_hash(content: &str) -> String { - format!("fnv1a64:{:016x}", fnv1a64(content.as_bytes())) +pub fn package_relative_path(package_id: &PackageId) -> PathBuf { + package_id.to_string().split('/').collect() } fn candidate_from_inventory_item( @@ -254,7 +233,7 @@ fn candidate_from_inventory_item( .. } => { let package_name = non_empty(package_name)?; - let package_id = PackageId::new(PackageKind::Android, package_name)?; + let package_id = PackageId::new(PackageKind::Android, format!("app/{package_name}"))?; let name = label .as_deref() .filter(|label| !label.trim().is_empty()) @@ -263,17 +242,10 @@ fn candidate_from_inventory_item( let installed = InstalledTarget::AndroidPackage { package_name: package_name.to_owned(), }; - let relative_path = package_relative_path(&package_id); - let content = render_package_lua(&package_id, &name, &installed); - let content_hash = content_hash(&content); - Ok(AutogenCandidate { - package_id, - name, - installed, - relative_path, - content, - content_hash, - }) + let input = AutogenRecordInput::InstalledAndroidPackage { + package_name: package_name.to_owned(), + }; + candidate(package_id, name, installed, input) } InstalledInventoryItem::MagiskModule { module_id, name, .. @@ -288,38 +260,113 @@ fn candidate_from_inventory_item( let installed = InstalledTarget::MagiskModule { module_id: module_id.to_owned(), }; - let relative_path = package_relative_path(&package_id); - let content = render_package_lua(&package_id, &name, &installed); - let content_hash = content_hash(&content); - Ok(AutogenCandidate { - package_id, - name, - installed, - relative_path, - content, - content_hash, - }) + let input = AutogenRecordInput::InstalledMagiskModule { + module_id: module_id.to_owned(), + }; + candidate(package_id, name, installed, input) } } } -fn non_empty(value: &str) -> Result<&str, AutogenError> { - let value = value.trim(); - if value.is_empty() { - Err(AutogenError::EmptyIdentity) - } else { - Ok(value) +fn candidate( + package_id: PackageId, + name: String, + installed: InstalledTarget, + input: AutogenRecordInput, +) -> Result { + let relative_path = package_relative_path(&package_id); + let files = generated_package_files(&name, &installed)?; + let record = autogen_record(package_id.clone(), relative_path.clone(), input, &files); + let content_hash = content_hash(&render_autogen_record(&record)?); + Ok(AutogenCandidate { + package_id, + name, + installed, + relative_path, + files, + record, + content_hash, + }) +} + +fn generated_package_files( + name: &str, + installed: &InstalledTarget, +) -> Result, AutogenError> { + let metadata = match installed { + InstalledTarget::AndroidPackage { package_name } => serde_json::json!({ + "type": "android:app", + "display_name": name, + "android": { "package_name": package_name }, + }), + InstalledTarget::MagiskModule { module_id } => serde_json::json!({ + "type": "magisk:module", + "display_name": name, + "magisk": { "module_id": module_id }, + }), + InstalledTarget::Generic { id } => serde_json::json!({ + "type": "generic", + "display_name": name, + "generic": { "id": id }, + }), + }; + let metadata = render_pretty_json(metadata)?; + let manifest = String::new(); + let version_lua = render_version_lua(installed); + Ok(vec![ + generated_file("metadata.jsonc", metadata), + generated_file("Manifest", manifest), + generated_file("9999.lua", version_lua), + ]) +} + +fn render_pretty_json(value: serde_json::Value) -> Result { + serde_json::to_string_pretty(&value).map(|mut content| { + content.push('\n'); + content + }) +} + +fn generated_file(relative_path: &str, content: String) -> GeneratedPackageFile { + GeneratedPackageFile { + relative_path: PathBuf::from(relative_path), + content_hash: content_hash(&content), + content, } } -pub fn package_relative_path(package_id: &PackageId) -> PathBuf { - let mut path = PathBuf::from("packages"); - path.push(package_id.kind().as_str()); - path.push(format!("{}.lua", package_id.name())); - path +fn autogen_record( + package_id: PackageId, + output_relative_path: PathBuf, + input: AutogenRecordInput, + files: &[GeneratedPackageFile], +) -> AutogenRecord { + AutogenRecord { + version: AUTOGEN_RECORD_VERSION, + generator: INSTALLED_AUTOGEN_GENERATOR.to_owned(), + package_id, + output_relative_path, + input, + files: files + .iter() + .map(|file| { + ( + record_file_key(&file.relative_path), + file.content_hash.clone(), + ) + }) + .collect(), + } } -fn render_package_lua(package_id: &PackageId, name: &str, installed: &InstalledTarget) -> String { +pub fn record_file_key(path: &Path) -> String { + path.components() + .map(|component| component.as_os_str().to_string_lossy()) + .collect::>() + .join("/") +} + +fn render_version_lua(installed: &InstalledTarget) -> String { let installed_lua = match installed { InstalledTarget::AndroidPackage { package_name } => format!( "{{ kind = \"android_package\", package_name = {} }}", @@ -335,9 +382,7 @@ fn render_package_lua(package_id: &PackageId, name: &str, installed: &InstalledT }; format!( - "-- {GENERATED_MARKER}\nreturn package_def {{\n id = {},\n name = {},\n installed = {{\n {installed_lua},\n }},\n}}\n", - lua_string(&package_id.to_string()), - lua_string(name) + "#!/bin/upa-lua v1\n-- {GENERATED_MARKER}\nreturn package_version {{\n installed = {{\n {installed_lua},\n }},\n}}\n", ) } @@ -357,27 +402,23 @@ fn lua_string(value: &str) -> String { escaped } -fn fnv1a64(bytes: &[u8]) -> u64 { - let mut hash = 0xcbf29ce484222325u64; - for byte in bytes { - hash ^= u64::from(*byte); - hash = hash.wrapping_mul(0x100000001b3); +fn non_empty(value: &str) -> Result<&str, AutogenError> { + let value = value.trim(); + if value.is_empty() { + Err(AutogenError::EmptyIdentity) + } else { + Ok(value) } - hash } #[cfg(test)] mod tests { use super::*; - #[cfg(feature = "lua")] - use crate::lua::evaluate_package_file; - #[cfg(feature = "lua")] - use crate::repository::RepositoryLayout; - #[cfg(feature = "lua")] + use crate::repository::RepositoryPackageDirectoryLayout; use std::fs; #[test] - fn preview_generates_deterministic_android_package_lua() { + fn preview_generates_deterministic_android_package_directory() { let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { package_name: "org.fdroid.fdroid".to_owned(), label: Some("F-Droid".to_owned()), @@ -396,20 +437,34 @@ mod tests { let candidate = &plan.candidates[0]; assert_eq!( candidate.package_id.to_string(), - "android/org.fdroid.fdroid" + "android/app/org.fdroid.fdroid" ); assert_eq!( candidate.relative_path, - PathBuf::from("packages/android/org.fdroid.fdroid.lua") + PathBuf::from("android/app/org.fdroid.fdroid") + ); + assert_eq!(candidate.files.len(), 3); + assert!(candidate + .files + .iter() + .any(|file| file.relative_path == PathBuf::from("metadata.jsonc") + && file.content.contains("org.fdroid.fdroid"))); + assert!(candidate.files.iter().any(|file| { + file.relative_path == PathBuf::from("9999.lua") + && file.content.starts_with("#!/bin/upa-lua v1\n") + && file.content.contains(GENERATED_MARKER) + })); + assert_eq!(candidate.record.package_id, candidate.package_id); + assert_eq!(candidate.record.files.len(), candidate.files.len()); + assert_eq!( + candidate.content_hash, + content_hash(&render_autogen_record(&candidate.record).unwrap()) ); - assert!(candidate.content.contains(GENERATED_MARKER)); - assert!(candidate.content.contains("package_def")); - assert_eq!(candidate.content_hash, content_hash(&candidate.content)); } #[test] fn preview_skips_packages_covered_by_higher_priority_repositories() { - let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + let package_id: PackageId = "android/app/org.fdroid.fdroid".parse().unwrap(); let mut covered = HashMap::new(); covered.insert(package_id, RepositoryId::new("official").unwrap()); let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { @@ -478,41 +533,12 @@ mod tests { } #[test] - fn manifest_records_generated_package_hashes() { - let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { - package_name: "org.fdroid.fdroid".to_owned(), - label: Some("F-Droid".to_owned()), - version_name: None, - version_code: None, - }]); - let plan = plan_installed_autogen(&inventory, &HashMap::new()).unwrap(); - - let manifest = AutogenManifest::from_candidates(&plan.candidates); - - assert_eq!(manifest.version, AUTOGEN_MANIFEST_VERSION); - assert_eq!( - manifest.repository_id.as_str(), - DEFAULT_AUTOGEN_REPOSITORY_ID - ); - assert_eq!(manifest.packages.len(), 1); - assert_eq!( - manifest.packages[0].content_hash, - content_hash(&plan.candidates[0].content) - ); - } - - #[cfg(feature = "lua")] - #[test] - fn generated_lua_evaluates_through_package_boundary() { + fn generated_files_form_a_package_directory_boundary() { let temp = tempfile::tempdir().unwrap(); let repo = temp.path(); - fs::write(repo.join("repo.toml"), default_autogen_repo_toml()).unwrap(); - fs::create_dir_all(repo.join("packages/android")).unwrap(); - fs::create_dir(repo.join("lib")).unwrap(); - fs::create_dir(repo.join("templates")).unwrap(); let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { package_name: "org.fdroid.fdroid".to_owned(), - label: Some("F-Droid \"client\"".to_owned()), + label: Some("F-Droid".to_owned()), version_name: None, version_code: None, }]); @@ -520,27 +546,29 @@ mod tests { .unwrap() .candidates .remove(0); - let package_path = repo.join(&candidate.relative_path); - fs::write(&package_path, candidate.content).unwrap(); + let package_dir = repo.join(&candidate.relative_path); + fs::create_dir_all(&package_dir).unwrap(); + for file in &candidate.files { + fs::write(package_dir.join(&file.relative_path), &file.content).unwrap(); + } + fs::write( + package_dir.join(AUTOGEN_RECORD_FILE), + render_autogen_record(&candidate.record).unwrap(), + ) + .unwrap(); - let layout = RepositoryLayout::load(repo).unwrap(); - let package = evaluate_package_file(&layout, &package_path).unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(repo).unwrap(); - assert_eq!(package.id.to_string(), "android/org.fdroid.fdroid"); - assert_eq!(package.name, "F-Droid \"client\""); + assert_eq!(layout.packages.len(), 1); assert_eq!( - package.installed, - vec![InstalledTarget::AndroidPackage { - package_name: "org.fdroid.fdroid".to_owned(), - }] + layout.packages[0].id.to_string(), + "android/app/org.fdroid.fdroid" ); + assert_eq!(layout.packages[0].version_scripts[0].version, "9999"); } #[test] - fn generated_file_marker_identifies_managed_files_only() { - assert!(generated_file_is_managed(&format!( - "-- {GENERATED_MARKER}\nreturn {{}}" - ))); - assert!(!generated_file_is_managed("return {}")); + fn content_hash_uses_sha512_prefix() { + assert!(content_hash("hello").starts_with("sha512:")); } } diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index c81da6b..26863cb 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -8,6 +8,7 @@ default = [] lua = ["getter-core/lua"] [dependencies] +json_comments = "0.2" getter-core = { path = "../getter-core", default-features = false } getter-providers = { path = "../getter-providers" } getter-storage = { path = "../getter-storage" } diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs index faa06a9..0c0b5f0 100644 --- a/crates/getter-operations/src/autogen.rs +++ b/crates/getter-operations/src/autogen.rs @@ -2,28 +2,28 @@ //! //! The CLI and native bridge both call this module so there is one implementation //! of installed-autogen preview/apply semantics. Platform layers provide installed -//! inventory facts; this module decides generated package ids, repository -//! coverage, file writes, manifest updates, preservation behavior, and tracked -//! state updates. +//! inventory facts; this module decides generated package directories, +//! repository coverage, package-local `.autogen.jsonc` ownership, file writes, +//! cleanup, and tracked state updates. use getter_core::autogen::{ - content_hash, local_repo_toml, plan_installed_autogen, AutogenManifest, AutogenManifestEntry, - AutogenPlan, AutogenSkipReason, InstalledInventory, DEFAULT_AUTOGEN_REPOSITORY_ID, - DEFAULT_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, LOCAL_REPOSITORY_NAME, + content_hash, content_hash_bytes, plan_installed_autogen, record_file_key, + render_autogen_record, AutogenCandidate, AutogenPlan, AutogenRecord, AutogenSkipReason, + GeneratedPackageFile, InstalledInventory, AUTOGEN_RECORD_FILE, AUTOGEN_RECORD_VERSION, + DEFAULT_AUTOGEN_REPOSITORY_ID, DEFAULT_AUTOGEN_REPOSITORY_NAME, INSTALLED_AUTOGEN_GENERATOR, }; use getter_core::repository::{ generated_repository_target, GeneratedRepositoryTarget, GetterDataDirLayout, RepositoryLayout, - RepositoryLoadError, RepositoryMetadata, RepositoryRootConfig, REPO_API_VERSION_V1, + RepositoryLoadError, RepositoryMetadata, RepositoryPackageDirectoryLayout, + RepositoryRootConfig, REPO_API_VERSION_V1, }; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; -use getter_storage::{MainDb, StorageError, StoredRepository}; +use getter_storage::{MainDb, StorageError}; use serde_json::{json, Value}; -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::fs; use std::path::{Component, Path, PathBuf}; -pub const AUTOGEN_MANIFEST_FILE: &str = "autogen-manifest.json"; - #[derive(Debug, Clone, PartialEq, Eq)] pub enum AutogenAcceptance { AcceptAll, @@ -79,7 +79,11 @@ pub fn installed_preview_json( plan: &AutogenPlan, ) -> AutogenOperationResult { let (target_alias, target_path, _target_priority) = generated_repository_config(data_dir)?; - let candidates: Vec = plan.candidates.iter().map(autogen_candidate_json).collect(); + let candidates: Vec = plan + .candidates + .iter() + .map(autogen_candidate_json) + .collect::>()?; let skipped: Vec = plan.skipped.iter().map(autogen_skip_json).collect(); Ok(json!({ "operation": "installed.preview", @@ -103,17 +107,14 @@ pub fn cleanup_preview_json( inventory: &InstalledInventory, ) -> AutogenOperationResult { let (target_alias, repo_path, _target_priority) = generated_repository_config(data_dir)?; - let Some(manifest) = read_autogen_manifest(&repo_path)? else { - return Ok(json!({ - "operation": "cleanup.preview", - "target_repo_id": target_alias.as_str(), - "target_repo_path": repo_path, - "summary": { "candidate_count": 0, "skipped_count": 0, "write_count": 0, "delete_count": 0 }, - "candidates": [], - "skipped": [], - "diagnostics": [], - })); - }; + if !repo_path.is_dir() { + return Ok(cleanup_preview_response( + target_alias, + repo_path, + Vec::new(), + Vec::new(), + )); + } let plan = build_installed_autogen_plan(data_dir, db, inventory)?; let installed_ids: BTreeSet = plan .candidates @@ -121,14 +122,32 @@ pub fn cleanup_preview_json( .map(|candidate| candidate.package_id.to_string()) .chain(plan.skipped.iter().map(|skip| skip.package_id.to_string())) .collect(); + + let layout = RepositoryPackageDirectoryLayout::load(&repo_path)?; let mut candidates = Vec::new(); - for entry in manifest.packages { - if !installed_ids.contains(&entry.package_id.to_string()) { + let mut diagnostics = Vec::new(); + for package in layout.packages { + let relative_path = relative_package_path(&repo_path, &package.path)?; + let ownership = match read_owned_record(&package.path, &package.id, &relative_path) { + Ok(ownership) => ownership, + Err(error) => { + diagnostics.push(autogen_diagnostic( + "autogen.ownership_conflict", + format!( + "generated package '{}' has invalid ownership: {error}", + package.id + ), + Some(package.id.to_string()), + )); + continue; + } + }; + if !installed_ids.contains(&package.id.to_string()) { candidates.push(json!({ - "package_id": entry.package_id.to_string(), + "package_id": package.id.to_string(), "action": "delete", - "output_relative_path": entry.relative_path, - "content_hash": entry.content_hash, + "output_relative_path": relative_path, + "content_hash": ownership.content_hash, "reason": "not_in_installed_inventory", })); } @@ -140,20 +159,12 @@ pub fn cleanup_preview_json( .unwrap_or_default() .to_owned() }); - Ok(json!({ - "operation": "cleanup.preview", - "target_repo_id": target_alias.as_str(), - "target_repo_path": repo_path, - "summary": { - "candidate_count": candidates.len(), - "skipped_count": 0, - "write_count": 0, - "delete_count": candidates.len(), - }, - "candidates": candidates, - "skipped": [], - "diagnostics": [], - })) + Ok(cleanup_preview_response( + target_alias, + repo_path, + candidates, + diagnostics, + )) } pub fn apply_installed_preview( @@ -171,74 +182,25 @@ pub fn apply_installed_preview( } ensure_generated_repository(&repo_path, db, &target_alias, target_priority)?; let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = - read_autogen_manifest(&repo_path)?.unwrap_or_else(|| empty_autogen_manifest(&target_alias)); let mut applied = Vec::new(); - let mut preserved = Vec::new(); for candidate in accepted { let package_id = preview_package_id(candidate)?; let relative_path = preview_relative_path(candidate)?; - let content = candidate - .get("content") - .and_then(Value::as_str) - .ok_or_else(|| { - AutogenOperationError::Autogen("preview candidate missing content".to_owned()) - })?; - let expected_hash = candidate - .get("content_hash") - .and_then(Value::as_str) - .ok_or_else(|| { - AutogenOperationError::Autogen("preview candidate missing content_hash".to_owned()) - })?; - if content_hash(content) != expected_hash { - return Err(AutogenOperationError::Autogen(format!( - "preview content hash mismatch for {package_id}" - ))); - } - let target = safe_join(&repo_path, &relative_path)?; - if target.exists() { - let current = fs::read_to_string(&target).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to read existing autogen file '{}': {source}", - target.display() - )) - })?; - let current_hash = content_hash(¤t); - let known_hash = manifest - .package(&package_id) - .map(|entry| entry.content_hash.as_str()); - if known_hash != Some(current_hash.as_str()) { - preserved.push(preserve_autogen_file_in_local( - data_dir, - db, - &package_id, - ¤t, - )?); - } - } - if let Some(parent) = target.parent() { - fs::create_dir_all(parent).map_err(|source| { + let payload = preview_candidate_payload(candidate, &package_id, &relative_path)?; + let target_dir = safe_join(&repo_path, &relative_path)?; + if target_dir.exists() { + read_owned_record(&target_dir, &package_id, &relative_path)?; + clear_directory_contents(&target_dir)?; + } else { + fs::create_dir_all(&target_dir).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to create autogen package directory '{}': {source}", - parent.display() + "failed to create generated package directory '{}': {source}", + target_dir.display() )) })?; } - fs::write(&target, content).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to write autogen package '{}': {source}", - target.display() - )) - })?; - upsert_manifest_entry( - &mut manifest, - AutogenManifestEntry { - package_id: package_id.clone(), - relative_path: relative_path.clone(), - content_hash: expected_hash.to_owned(), - }, - ); + write_generated_package(&target_dir, &payload.files, &payload.record_content)?; db.upsert_generated_tracked_package_preserving_user_state(&package_id, &target_alias)?; applied.push(json!({ "package_id": package_id.to_string(), @@ -246,13 +208,11 @@ pub fn apply_installed_preview( })); } - write_autogen_manifest(&repo_path, &manifest)?; Ok(json!({ "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "applied_count": applied.len(), "applied": applied, - "preserved_to_local": preserved, })) } @@ -270,10 +230,7 @@ pub fn apply_cleanup_preview( ))); } let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = - read_autogen_manifest(&repo_path)?.unwrap_or_else(|| empty_autogen_manifest(&target_alias)); let mut deleted = Vec::new(); - let mut preserved = Vec::new(); for candidate in accepted { let package_id = preview_package_id(candidate)?; let relative_path = preview_relative_path(candidate)?; @@ -285,57 +242,25 @@ pub fn apply_cleanup_preview( "cleanup preview candidate missing content_hash".to_owned(), ) })?; - let manifest_entry = manifest.package(&package_id).ok_or_else(|| { - AutogenOperationError::Autogen(format!( - "cleanup preview candidate {package_id} is not managed by autogen manifest" - )) - })?; - if manifest_entry.relative_path != relative_path - || manifest_entry.content_hash != expected_hash - { + let target_dir = safe_join(&repo_path, &relative_path)?; + let ownership = read_owned_record(&target_dir, &package_id, &relative_path)?; + if ownership.content_hash != expected_hash { return Err(AutogenOperationError::Autogen(format!( - "cleanup preview candidate {package_id} does not match autogen manifest" + "cleanup preview candidate {package_id} does not match current autogen record" ))); } - let target = safe_join(&repo_path, &relative_path)?; - if target.exists() { - let current = fs::read_to_string(&target).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to read existing autogen file '{}': {source}", - target.display() - )) - })?; - if content_hash(¤t) != expected_hash { - preserved.push(preserve_autogen_file_in_local( - data_dir, - db, - &package_id, - ¤t, - )?); - } - fs::remove_file(&target).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to delete autogen package '{}': {source}", - target.display() - )) - })?; - } - manifest - .packages - .retain(|entry| entry.package_id != package_id); + clear_directory_contents(&target_dir)?; db.delete_generated_tracked_package(&package_id, &target_alias)?; deleted.push(json!({ "package_id": package_id.to_string(), "output_relative_path": relative_path, })); } - write_autogen_manifest(&repo_path, &manifest)?; Ok(json!({ "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "deleted_count": deleted.len(), "deleted": deleted, - "preserved_to_local": preserved, })) } @@ -391,21 +316,48 @@ fn higher_priority_package_coverage( let Some(path) = repo.path.as_ref() else { continue; }; - let layout = load_repository_layout(Path::new(path))?; - for package in layout.packages { - covered.entry(package.id).or_insert_with(|| repo.id.clone()); + for package_id in load_repository_package_ids(Path::new(path))? { + covered.entry(package_id).or_insert_with(|| repo.id.clone()); } } Ok(covered) } -fn load_repository_layout(path: &Path) -> AutogenOperationResult { - RepositoryLayout::load(path) - .map_err(|source| AutogenOperationError::Repository(source.to_string())) +fn load_repository_package_ids(path: &Path) -> AutogenOperationResult> { + if path.join("repo.toml").is_file() { + let layout = RepositoryLayout::load(path) + .map_err(|source| AutogenOperationError::Repository(source.to_string()))?; + Ok(layout + .packages + .into_iter() + .map(|package| package.id) + .collect()) + } else { + let layout = RepositoryPackageDirectoryLayout::load(path) + .map_err(|source| AutogenOperationError::Repository(source.to_string()))?; + Ok(layout + .packages + .into_iter() + .map(|package| package.id) + .collect()) + } } -fn autogen_candidate_json(candidate: &getter_core::autogen::AutogenCandidate) -> Value { - json!({ +fn autogen_candidate_json(candidate: &AutogenCandidate) -> AutogenOperationResult { + let record_content = render_autogen_record(&candidate.record) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + let files: Vec = candidate + .files + .iter() + .map(|file| { + json!({ + "relative_path": file.relative_path, + "content_hash": file.content_hash, + "content": file.content, + }) + }) + .collect(); + Ok(json!({ "package_id": candidate.package_id.to_string(), "kind": candidate.package_id.kind().as_str(), "display_name": candidate.name, @@ -413,8 +365,10 @@ fn autogen_candidate_json(candidate: &getter_core::autogen::AutogenCandidate) -> "action": "create", "output_relative_path": candidate.relative_path, "content_hash": candidate.content_hash, - "content": candidate.content, - }) + "content": record_content, + "autogen_record_content": record_content, + "files": files, + })) } fn autogen_skip_json(skip: &getter_core::autogen::AutogenSkip) -> Value { @@ -428,6 +382,28 @@ fn autogen_skip_json(skip: &getter_core::autogen::AutogenSkip) -> Value { }) } +fn cleanup_preview_response( + target_alias: RepositoryId, + repo_path: PathBuf, + candidates: Vec, + diagnostics: Vec, +) -> Value { + json!({ + "operation": "cleanup.preview", + "target_repo_id": target_alias.as_str(), + "target_repo_path": repo_path, + "summary": { + "candidate_count": candidates.len(), + "skipped_count": 0, + "write_count": 0, + "delete_count": candidates.len(), + }, + "candidates": candidates, + "skipped": [], + "diagnostics": diagnostics, + }) +} + fn accepted_preview_candidates<'a>( preview: &'a Value, acceptance: &AutogenAcceptance, @@ -480,13 +456,111 @@ fn preview_relative_path(candidate: &Value) -> AutogenOperationResult { }) } +struct PreviewCandidatePayload { + record_content: String, + files: Vec, +} + +fn preview_candidate_payload( + candidate: &Value, + package_id: &PackageId, + relative_path: &Path, +) -> AutogenOperationResult { + let record_content = candidate + .get("autogen_record_content") + .or_else(|| candidate.get("content")) + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "preview candidate missing autogen_record_content".to_owned(), + ) + })? + .to_owned(); + let expected_hash = candidate + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen("preview candidate missing content_hash".to_owned()) + })?; + if content_hash(&record_content) != expected_hash { + return Err(AutogenOperationError::Autogen(format!( + "preview autogen record hash mismatch for {package_id}" + ))); + } + let files = preview_generated_files(candidate)?; + let record = parse_record_content(&record_content)?; + validate_record(&record, package_id, relative_path, &files)?; + Ok(PreviewCandidatePayload { + record_content, + files, + }) +} + +fn preview_generated_files(candidate: &Value) -> AutogenOperationResult> { + let files = candidate + .get("files") + .and_then(Value::as_array) + .ok_or_else(|| { + AutogenOperationError::Autogen("preview candidate missing files".to_owned()) + })?; + files + .iter() + .map(|file| { + let relative_path = file + .get("relative_path") + .and_then(Value::as_str) + .map(PathBuf::from) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "preview generated file missing relative_path".to_owned(), + ) + })?; + validate_relative_path(&relative_path)?; + let content = file + .get("content") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "preview generated file missing content".to_owned(), + ) + })? + .to_owned(); + let expected_hash = file + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "preview generated file missing content_hash".to_owned(), + ) + })?; + let actual_hash = content_hash(&content); + if actual_hash != expected_hash { + return Err(AutogenOperationError::Autogen(format!( + "preview generated file '{}' hash mismatch", + relative_path.display() + ))); + } + Ok(GeneratedPackageFile { + relative_path, + content, + content_hash: actual_hash, + }) + }) + .collect() +} + fn ensure_generated_repository( repo_path: &Path, db: &MainDb, alias: &RepositoryId, priority: RepositoryPriority, ) -> AutogenOperationResult<()> { - ensure_repository_layout(repo_path, &generated_repo_toml(alias, priority))?; + fs::create_dir_all(repo_path).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create generated repository '{}': {source}", + repo_path.display() + )) + })?; db.upsert_repository( &RepositoryMetadata { id: alias.clone(), @@ -500,15 +574,6 @@ fn ensure_generated_repository( Ok(()) } -fn generated_repo_toml(alias: &RepositoryId, priority: RepositoryPriority) -> String { - format!( - "id = \"{}\"\nname = \"{}\"\npriority = {}\napi_version = \"{REPO_API_VERSION_V1}\"\n", - alias.as_str(), - generated_repository_name(alias), - priority.value() - ) -} - fn generated_repository_name(alias: &RepositoryId) -> String { if alias.as_str() == DEFAULT_AUTOGEN_REPOSITORY_ID { DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned() @@ -517,177 +582,248 @@ fn generated_repository_name(alias: &RepositoryId) -> String { } } -fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> AutogenOperationResult { - let local_id = RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id"); - if let Ok(existing) = find_repository(db, &local_id) { - let repo_path = repo_path(&existing)?; - ensure_repository_layout(&repo_path, &local_repo_toml())?; - return Ok(repo_path); - } - - let repo_path = GetterDataDirLayout::new(data_dir) - .repository_path(&RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id")); - ensure_repository_layout(&repo_path, &local_repo_toml())?; - db.upsert_repository( - &RepositoryMetadata { - id: local_id, - name: LOCAL_REPOSITORY_NAME.to_owned(), - priority: RepositoryPriority::LOCAL, - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_path), - None, - )?; - Ok(repo_path) -} - -fn find_repository(db: &MainDb, id: &RepositoryId) -> AutogenOperationResult { - db.repositories()? - .into_iter() - .find(|repo| &repo.id == id) - .ok_or_else(|| { - AutogenOperationError::Repository(format!("repository '{id}' is not registered")) - }) -} - -fn repo_path(repo: &StoredRepository) -> AutogenOperationResult { - repo.path.as_ref().map(PathBuf::from).ok_or_else(|| { - AutogenOperationError::Repository(format!("repository '{}' has no path", repo.id)) - }) +struct LoadedAutogenRecord { + content_hash: String, } -fn ensure_repository_layout(repo_path: &Path, repo_toml: &str) -> AutogenOperationResult<()> { - fs::create_dir_all(repo_path.join("packages")).map_err(|source| { +fn read_owned_record( + package_dir: &Path, + package_id: &PackageId, + relative_path: &Path, +) -> AutogenOperationResult { + let record_path = package_dir.join(AUTOGEN_RECORD_FILE); + if !record_path.is_file() { + return Err(AutogenOperationError::Autogen(format!( + "generated package '{}' is missing {AUTOGEN_RECORD_FILE}", + package_dir.display() + ))); + } + let bytes = fs::read(&record_path).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to create repository packages dir '{}': {source}", - repo_path.display() + "failed to read autogen record '{}': {source}", + record_path.display() )) })?; - fs::create_dir_all(repo_path.join("lib")).map_err(|source| { + let record: AutogenRecord = serde_json::from_reader(json_comments::StripComments::new( + bytes.as_slice(), + )) + .map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to create repository lib dir '{}': {source}", - repo_path.display() + "failed to parse autogen record '{}': {source}", + record_path.display() )) })?; - fs::create_dir_all(repo_path.join("templates")).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to create repository templates dir '{}': {source}", - repo_path.display() - )) - })?; - let repo_toml_path = repo_path.join("repo.toml"); - if !repo_toml_path.exists() { - fs::write(&repo_toml_path, repo_toml).map_err(|source| { + let files = read_recorded_files(package_dir, &record)?; + validate_record(&record, package_id, relative_path, &files)?; + Ok(LoadedAutogenRecord { + content_hash: content_hash_bytes(&bytes), + }) +} + +fn read_recorded_files( + package_dir: &Path, + record: &AutogenRecord, +) -> AutogenOperationResult> { + let mut files = Vec::new(); + for (relative, expected_hash) in &record.files { + let relative_path = PathBuf::from(relative); + validate_relative_path(&relative_path)?; + let path = package_dir.join(&relative_path); + if !path.is_file() { + return Err(AutogenOperationError::Autogen(format!( + "generated file '{}' listed in {AUTOGEN_RECORD_FILE} is missing", + path.display() + ))); + } + let bytes = fs::read(&path).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to write repo.toml '{}': {source}", - repo_toml_path.display() + "failed to read generated file '{}': {source}", + path.display() )) })?; + let actual_hash = content_hash_bytes(&bytes); + if &actual_hash != expected_hash { + return Err(AutogenOperationError::Autogen(format!( + "generated file '{}' hash does not match {AUTOGEN_RECORD_FILE}", + path.display() + ))); + } + let content = String::from_utf8(bytes).map_err(|source| { + AutogenOperationError::Autogen(format!( + "generated file '{}' is not UTF-8: {source}", + path.display() + )) + })?; + files.push(GeneratedPackageFile { + relative_path, + content, + content_hash: actual_hash, + }); } - Ok(()) + Ok(files) } -fn preserve_autogen_file_in_local( - data_dir: &Path, - db: &MainDb, +fn parse_record_content(content: &str) -> AutogenOperationResult { + serde_json::from_str(content).map_err(|source| { + AutogenOperationError::Autogen(format!("failed to parse preview autogen record: {source}")) + }) +} + +fn validate_record( + record: &AutogenRecord, package_id: &PackageId, - content: &str, -) -> AutogenOperationResult { - let local_repo = ensure_local_repository(data_dir, db)?; - let primary_relative = getter_core::autogen::package_relative_path(package_id); - let primary_target = safe_join(&local_repo, &primary_relative)?; - let relative_path = if primary_target.exists() { - let backup = PathBuf::from("autogen-preserved") - .join(package_id.kind().as_str()) - .join(format!( - "{}.{}.lua", - package_id.name(), - content_hash(content).replace(':', "-") - )); - safe_join(&local_repo, &backup)?; - backup - } else { - primary_relative - }; - let target = safe_join(&local_repo, &relative_path)?; - if let Some(parent) = target.parent() { - fs::create_dir_all(parent).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to create local preservation directory '{}': {source}", - parent.display() - )) - })?; + relative_path: &Path, + files: &[GeneratedPackageFile], +) -> AutogenOperationResult<()> { + if record.version != AUTOGEN_RECORD_VERSION { + return Err(AutogenOperationError::Autogen(format!( + "unsupported autogen record version {}; expected {AUTOGEN_RECORD_VERSION}", + record.version + ))); } - fs::write(&target, content).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to preserve modified autogen file '{}': {source}", - target.display() - )) - })?; - Ok(json!({ - "package_id": package_id.to_string(), - "repository_id": LOCAL_REPOSITORY_ID, - "relative_path": relative_path, - })) + if record.generator != INSTALLED_AUTOGEN_GENERATOR { + return Err(AutogenOperationError::Autogen(format!( + "autogen record generator '{}' does not match '{INSTALLED_AUTOGEN_GENERATOR}'", + record.generator + ))); + } + if &record.package_id != package_id { + return Err(AutogenOperationError::Autogen(format!( + "autogen record package '{}' does not match '{package_id}'", + record.package_id + ))); + } + if record.output_relative_path != relative_path { + return Err(AutogenOperationError::Autogen(format!( + "autogen record output path '{}' does not match '{}'", + record.output_relative_path.display(), + relative_path.display() + ))); + } + let file_hashes: BTreeMap = files + .iter() + .map(|file| { + ( + record_file_key(&file.relative_path), + file.content_hash.clone(), + ) + }) + .collect(); + if record.files.contains_key(AUTOGEN_RECORD_FILE) { + return Err(AutogenOperationError::Autogen(format!( + "autogen record must not list {AUTOGEN_RECORD_FILE} in files" + ))); + } + if record.files != file_hashes { + return Err(AutogenOperationError::Autogen( + "autogen record files do not match generated files".to_owned(), + )); + } + Ok(()) } -fn read_autogen_manifest(repo_path: &Path) -> AutogenOperationResult> { - let path = repo_path.join(AUTOGEN_MANIFEST_FILE); - if !path.exists() { - return Ok(None); +fn write_generated_package( + package_dir: &Path, + files: &[GeneratedPackageFile], + record_content: &str, +) -> AutogenOperationResult<()> { + for file in files { + let target = safe_join(package_dir, &file.relative_path)?; + if let Some(parent) = target.parent() { + fs::create_dir_all(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create generated package directory '{}': {source}", + parent.display() + )) + })?; + } + fs::write(&target, &file.content).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to write generated package file '{}': {source}", + target.display() + )) + })?; } - let bytes = fs::read(&path).map_err(|source| { + fs::write(package_dir.join(AUTOGEN_RECORD_FILE), record_content).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to read autogen manifest '{}': {source}", - path.display() + "failed to write autogen record '{}': {source}", + package_dir.join(AUTOGEN_RECORD_FILE).display() )) - })?; - serde_json::from_slice(&bytes).map(Some).map_err(|source| { - AutogenOperationError::Autogen(format!("failed to parse autogen manifest: {source}")) }) } -fn write_autogen_manifest( - repo_path: &Path, - manifest: &AutogenManifest, -) -> AutogenOperationResult<()> { - fs::create_dir_all(repo_path).map_err(|source| { +fn clear_directory_contents(path: &Path) -> AutogenOperationResult<()> { + fs::create_dir_all(path).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to create autogen repository '{}': {source}", - repo_path.display() + "failed to create generated package directory '{}': {source}", + path.display() )) })?; - let path = repo_path.join(AUTOGEN_MANIFEST_FILE); - let bytes = serde_json::to_vec_pretty(manifest).map_err(|source| { - AutogenOperationError::Autogen(format!("failed to serialize manifest: {source}")) - })?; - fs::write(&path, bytes).map_err(|source| { + for entry in fs::read_dir(path).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to write autogen manifest '{}': {source}", + "failed to read generated package directory '{}': {source}", path.display() )) - }) + })? { + let entry = entry.map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to read generated package directory '{}': {source}", + path.display() + )) + })?; + let child = entry.path(); + let file_type = entry.file_type().map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to inspect generated package entry '{}': {source}", + child.display() + )) + })?; + if file_type.is_dir() { + fs::remove_dir_all(&child).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to delete generated package directory '{}': {source}", + child.display() + )) + })?; + } else { + fs::remove_file(&child).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to delete generated package file '{}': {source}", + child.display() + )) + })?; + } + } + Ok(()) } -fn empty_autogen_manifest(repository_id: &RepositoryId) -> AutogenManifest { - AutogenManifest { - version: getter_core::autogen::AUTOGEN_MANIFEST_VERSION, - repository_id: repository_id.clone(), - packages: Vec::new(), - } +fn relative_package_path(root: &Path, package_dir: &Path) -> AutogenOperationResult { + package_dir + .strip_prefix(root) + .map(Path::to_path_buf) + .map_err(|_| { + AutogenOperationError::Autogen(format!( + "package directory '{}' is not under generated repository '{}'", + package_dir.display(), + root.display() + )) + }) } -fn upsert_manifest_entry(manifest: &mut AutogenManifest, entry: AutogenManifestEntry) { - manifest - .packages - .retain(|existing| existing.package_id != entry.package_id); - manifest.packages.push(entry); - manifest - .packages - .sort_by_key(|existing| existing.package_id.to_string()); +fn autogen_diagnostic(code: &str, message: String, package_id: Option) -> Value { + json!({ + "code": code, + "message": message, + "detail": package_id, + }) } fn safe_join(root: &Path, relative: &Path) -> AutogenOperationResult { + validate_relative_path(relative)?; + Ok(root.join(relative)) +} + +fn validate_relative_path(relative: &Path) -> AutogenOperationResult<()> { if relative.is_absolute() || relative.components().any(|component| { matches!( @@ -701,7 +837,7 @@ fn safe_join(root: &Path, relative: &Path) -> AutogenOperationResult { relative.display() ))); } - Ok(root.join(relative)) + Ok(()) } #[cfg(test)] @@ -734,6 +870,11 @@ mod tests { preview["target_repo_path"].as_str(), data_dir.join("repo/autogen").to_str() ); + assert_eq!( + preview["candidates"][0]["output_relative_path"], + "android/app/com.example.autogen" + ); + assert!(preview["candidates"][0]["files"].is_array()); assert!(!data_dir.join("repo/autogen").exists()); } @@ -750,15 +891,114 @@ mod tests { .unwrap(); assert_eq!(result["target_repo_id"], "autogen"); - assert!(data_dir.join("repo/autogen/repo.toml").is_file()); + assert!(data_dir.join("repo/autogen").is_dir()); + assert!(data_dir + .join("repo/autogen/android/app/com.example.autogen/metadata.jsonc") + .is_file()); assert!(data_dir - .join("repo/autogen/packages/android/com.example.autogen.lua") + .join("repo/autogen/android/app/com.example.autogen/9999.lua") .is_file()); + assert!(data_dir + .join("repo/autogen/android/app/com.example.autogen/.autogen.jsonc") + .is_file()); + assert!(!data_dir.join("repo/autogen/repo.toml").exists()); + assert!(!data_dir.join("repo/autogen/packages").exists()); let repos = db.repositories().unwrap(); assert_eq!(repos[0].id.as_str(), "autogen"); assert_eq!(repos[0].priority.value(), -1); } + #[test] + fn apply_rejects_existing_package_directory_without_ownership_record() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + fs::create_dir_all(data_dir.join("repo/autogen/android/app/com.example.autogen")).unwrap(); + fs::write( + data_dir.join("repo/autogen/android/app/com.example.autogen/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + + let error = apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("missing .autogen.jsonc")) + ); + } + + #[test] + fn apply_rejects_modified_generated_files_instead_of_preserving_to_local() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll).unwrap(); + fs::write( + data_dir.join("repo/autogen/android/app/com.example.autogen/9999.lua"), + "#!/bin/upa-lua v1\n-- user edited\nreturn {}\n", + ) + .unwrap(); + + let error = apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("hash does not match")) + ); + assert!(!data_dir.join("repo/local").exists()); + } + + #[test] + fn cleanup_clears_generated_package_directory_but_keeps_directory() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll).unwrap(); + let empty_inventory = InstalledInventory::new(Vec::new()); + let cleanup = cleanup_preview_json(data_dir, &db, &empty_inventory).unwrap(); + + assert_eq!( + cleanup["candidates"][0]["package_id"], + "android/app/com.example.autogen" + ); + apply_cleanup_preview(data_dir, &db, &cleanup, &AutogenAcceptance::AcceptAll).unwrap(); + + let package_dir = data_dir.join("repo/autogen/android/app/com.example.autogen"); + assert!(package_dir.is_dir()); + assert_eq!(fs::read_dir(package_dir).unwrap().count(), 0); + } + + #[test] + fn cleanup_rejects_stale_preview_when_autogen_record_changes() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll).unwrap(); + let empty_inventory = InstalledInventory::new(Vec::new()); + let cleanup = cleanup_preview_json(data_dir, &db, &empty_inventory).unwrap(); + fs::write( + data_dir.join("repo/autogen/android/app/com.example.autogen/.autogen.jsonc"), + r#"{ "version": 1, "generator": "installed-inventory", "package_id": "android/app/com.example.autogen", "output_relative_path": "android/app/com.example.autogen", "input": { "kind": "installed_android_package", "package_name": "com.example.autogen" }, "files": {} }"#, + ) + .unwrap(); + + let error = apply_cleanup_preview(data_dir, &db, &cleanup, &AutogenAcceptance::AcceptAll) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("does not match current autogen record") || detail.contains("files do not match")) + ); + } + #[test] fn custom_generated_repository_must_already_exist() { let temp = tempfile::tempdir().unwrap(); From 367fa9bc9c3afff4eaf834041cd38f6bc25f577b Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 01:49:07 +0800 Subject: [PATCH 31/72] feat(repository): evaluate package directories --- crates/getter-cli/src/lib.rs | 97 +++- crates/getter-cli/tests/bdd_cli.rs | 52 +- .../features/cli/autogen_installed.feature | 3 + .../cli/repository_package_eval.feature | 24 + crates/getter-core/src/diagnostics.rs | 123 ++++- crates/getter-core/src/lua.rs | 455 +++++++++++++++++- crates/getter-core/src/repository.rs | 206 ++++++-- crates/getter-operations/src/read_model.rs | 149 ++++-- crates/getter-operations/src/runtime.rs | 112 ++++- 9 files changed, 1105 insertions(+), 116 deletions(-) diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 9b03e76..0cb5e6f 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -6,7 +6,7 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; -use getter_core::lua::evaluate_package_file; +use getter_core::lua::{evaluate_package_directory_script, evaluate_package_file}; use getter_core::repository::{ default_repository_priority, GetterDataDirLayout, RepositoryLayout, RepositoryMetadata, RepositoryPackageDirectoryLayout, REPOSITORY_ROOT_METADATA_FILE, REPO_API_VERSION_V1, @@ -596,14 +596,10 @@ fn execute(invocation: CliInvocation) -> Result { CliCommand::RepoEval { id } => { let db = open_main_db(&invocation.data_dir)?; let repo = find_repository(&db, &id)?; - let path = repo_path(&repo)?; - let layout = load_repository_layout(&path)?; - let mut packages = Vec::new(); - for package_file in &layout.packages { - let package = evaluate_package_file(&layout, &package_file.path) - .map_err(|error| CliError::PackageEval(error.to_string()))?; - packages.push(package_json(package)?); - } + let packages = evaluate_repository_packages(&repo)? + .into_iter() + .map(package_json) + .collect::, _>>()?; Ok(json!({ "repository": repository_json(repo), "packages": packages, @@ -929,6 +925,13 @@ fn load_repository_layout(path: &Path) -> Result { RepositoryLayout::load(path).map_err(|source| CliError::Repository(source.to_string())) } +fn load_package_directory_layout( + path: &Path, +) -> Result { + RepositoryPackageDirectoryLayout::load(path) + .map_err(|source| CliError::Repository(source.to_string())) +} + fn load_repository_metadata( id: &RepositoryId, path: &Path, @@ -979,16 +982,12 @@ fn evaluate_package_from_repo( package_id: &PackageId, ) -> Result { let repo = find_repository(db, repo_id)?; - let path = repo_path(&repo)?; - let layout = load_repository_layout(&path)?; - let package_file = layout.package_file(package_id).ok_or_else(|| { + evaluate_package_in_repository(&repo, package_id)?.ok_or_else(|| { CliError::PackageEval(format!( "package '{}' was not found in repository '{}'", package_id, repo_id )) - })?; - evaluate_package_file(&layout, &package_file.path) - .map_err(|error| CliError::PackageEval(error.to_string())) + }) } fn evaluate_highest_priority_package( @@ -996,11 +995,8 @@ fn evaluate_highest_priority_package( package_id: &PackageId, ) -> Result { for repo in db.repositories()? { - let path = repo_path(&repo)?; - let layout = load_repository_layout(&path)?; - if let Some(package_file) = layout.package_file(package_id) { - return evaluate_package_file(&layout, &package_file.path) - .map_err(|error| CliError::PackageEval(error.to_string())); + if let Some(package) = evaluate_package_in_repository(&repo, package_id)? { + return Ok(package); } } Err(CliError::PackageEval(format!( @@ -1008,6 +1004,67 @@ fn evaluate_highest_priority_package( ))) } +fn evaluate_repository_packages( + repo: &StoredRepository, +) -> Result, CliError> { + let path = repo_path(repo)?; + if path.join("repo.toml").is_file() { + let layout = load_repository_layout(&path)?; + return layout + .packages + .iter() + .map(|package_file| { + evaluate_package_file(&layout, &package_file.path) + .map_err(|error| CliError::PackageEval(error.to_string())) + }) + .collect(); + } + + let layout = load_package_directory_layout(&path)?; + layout + .packages + .iter() + .map(|package| evaluate_package_directory(&repo.id, &layout, package)) + .collect() +} + +fn evaluate_package_in_repository( + repo: &StoredRepository, + package_id: &PackageId, +) -> Result, CliError> { + let path = repo_path(repo)?; + if path.join("repo.toml").is_file() { + let layout = load_repository_layout(&path)?; + let Some(package_file) = layout.package_file(package_id) else { + return Ok(None); + }; + return evaluate_package_file(&layout, &package_file.path) + .map(Some) + .map_err(|error| CliError::PackageEval(error.to_string())); + } + + let layout = load_package_directory_layout(&path)?; + let Some(package) = layout.package(package_id) else { + return Ok(None); + }; + evaluate_package_directory(&repo.id, &layout, package).map(Some) +} + +fn evaluate_package_directory( + repo_id: &RepositoryId, + layout: &RepositoryPackageDirectoryLayout, + package: &getter_core::repository::PackageDirectory, +) -> Result { + let metadata = layout + .package_metadata(package) + .map_err(|error| CliError::PackageEval(error.to_string()))?; + let script = layout + .unambiguous_version_script(package) + .map_err(|error| CliError::PackageEval(error.to_string()))?; + evaluate_package_directory_script(repo_id, package, &metadata, script) + .map_err(|error| CliError::PackageEval(error.to_string())) +} + fn read_installed_inventory(path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index e5e2787..35754d5 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -349,20 +349,45 @@ fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: Str #[given(expr = "a package-directory repository {string} with package {string}")] fn package_directory_repository(world: &mut CliWorld, repo_id: String, package_id: String) { + create_package_directory_repository(world, repo_id, package_id, 1); +} + +#[given(expr = "a package-directory repository {string} with multi-version package {string}")] +fn package_directory_repository_multi_version( + world: &mut CliWorld, + repo_id: String, + package_id: String, +) { + create_package_directory_repository(world, repo_id, package_id, 2); +} + +fn create_package_directory_repository( + world: &mut CliWorld, + repo_id: String, + package_id: String, + script_count: usize, +) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); let package_dir = repo_path.join(package_id.replace('/', std::path::MAIN_SEPARATOR_STR)); fs::create_dir_all(&package_dir).expect("create package dir"); fs::write( package_dir.join("metadata.jsonc"), - r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + r#"{ "type": "android:app", "display_name": "F-Droid", "android": { "package_name": "org.fdroid.fdroid" } }"#, ) .expect("write package metadata"); fs::write( package_dir.join("1.20.0.lua"), - "#!/bin/upa-lua v1\nreturn {}", + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", ) .expect("write version Lua"); + if script_count > 1 { + fs::write( + package_dir.join("9999.lua"), + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", + ) + .expect("write live version Lua"); + } world.fixture_repo_id = Some(repo_id); world.fixture_repo_path = Some(repo_path); @@ -905,6 +930,21 @@ fn command_fails_with_autogen_error(world: &mut CliWorld) { world.json = Some(json); } +#[then("the command fails with a package eval error")] +fn command_fails_with_package_eval_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(1)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["command"], "package eval"); + assert_eq!(json["error"]["code"], "package.eval_error"); + assert!(json["error"]["detail"] + .as_str() + .unwrap_or_default() + .contains("explicit version selection is not implemented")); + world.json = Some(json); +} + #[then("the command fails with an update check error")] fn command_fails_with_update_check_error(world: &mut CliWorld) { let output = world.output.as_ref().expect("command output exists"); @@ -1224,6 +1264,14 @@ fn output_contains_named_package(world: &mut CliWorld, package_id: String, packa assert_eq!(json["data"]["package"]["name"], package_name); } +#[then(expr = "the package eval name is {string}")] +fn package_eval_name_is(world: &mut CliWorld, package_name: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "package eval"); + assert_eq!(json["data"]["package"]["name"], package_name); +} + #[then(expr = "the pinned package version is {string}")] fn pinned_package_version_is(world: &mut CliWorld, version: String) { let json = current_json(world); diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 695dc78..6a0838d 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -21,6 +21,9 @@ Feature: Installed app autogen And the app list contains autogen tracked package "android/app/com.example.autogen" When I run getter repo validate for autogen Then the output reports a valid repository without network + When I run getter package eval for package "android/app/com.example.autogen" + Then the command succeeds + And the package eval name is "Example Autogen" Scenario: Higher-priority repositories suppress installed app autogen candidates Given an initialized getter data directory diff --git a/crates/getter-cli/tests/features/cli/repository_package_eval.feature b/crates/getter-cli/tests/features/cli/repository_package_eval.feature index 4edefc2..172e634 100644 --- a/crates/getter-cli/tests/features/cli/repository_package_eval.feature +++ b/crates/getter-cli/tests/features/cli/repository_package_eval.feature @@ -1,5 +1,29 @@ @getter-cli @repository Feature: Getter CLI repository and package evaluation + Scenario: User adds and evaluates a package-directory repository + Given an initialized getter data directory + And a package-directory repository "official" with package "android/app/org.fdroid.fdroid" + When I run getter repo add for that repository with priority 0 + Then the command succeeds + And the output is valid JSON + And the output contains the added repository + When I run getter repo eval for that repository + Then the command succeeds + And the output is valid JSON + And the output contains the evaluated fixture package + When I run getter package eval for that fixture package + Then the command succeeds + And the output is valid JSON + And the output contains the fixture package + + Scenario: Package-directory package eval requires an unambiguous version script + Given an initialized getter data directory + And a package-directory repository "official" with multi-version package "android/app/org.fdroid.fdroid" + When I run getter repo add for that repository with priority 0 + Then the command succeeds + When I run getter package eval for that fixture package + Then the command fails with a package eval error + Scenario: User adds and evaluates a fixture Lua repository Given an initialized getter data directory And a fixture Lua repository "official" with package "android/org.fdroid.fdroid" diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index 817b310..48f36a3 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -3,7 +3,7 @@ //! Diagnostics are getter-owned DTOs used by CLI and future app bridges. They //! describe what getter observed; Flutter should only render them. -use crate::lua::{evaluate_package_file, LuaPackageError}; +use crate::lua::{evaluate_package_directory_script, evaluate_package_file, LuaPackageError}; use crate::repository::{ package_cache_key, InvalidPackageDirectory, RepositoryLayout, RepositoryLoadError, RepositoryPackageDirectoryLayout, @@ -103,12 +103,42 @@ fn validate_package_directory_repository_path(root: &Path) -> RepositoryValidati return RepositoryValidationReport::new(0, vec![repository_load_diagnostic(error)]) } }; - let diagnostics = layout + let mut diagnostics: Vec<_> = layout .invalid_packages .iter() .map(invalid_package_directory_diagnostic) .collect(); - RepositoryValidationReport::new(layout.packages.len(), diagnostics) + let mut package_count = 0usize; + let repository_id = "validation".parse().expect("static repository id is valid"); + for package in &layout.packages { + let metadata = match layout.package_metadata(package) { + Ok(metadata) => metadata, + Err(error) => { + diagnostics.push(repository_load_diagnostic(error)); + continue; + } + }; + if package.version_scripts.is_empty() { + diagnostics.push(repository_load_diagnostic( + RepositoryLoadError::MissingPackageVersionScript { + package_id: package.id.clone(), + }, + )); + continue; + } + let diagnostic_count_before = diagnostics.len(); + for script in &package.version_scripts { + if let Err(error) = + evaluate_package_directory_script(&repository_id, package, &metadata, script) + { + diagnostics.push(lua_diagnostic(error, Some(package.id.clone()))); + } + } + if diagnostics.len() == diagnostic_count_before { + package_count += 1; + } + } + RepositoryValidationReport::new(package_count, diagnostics) } fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDiagnostic { @@ -185,6 +215,38 @@ fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDi path, format!("failed to hash package file: {source}"), ), + RepositoryLoadError::ReadPackageMetadata { path, source } => ( + "package.read_metadata", + path, + format!("failed to read package metadata: {source}"), + ), + RepositoryLoadError::ParsePackageMetadata { path, source } => ( + "package.parse_metadata", + path, + format!("failed to parse package metadata: {source}"), + ), + RepositoryLoadError::InvalidPackageMetadata { path, reason } => ( + "package.metadata", + path, + reason, + ), + RepositoryLoadError::MissingPackageVersionScript { package_id } => ( + "package.missing_version_script", + PathBuf::from(format!("{package_id}/metadata.jsonc")), + format!("package '{package_id}' has no enabled version scripts"), + ), + RepositoryLoadError::AmbiguousPackageVersionScript { package_id } => ( + "package.ambiguous_version_script", + PathBuf::from(format!("{package_id}/metadata.jsonc")), + format!( + "package '{package_id}' has multiple enabled version scripts; explicit version selection is not implemented" + ), + ), + RepositoryLoadError::MissingLuaApiShebang { path, expected } => ( + "package.lua_api_shebang", + path, + format!("Lua version script is missing required shebang '{expected}'"), + ), }; diagnostic(code, message, path, None, None) } @@ -245,6 +307,13 @@ fn lua_diagnostic( LuaPackageError::Domain { path, message } => { diagnostic("package.domain", message, path, None, fallback_package_id) } + LuaPackageError::Repository { path, source } => diagnostic( + "package.repository", + source.to_string(), + path, + None, + fallback_package_id, + ), } } @@ -325,7 +394,7 @@ api_version = "getter.repo.v1" .unwrap(); fs::write( package_dir.join("1.20.0.lua"), - "#!/bin/upa-lua v1\nreturn {}", + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", ) .unwrap(); @@ -337,6 +406,52 @@ api_version = "getter.repo.v1" assert!(!report.network_required); } + #[test] + fn package_directory_without_shebang_is_reported_as_invalid() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .unwrap(); + fs::write(package_dir.join("1.20.0.lua"), "return package_version {}").unwrap(); + + let report = validate_repository_path(temp.path()); + + assert!(!report.valid); + assert_eq!(report.diagnostics[0].code, "package.repository"); + assert!(report.diagnostics[0].message.contains("#!/bin/upa-lua v1")); + } + + #[test] + fn package_directory_validator_checks_each_version_script_without_selecting_one() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .unwrap(); + fs::write( + package_dir.join("1.20.0.lua"), + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + + assert!(report.valid, "{report:?}"); + assert_eq!(report.package_count, 1); + } + #[test] fn package_directory_metadata_error_is_stable_package_diagnostic() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index d461539..4bea4d6 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -1,8 +1,12 @@ //! Minimal Lua package-file evaluation and Rust validation boundary. -use crate::repository::RepositoryLayout; +use crate::repository::{ + PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageTypeMetadata, + PackageVersionScript, RepositoryLayout, RepositoryLoadError, LUA_API_SHEBANG_V1, + REPOSITORY_LUACLASS_DIR, +}; use crate::{ - InstalledTarget, PackageId, PackagePermissions, ResolvedPackage, UpdateArtifact, + InstalledTarget, PackageId, PackagePermissions, RepositoryId, ResolvedPackage, UpdateArtifact, UpdateCandidate, }; use mlua::{Lua, Table, Value}; @@ -36,6 +40,12 @@ pub enum LuaPackageError { Schema { path: PathBuf, message: String }, #[error("domain validation failed for {path}: {message}")] Domain { path: PathBuf, message: String }, + #[error("repository layout failed for {path}: {source}")] + Repository { + path: PathBuf, + #[source] + source: RepositoryLoadError, + }, } /// Evaluate and validate a Lua package file from a repository layout. @@ -59,8 +69,49 @@ pub fn evaluate_package_source( source: &str, ) -> Result { let path = path.as_ref().to_path_buf(); + let json = evaluate_package_source_to_json( + &LuaRepositoryEnvironment::Legacy(repository), + &path, + source, + )?; + validate_package_json(repository, &path, json) +} + +pub fn evaluate_package_directory_script( + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, +) -> Result { + let source = fs::read_to_string(&script.path).map_err(|source| LuaPackageError::ReadFile { + path: script.path.clone(), + source, + })?; + if source.lines().next() != Some(LUA_API_SHEBANG_V1) { + return Err(LuaPackageError::Repository { + path: script.path.clone(), + source: RepositoryLoadError::MissingLuaApiShebang { + path: script.path.clone(), + expected: LUA_API_SHEBANG_V1, + }, + }); + } + let json = evaluate_package_source_to_json( + &LuaRepositoryEnvironment::PackageDirectory { package }, + &script.path, + &source, + )?; + validate_package_directory_version_json(repository_id, package, metadata, script, json) +} + +fn evaluate_package_source_to_json( + environment: &LuaRepositoryEnvironment<'_>, + path: &Path, + source: &str, +) -> Result { + let path = path.to_path_buf(); let lua = Lua::new(); - configure_package_path(&lua, repository).map_err(|source| LuaPackageError::Runtime { + configure_package_path(&lua, environment).map_err(|source| LuaPackageError::Runtime { path: path.clone(), source, })?; @@ -85,26 +136,55 @@ pub fn evaluate_package_source( Value::Table(table) => table, _ => return Err(LuaPackageError::NotATable { path }), }; - let json = lua_table_to_json(&path, "$", table)?; - validate_package_json(repository, &path, json) + lua_table_to_json(&path, "$", table) +} + +enum LuaRepositoryEnvironment<'a> { + Legacy(&'a RepositoryLayout), + PackageDirectory { package: &'a PackageDirectory }, } -fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Result<()> { +fn configure_package_path( + lua: &Lua, + environment: &LuaRepositoryEnvironment<'_>, +) -> mlua::Result<()> { let package: Table = lua.globals().get("package")?; - let lib_pattern = repository.lib_dir.join("?.lua"); - let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); - let new_path = format!( - "{};{}", - lib_pattern.to_string_lossy(), - nested_lib_pattern.to_string_lossy() - ); - package.set("path", new_path)?; package.set("cpath", "")?; package.set("loadlib", Value::Nil)?; - install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone())?; + match environment { + LuaRepositoryEnvironment::Legacy(repository) => { + let lib_pattern = repository.lib_dir.join("?.lua"); + let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); + let new_path = format!( + "{};{}", + lib_pattern.to_string_lossy(), + nested_lib_pattern.to_string_lossy() + ); + package.set("path", new_path)?; + install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone())?; + } + LuaRepositoryEnvironment::PackageDirectory { + package: package_dir, + } => { + package.set("path", "")?; + install_luaclass_prefix_searcher( + lua, + &package, + package_directory_repository_root(package_dir).join(REPOSITORY_LUACLASS_DIR), + )?; + } + } disable_native_module_searchers(&package) } +fn package_directory_repository_root(package: &PackageDirectory) -> PathBuf { + let mut root = package.path.clone(); + for _ in package.id.to_string().split('/') { + root.pop(); + } + root +} + fn disable_native_module_searchers(package: &Table) -> mlua::Result<()> { let searchers = package_searchers(package)?; let len = searchers.raw_len(); @@ -128,24 +208,54 @@ fn remove_unsafe_globals(lua: &Lua) -> mlua::Result<()> { } fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> mlua::Result<()> { + install_prefixed_file_searcher( + lua, + package, + "lib.", + lib_dir, + "constrained repository lib searcher only handles lib.* modules", + ) +} + +fn install_luaclass_prefix_searcher( + lua: &Lua, + package: &Table, + luaclass_dir: PathBuf, +) -> mlua::Result<()> { + install_prefixed_file_searcher( + lua, + package, + "luaclass.", + luaclass_dir, + "constrained repository luaclass searcher only handles luaclass.* modules", + ) +} + +fn install_prefixed_file_searcher( + lua: &Lua, + package: &Table, + prefix: &'static str, + module_dir: PathBuf, + wrong_prefix_message: &'static str, +) -> mlua::Result<()> { let searchers = package_searchers(package)?; let searcher = lua.create_function(move |lua, module: String| { - let Some(module) = module.strip_prefix("lib.") else { + let Some(module) = module.strip_prefix(prefix) else { return lua - .create_string("\n\tconstrained repository lib searcher only handles lib.* modules") + .create_string(format!("\n\t{wrong_prefix_message}")) .map(Value::String); }; let Some(relative_module) = module_to_relative_path(module) else { return lua .create_string(format!( - "\n\tinvalid repository lib module name 'lib.{module}'" + "\n\tinvalid repository module name '{prefix}{module}'" )) .map(Value::String); }; - let module_path = lib_dir.join(&relative_module).with_extension("lua"); - let init_path = lib_dir.join(&relative_module).join("init.lua"); + let module_path = module_dir.join(&relative_module).with_extension("lua"); + let init_path = module_dir.join(&relative_module).join("init.lua"); for candidate in [&module_path, &init_path] { if candidate.is_file() { let source = fs::read_to_string(candidate).map_err(mlua::Error::external)?; @@ -157,8 +267,8 @@ fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> } lua.create_string(format!( - "\n\tno repository lib module 'lib.{module}' in {}", - lib_dir.display() + "\n\tno repository module '{prefix}{module}' in {}", + module_dir.display() )) .map(Value::String) })?; @@ -201,6 +311,7 @@ fn module_to_relative_path(module: &str) -> Option { fn install_helpers(lua: &Lua) -> mlua::Result<()> { let package_fn = lua.create_function(|_, table: Table| Ok(table))?; lua.globals().set("package_def", package_fn.clone())?; + lua.globals().set("package_version", package_fn.clone())?; lua.globals().set("android_app", package_fn.clone())?; lua.globals().set("magisk_module", package_fn.clone())?; lua.globals().set("generic_package", package_fn)?; @@ -337,15 +448,115 @@ fn validate_package_json( } let name = required_string(path, object, "name")?.to_owned(); + package_from_version_json( + repository.metadata.id.clone(), + id, + name, + PackagePermissions::default(), + path, + object, + ) +} + +fn validate_package_directory_version_json( + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + value: JsonValue, +) -> Result { + let object = value.as_object().ok_or_else(|| LuaPackageError::Schema { + path: script.path.clone(), + message: "package value must be an object".to_owned(), + })?; + if object.contains_key("id") { + return Err(LuaPackageError::Schema { + path: script.path.clone(), + message: "field 'id' must not be declared by package version scripts; package id is derived from path".to_owned(), + }); + } + let name = object + .get("name") + .and_then(JsonValue::as_str) + .map(str::to_owned) + .unwrap_or_else(|| metadata.display_name_for(&package.id)); + let mut package = package_from_version_json( + repository_id.clone(), + package.id.clone(), + name, + metadata_permissions_for_script(metadata, &script.file_name), + &script.path, + object, + )?; + apply_metadata_installed_target(&mut package, metadata, &script.path)?; + Ok(package) +} + +fn apply_metadata_installed_target( + package: &mut ResolvedPackage, + metadata: &PackageDirectoryMetadata, + path: &Path, +) -> Result<(), LuaPackageError> { + let metadata_target = metadata_installed_target(metadata); + if package.installed.is_empty() { + package.installed.push(metadata_target); + return Ok(()); + } + if package.installed == [metadata_target] { + return Ok(()); + } + Err(LuaPackageError::Domain { + path: path.to_path_buf(), + message: "field 'installed' must match package metadata identity".to_owned(), + }) +} + +fn metadata_installed_target(metadata: &PackageDirectoryMetadata) -> InstalledTarget { + match &metadata.package { + PackageTypeMetadata::AndroidApp { android } => InstalledTarget::AndroidPackage { + package_name: android.package_name.clone(), + }, + PackageTypeMetadata::MagiskModule { magisk } => InstalledTarget::MagiskModule { + module_id: magisk.module_id.clone(), + }, + PackageTypeMetadata::Generic { generic } => InstalledTarget::Generic { + id: generic.id.clone(), + }, + } +} + +fn metadata_permissions_for_script( + metadata: &PackageDirectoryMetadata, + file_name: &str, +) -> PackagePermissions { + PackagePermissions { + free_network: metadata + .permissions_for(file_name) + .iter() + .any(|permission| *permission == PackageLuaPermission::AllowFreeNetwork), + } +} + +fn package_from_version_json( + repository: RepositoryId, + id: PackageId, + name: String, + default_permissions: PackagePermissions, + path: &Path, + object: &Map, +) -> Result { let installed = parse_installed_targets(path, object.get("installed"))?; - let permissions = parse_permissions(path, object.get("permissions"))?; + let permissions = match object.get("permissions") { + Some(value) => parse_permissions(path, Some(value))?, + None => default_permissions, + }; let source_priority = parse_string_array(path, "source_priority", object.get("source_priority"))?; let updates = parse_update_candidates(path, object.get("updates"))?; Ok(ResolvedPackage { id, - repository: repository.metadata.id.clone(), + repository, name, installed, permissions, @@ -516,7 +727,8 @@ fn parse_string_array( #[cfg(test)] mod tests { use super::*; - use crate::repository::RepositoryLayout; + use crate::repository::{RepositoryLayout, RepositoryPackageDirectoryLayout}; + use crate::RepositoryId; use std::fs; fn fixture_repo() -> (tempfile::TempDir, RepositoryLayout, PathBuf) { @@ -596,6 +808,199 @@ return package_def { ); } + #[test] + fn evaluates_package_directory_version_script_from_metadata_identity() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" }, + "lua": { "9999.lua": { "permission": ["allow_free_network"] } } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { + installed = { + { kind = "android_package", package_name = "com.example.autogen" }, + }, + updates = { + { + version = "1.2.0", + artifacts = { + { name = "app.apk", url = "https://example.invalid/app.apk", file_name = "app.apk" }, + }, + }, + }, +} +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let package = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap(); + + assert_eq!(package.id.to_string(), "android/app/com.example.autogen"); + assert_eq!(package.repository.as_str(), "autogen"); + assert_eq!(package.name, "Example Autogen"); + assert!(package.permissions.free_network); + assert_eq!(package.installed.len(), 1); + assert_eq!(package.updates[0].version, "1.2.0"); + } + + #[test] + fn rejects_package_directory_installed_target_mismatch() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { + installed = { + { kind = "android_package", package_name = "com.other.app" }, + }, +} +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let err = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap_err(); + + assert!(matches!(err, LuaPackageError::Domain { .. })); + assert!(err.to_string().contains("metadata identity")); + } + + #[test] + fn rejects_package_directory_version_script_id_field() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { id = "android/app/com.example.autogen" } +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let err = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap_err(); + + assert!(matches!(err, LuaPackageError::Schema { .. })); + assert!(err.to_string().contains("must not be declared")); + } + + #[test] + fn package_directory_can_load_luaclass_modules() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::create_dir_all(temp.path().join("luaclass")).unwrap(); + fs::write( + temp.path().join("luaclass/android.lua"), + r#" +return { + package_version = function(input) + return { + installed = input.installed, + updates = input.updates, + } + end +} +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local android = require("luaclass.android") +return android.package_version { + installed = { + { kind = "android_package", package_name = "com.example.autogen" }, + }, +} +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let package = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap(); + + assert_eq!(package.name, "Example Autogen"); + assert_eq!(package.installed.len(), 1); + } + #[test] fn rejects_package_id_that_does_not_match_path() { let (_temp, layout, package_path) = fixture_repo(); diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index fd16ef5..f85c81a 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -2,6 +2,7 @@ use crate::{PackageId, PackageIdError, RepositoryId, RepositoryIdError, RepositoryPriority}; use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha512}; use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; @@ -16,7 +17,9 @@ pub const REPOSITORY_ROOT_METADATA_VERSION: u32 = 1; pub const REPOSITORY_SELF_METADATA_DIR: &str = ".metadata"; pub const REPOSITORY_LUACLASS_DIR: &str = "luaclass"; pub const PACKAGE_METADATA_FILE: &str = "metadata.jsonc"; +pub const PACKAGE_MANIFEST_FILE: &str = "Manifest"; pub const LUA_SCRIPT_EXTENSION: &str = "lua"; +pub const LUA_API_SHEBANG_V1: &str = "#!/bin/upa-lua v1"; pub const LOCAL_REPOSITORY_ALIAS: &str = "local"; pub const DEFAULT_GENERATED_REPOSITORY_ALIAS: &str = "autogen"; @@ -253,6 +256,54 @@ pub struct PackageDirectory { pub version_scripts: Vec, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PackageDirectoryMetadata { + #[serde(default)] + pub display_name: Option, + #[serde(default)] + pub lua: HashMap, + #[serde(flatten)] + pub package: PackageTypeMetadata, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum PackageTypeMetadata { + #[serde(rename = "android:app")] + AndroidApp { android: AndroidPackageMetadata }, + #[serde(rename = "magisk:module")] + MagiskModule { magisk: MagiskPackageMetadata }, + #[serde(rename = "generic")] + Generic { generic: GenericPackageMetadata }, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AndroidPackageMetadata { + pub package_name: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct MagiskPackageMetadata { + pub module_id: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GenericPackageMetadata { + pub id: String, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct PackageLuaMetadata { + #[serde(default)] + pub permission: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum PackageLuaPermission { + AllowFreeNetwork, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct PackageVersionScript { pub version: String, @@ -354,6 +405,29 @@ pub enum RepositoryLoadError { #[source] source: std::io::Error, }, + #[error("failed to read package metadata at {path}: {source}")] + ReadPackageMetadata { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("failed to parse package metadata at {path}: {source}")] + ParsePackageMetadata { + path: PathBuf, + #[source] + source: serde_json::Error, + }, + #[error("invalid package metadata at {path}: {reason}")] + InvalidPackageMetadata { path: PathBuf, reason: String }, + #[error("package '{package_id}' has no enabled version scripts")] + MissingPackageVersionScript { package_id: PackageId }, + #[error("package '{package_id}' has multiple enabled version scripts; explicit version selection is not implemented")] + AmbiguousPackageVersionScript { package_id: PackageId }, + #[error("Lua version script {path} is missing required shebang '{expected}'")] + MissingLuaApiShebang { + path: PathBuf, + expected: &'static str, + }, } impl RepositoryPackageDirectoryLayout { @@ -374,6 +448,43 @@ impl RepositoryPackageDirectoryLayout { pub fn package(&self, id: &PackageId) -> Option<&PackageDirectory> { self.packages.iter().find(|package| &package.id == id) } + + pub fn package_metadata( + &self, + package: &PackageDirectory, + ) -> Result { + load_package_metadata(&package.metadata_path) + } + + pub fn unambiguous_version_script<'a>( + &self, + package: &'a PackageDirectory, + ) -> Result<&'a PackageVersionScript, RepositoryLoadError> { + match package.version_scripts.as_slice() { + [script] => Ok(script), + [] => Err(RepositoryLoadError::MissingPackageVersionScript { + package_id: package.id.clone(), + }), + _ => Err(RepositoryLoadError::AmbiguousPackageVersionScript { + package_id: package.id.clone(), + }), + } + } +} + +impl PackageDirectoryMetadata { + pub fn display_name_for(&self, package_id: &PackageId) -> String { + self.display_name + .clone() + .unwrap_or_else(|| package_id.to_string()) + } + + pub fn permissions_for(&self, file_name: &str) -> &[PackageLuaPermission] { + self.lua + .get(file_name) + .map(|metadata| metadata.permission.as_slice()) + .unwrap_or(&[]) + } } impl RepositoryLayout { @@ -551,17 +662,37 @@ fn collect_package_boundary( } fn parse_package_metadata(metadata_path: &Path) -> Result<(), String> { - let bytes = fs::read(metadata_path) - .map_err(|source| format!("failed to read package metadata: {source}"))?; - let value = serde_json::from_reader::<_, serde_json::Value>(json_comments::StripComments::new( + load_package_metadata(metadata_path) + .map(|_| ()) + .map_err(|source| source.to_string()) +} + +pub fn load_package_metadata( + metadata_path: impl AsRef, +) -> Result { + let metadata_path = metadata_path.as_ref(); + let bytes = + fs::read(metadata_path).map_err(|source| RepositoryLoadError::ReadPackageMetadata { + path: metadata_path.to_path_buf(), + source, + })?; + let value: serde_json::Value = serde_json::from_reader(json_comments::StripComments::new( bytes.as_slice(), )) - .map_err(|source| format!("failed to parse package metadata: {source}"))?; - if value.is_object() { - Ok(()) - } else { - Err("package metadata must be a JSON object".to_owned()) + .map_err(|source| RepositoryLoadError::ParsePackageMetadata { + path: metadata_path.to_path_buf(), + source, + })?; + if !value.is_object() { + return Err(RepositoryLoadError::InvalidPackageMetadata { + path: metadata_path.to_path_buf(), + reason: "package metadata must be a JSON object".to_owned(), + }); } + serde_json::from_value(value).map_err(|source| RepositoryLoadError::ParsePackageMetadata { + path: metadata_path.to_path_buf(), + source, + }) } fn discover_version_scripts( @@ -704,22 +835,42 @@ pub fn package_cache_key( }) } +pub fn package_directory_cache_key( + repository_id: &RepositoryId, + package: &PackageDirectory, + script: &PackageVersionScript, +) -> Result { + let package_file_hash = format!( + "metadata={};script={};manifest={}", + package_file_content_hash(&package.metadata_path)?, + package_file_content_hash(&script.path)?, + optional_file_content_hash(&package.path.join(PACKAGE_MANIFEST_FILE))? + .unwrap_or_else(|| "missing".to_owned()) + ); + Ok(RepositoryPackageCacheKey { + repository_id: repository_id.clone(), + package_id: package.id.clone(), + api_version: REPO_API_VERSION_V1.to_owned(), + package_file_hash, + }) +} + +fn optional_file_content_hash(path: &Path) -> Result, RepositoryLoadError> { + if path.exists() { + package_file_content_hash(path).map(Some) + } else { + Ok(None) + } +} + pub fn package_file_content_hash(path: impl AsRef) -> Result { let path = path.as_ref(); let bytes = fs::read(path).map_err(|source| RepositoryLoadError::HashPackageFile { path: path.to_path_buf(), source, })?; - Ok(format!("{:016x}", fnv1a64(&bytes))) -} - -fn fnv1a64(bytes: &[u8]) -> u64 { - let mut hash = 0xcbf29ce484222325u64; - for byte in bytes { - hash ^= u64::from(*byte); - hash = hash.wrapping_mul(0x100000001b3); - } - hash + let hash = Sha512::digest(&bytes); + Ok(format!("sha512:{hash:x}")) } pub fn highest_priority(items: &[T], priority: F) -> Option<&T> @@ -906,7 +1057,7 @@ mod tests { fs::create_dir_all(package_dir.join("nested")).unwrap(); fs::write( package_dir.join(PACKAGE_METADATA_FILE), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, ) .unwrap(); fs::write(package_dir.join("1.2.3.lua"), "return {}").unwrap(); @@ -944,7 +1095,7 @@ mod tests { fs::write(package_dir.join(PACKAGE_METADATA_FILE), "{not-json").unwrap(); fs::write( package_dir.join("nested/android/app/hidden/metadata.jsonc"), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "hidden" } }"#, ) .unwrap(); @@ -968,19 +1119,19 @@ mod tests { fs::create_dir_all(root.join(".metadata/android/app/hidden")).unwrap(); fs::write( root.join(".metadata/android/app/hidden/metadata.jsonc"), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "hidden" } }"#, ) .unwrap(); fs::create_dir_all(root.join("luaclass/android/app/hidden")).unwrap(); fs::write( root.join("luaclass/android/app/hidden/metadata.jsonc"), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "hidden" } }"#, ) .unwrap(); fs::create_dir_all(root.join("android/app/visible")).unwrap(); fs::write( root.join("android/app/visible/metadata.jsonc"), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "visible" } }"#, ) .unwrap(); @@ -1014,10 +1165,9 @@ mod tests { assert!(layout.packages.is_empty()); assert_eq!(layout.invalid_packages.len(), 1); - assert_eq!( - layout.invalid_packages[0].reason, - "package metadata must be a JSON object" - ); + assert!(layout.invalid_packages[0] + .reason + .contains("package metadata must be a JSON object")); } #[test] @@ -1027,7 +1177,7 @@ mod tests { fs::create_dir_all(root.join("invalidkind/app/example")).unwrap(); fs::write( root.join("invalidkind/app/example/metadata.jsonc"), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "example" } }"#, ) .unwrap(); diff --git a/crates/getter-operations/src/read_model.rs b/crates/getter-operations/src/read_model.rs index 898864b..79f5cad 100644 --- a/crates/getter-operations/src/read_model.rs +++ b/crates/getter-operations/src/read_model.rs @@ -6,9 +6,11 @@ //! to parse and render. #[cfg(feature = "lua")] -use getter_core::lua::evaluate_package_file; +use getter_core::lua::{evaluate_package_directory_script, evaluate_package_file}; #[cfg(feature = "lua")] -use getter_core::repository::{RepositoryLayout, RepositoryLoadError}; +use getter_core::repository::{ + RepositoryLayout, RepositoryLoadError, RepositoryPackageDirectoryLayout, +}; #[cfg(feature = "lua")] use getter_core::{PackageId, RepositoryId}; use getter_storage::{MainDb, StorageError, StoredRepository, StoredTrackedPackage}; @@ -140,16 +142,12 @@ fn evaluate_package_from_repo( package_id: &PackageId, ) -> Result { let repo = find_repository(db, repo_id)?; - let path = repo_path(&repo)?; - let layout = RepositoryLayout::load(&path)?; - let package_file = layout.package_file(package_id).ok_or_else(|| { + evaluate_package_in_repository(&repo, package_id)?.ok_or_else(|| { ReadModelOperationError::PackageEval(format!( "package '{}' was not found in repository '{}'", package_id, repo_id )) - })?; - evaluate_package_file(&layout, &package_file.path) - .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())) + }) } #[cfg(feature = "lua")] @@ -158,11 +156,8 @@ fn evaluate_highest_priority_package( package_id: &PackageId, ) -> Result { for repo in db.repositories()? { - let path = repo_path(&repo)?; - let layout = RepositoryLayout::load(&path)?; - if let Some(package_file) = layout.package_file(package_id) { - return evaluate_package_file(&layout, &package_file.path) - .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())); + if let Some(package) = evaluate_package_in_repository(&repo, package_id)? { + return Ok(package); } } Err(ReadModelOperationError::PackageEval(format!( @@ -170,6 +165,33 @@ fn evaluate_highest_priority_package( ))) } +#[cfg(feature = "lua")] +fn evaluate_package_in_repository( + repo: &StoredRepository, + package_id: &PackageId, +) -> Result, ReadModelOperationError> { + let path = repo_path(repo)?; + if path.join("repo.toml").is_file() { + let layout = RepositoryLayout::load(&path)?; + let Some(package_file) = layout.package_file(package_id) else { + return Ok(None); + }; + return evaluate_package_file(&layout, &package_file.path) + .map(Some) + .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())); + } + + let layout = RepositoryPackageDirectoryLayout::load(&path)?; + let Some(package) = layout.package(package_id) else { + return Ok(None); + }; + let metadata = layout.package_metadata(package)?; + let script = layout.unambiguous_version_script(package)?; + evaluate_package_directory_script(&repo.id, package, &metadata, script) + .map(Some) + .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())) +} + #[cfg(feature = "lua")] fn find_repository( db: &MainDb, @@ -274,6 +296,68 @@ mod tests { fn package_eval_reads_registered_lua_repository() { let temp = tempdir().unwrap(); let repo_path = temp.path().join("repo"); + write_legacy_lua_repo(&repo_path); + + let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::new(10), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + ) + .unwrap(); + + let result = package_eval_json( + temp.path(), + r#"{"package_id":"android/org.fdroid.fdroid","repository_id":"official"}"#, + ) + .unwrap(); + assert_eq!(result["package"]["id"], "android/org.fdroid.fdroid"); + assert_eq!(result["package"]["name"], "F-Droid"); + assert_eq!(result["package"]["permissions"]["free_network"], true); + } + + #[cfg(feature = "lua")] + #[test] + fn package_eval_reads_registered_package_directory_repository() { + let temp = tempdir().unwrap(); + let repo_path = temp.path().join("repo"); + write_package_directory_repo(&repo_path); + + let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "autogen".parse().unwrap(), + name: "Autogen".to_owned(), + priority: RepositoryPriority::new(-1), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + ) + .unwrap(); + + let result = package_eval_json( + temp.path(), + r#"{"package_id":"android/app/com.example.autogen","repository_id":"autogen"}"#, + ) + .unwrap(); + + assert_eq!(result["package"]["id"], "android/app/com.example.autogen"); + assert_eq!(result["package"]["repository"], "autogen"); + assert_eq!(result["package"]["name"], "Example Autogen"); + assert_eq!( + result["package"]["installed"][0]["package_name"], + "com.example.autogen" + ); + } + + #[cfg(feature = "lua")] + fn write_legacy_lua_repo(repo_path: &Path) { fs::create_dir_all(repo_path.join("packages/android")).unwrap(); fs::create_dir_all(repo_path.join("lib")).unwrap(); fs::create_dir_all(repo_path.join("templates")).unwrap(); @@ -299,27 +383,32 @@ api_version = "getter.repo.v1" "#, ) .unwrap(); + } - let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); - db.upsert_repository( - &RepositoryMetadata { - id: "official".parse().unwrap(), - name: "Official".to_owned(), - priority: RepositoryPriority::new(10), - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_path), - None, + #[cfg(feature = "lua")] + fn write_package_directory_repo(repo_path: &Path) { + let package_dir = repo_path.join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, ) .unwrap(); - - let result = package_eval_json( - temp.path(), - r#"{"package_id":"android/org.fdroid.fdroid","repository_id":"official"}"#, + fs::write(package_dir.join("Manifest"), "").unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { + installed = { + { kind = "android_package", package_name = "com.example.autogen" }, + }, +} +"#, ) .unwrap(); - assert_eq!(result["package"]["id"], "android/org.fdroid.fdroid"); - assert_eq!(result["package"]["name"], "F-Droid"); - assert_eq!(result["package"]["permissions"]["free_network"], true); } } diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 1392322..d9e53f1 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -8,8 +8,11 @@ #[cfg(feature = "lua")] use getter_core::{ - lua::evaluate_package_file, - repository::{package_cache_key, RepositoryLayout, RepositoryLoadError}, + lua::{evaluate_package_directory_script, evaluate_package_file}, + repository::{ + package_cache_key, package_directory_cache_key, RepositoryLayout, RepositoryLoadError, + RepositoryPackageDirectoryLayout, + }, }; use getter_core::{ runtime::{ @@ -383,13 +386,31 @@ fn evaluate_registered_package( continue; }; let root = PathBuf::from(root); - let layout = RepositoryLayout::load(&root)?; - let Some(package_file) = layout.package_file(&request.package_id) else { + if root.join("repo.toml").is_file() { + let layout = RepositoryLayout::load(&root)?; + let Some(package_file) = layout.package_file(&request.package_id) else { + continue; + }; + let package = evaluate_package_file(&layout, &package_file.path) + .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; + let cache_key = package_cache_key(&layout, package_file)?; + let dependency_digest = format!( + "repo:{}:package:{}:hash:{}", + cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash + ); + return Ok((package, dependency_digest)); + } + + let layout = RepositoryPackageDirectoryLayout::load(&root)?; + let Some(package_directory) = layout.package(&request.package_id) else { continue; }; - let package = evaluate_package_file(&layout, &package_file.path) - .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; - let cache_key = package_cache_key(&layout, package_file)?; + let metadata = layout.package_metadata(package_directory)?; + let script = layout.unambiguous_version_script(package_directory)?; + let package = + evaluate_package_directory_script(&repository.id, package_directory, &metadata, script) + .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; + let cache_key = package_directory_cache_key(&repository.id, package_directory, script)?; let dependency_digest = format!( "repo:{}:package:{}:hash:{}", cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash @@ -484,6 +505,47 @@ mod tests { assert_eq!(submitted["package_id"], "android/org.fdroid.fdroid"); } + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_issues_action_from_package_directory_static_updates() { + let temp = tempfile::tempdir().unwrap(); + let repo_root = temp.path().join("repo"); + write_package_directory_static_update_repo(&repo_root); + let db = MainDb::open_in_memory().unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "autogen".parse().unwrap(), + name: "Autogen".to_owned(), + priority: RepositoryPriority::new(-1), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_root), + None, + ) + .unwrap(); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + &db, + &json!({ + "package_id": "android/app/com.example.autogen", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["package"]["repository"], "autogen"); + assert_eq!(issued["package"]["name"], "Example Autogen"); + assert_eq!(issued["update"]["status"], "update_available"); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + assert_eq!(submitted["package_id"], "android/app/com.example.autogen"); + } + #[cfg(feature = "lua")] #[test] fn registered_package_update_check_without_update_does_not_issue_action() { @@ -682,6 +744,42 @@ return package_def { .unwrap(); } + #[cfg(feature = "lua")] + fn write_package_directory_static_update_repo(root: &std::path::Path) { + let package_dir = root.join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write(package_dir.join("Manifest"), "").unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { + updates = { + { + version = "1.2.0", + artifacts = { + { + name = "app.apk", + url = "https://example.invalid/app.apk", + file_name = "app.apk", + }, + }, + }, + }, +} +"#, + ) + .unwrap(); + } + fn update_fixture( package_id: &str, installed_version: Option<&str>, From 15788b42a6c786ce86de80af744e72662e1e3e9d Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 02:08:03 +0800 Subject: [PATCH 32/72] fix(core): satisfy clippy for Lua errors --- crates/getter-core/src/lua.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 4bea4d6..53cd121 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -26,7 +26,7 @@ pub enum LuaPackageError { Runtime { path: PathBuf, #[source] - source: mlua::Error, + source: Box, }, #[error("Lua package {path} did not return a table")] NotATable { path: PathBuf }, @@ -44,7 +44,7 @@ pub enum LuaPackageError { Repository { path: PathBuf, #[source] - source: RepositoryLoadError, + source: Box, }, } @@ -90,10 +90,10 @@ pub fn evaluate_package_directory_script( if source.lines().next() != Some(LUA_API_SHEBANG_V1) { return Err(LuaPackageError::Repository { path: script.path.clone(), - source: RepositoryLoadError::MissingLuaApiShebang { + source: Box::new(RepositoryLoadError::MissingLuaApiShebang { path: script.path.clone(), expected: LUA_API_SHEBANG_V1, - }, + }), }); } let json = evaluate_package_source_to_json( @@ -113,15 +113,15 @@ fn evaluate_package_source_to_json( let lua = Lua::new(); configure_package_path(&lua, environment).map_err(|source| LuaPackageError::Runtime { path: path.clone(), - source, + source: Box::new(source), })?; remove_unsafe_globals(&lua).map_err(|source| LuaPackageError::Runtime { path: path.clone(), - source, + source: Box::new(source), })?; install_helpers(&lua).map_err(|source| LuaPackageError::Runtime { path: path.clone(), - source, + source: Box::new(source), })?; let value = lua @@ -130,7 +130,7 @@ fn evaluate_package_source_to_json( .eval::() .map_err(|source| LuaPackageError::Runtime { path: path.clone(), - source, + source: Box::new(source), })?; let table = match value { Value::Table(table) => table, @@ -325,13 +325,13 @@ fn lua_table_to_json( ) -> Result { if is_array_table(&table).map_err(|source| LuaPackageError::Runtime { path: path.to_path_buf(), - source, + source: Box::new(source), })? { let mut array = Vec::new(); for pair in table.sequence_values::() { let value = pair.map_err(|source| LuaPackageError::Runtime { path: path.to_path_buf(), - source, + source: Box::new(source), })?; array.push(lua_value_to_json(path, &format!("{location}[]"), value)?); } @@ -341,14 +341,14 @@ fn lua_table_to_json( for pair in table.pairs::() { let (key, value) = pair.map_err(|source| LuaPackageError::Runtime { path: path.to_path_buf(), - source, + source: Box::new(source), })?; let key = match key { Value::String(value) => value .to_str() .map_err(|source| LuaPackageError::Runtime { path: path.to_path_buf(), - source, + source: Box::new(source), })? .to_owned(), Value::Integer(value) => value.to_string(), @@ -388,7 +388,7 @@ fn lua_value_to_json( .to_str() .map_err(|source| LuaPackageError::Runtime { path: path.to_path_buf(), - source, + source: Box::new(source), })? .to_owned(), )), @@ -532,8 +532,7 @@ fn metadata_permissions_for_script( PackagePermissions { free_network: metadata .permissions_for(file_name) - .iter() - .any(|permission| *permission == PackageLuaPermission::AllowFreeNetwork), + .contains(&PackageLuaPermission::AllowFreeNetwork), } } From 091bb9fde26187fa01da008a9ce1faf3b2411fcc Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 02:17:58 +0800 Subject: [PATCH 33/72] feat(lua): expose package-local file reads --- crates/getter-core/src/lua.rs | 173 +++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 53cd121..8d6f74e 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -119,7 +119,7 @@ fn evaluate_package_source_to_json( path: path.clone(), source: Box::new(source), })?; - install_helpers(&lua).map_err(|source| LuaPackageError::Runtime { + install_helpers(&lua, environment).map_err(|source| LuaPackageError::Runtime { path: path.clone(), source: Box::new(source), })?; @@ -308,16 +308,69 @@ fn module_to_relative_path(module: &str) -> Option { } } -fn install_helpers(lua: &Lua) -> mlua::Result<()> { +fn install_helpers(lua: &Lua, environment: &LuaRepositoryEnvironment<'_>) -> mlua::Result<()> { let package_fn = lua.create_function(|_, table: Table| Ok(table))?; lua.globals().set("package_def", package_fn.clone())?; lua.globals().set("package_version", package_fn.clone())?; lua.globals().set("android_app", package_fn.clone())?; lua.globals().set("magisk_module", package_fn.clone())?; lua.globals().set("generic_package", package_fn)?; + if let LuaRepositoryEnvironment::PackageDirectory { package } = environment { + install_package_file_helpers(lua, package)?; + } Ok(()) } +fn install_package_file_helpers(lua: &Lua, package: &PackageDirectory) -> mlua::Result<()> { + let files_root = package.path.join("files"); + let read_package_file = lua.create_function(move |lua, requested_path: String| { + let relative_path = package_file_relative_path(&requested_path)?; + let path = files_root.join(relative_path); + let metadata = fs::metadata(&path).map_err(mlua::Error::external)?; + if !metadata.is_file() { + return Err(mlua::Error::external(format!( + "read_package_file target {} is not a file", + path.display() + ))); + } + let bytes = fs::read(&path).map_err(mlua::Error::external)?; + lua.create_string(&bytes) + })?; + + let globals = lua.globals(); + let getter_builtin = match globals.get::("getter_builtin")? { + Value::Table(table) => table, + Value::Nil => { + let table = lua.create_table()?; + globals.set("getter_builtin", table.clone())?; + table + } + _ => return Err(mlua::Error::external("getter_builtin must be a table")), + }; + getter_builtin.set("read_package_file", read_package_file.clone())?; + globals.set("read_package_file", read_package_file) +} + +fn package_file_relative_path(path: &str) -> mlua::Result { + let mut relative = PathBuf::new(); + for component in Path::new(path).components() { + match component { + std::path::Component::Normal(part) => relative.push(part), + _ => { + return Err(mlua::Error::external( + "read_package_file path must be relative to files/ and must not contain special path components", + )) + } + } + } + if relative.as_os_str().is_empty() { + return Err(mlua::Error::external( + "read_package_file path must not be empty", + )); + } + Ok(relative) +} + fn lua_table_to_json( path: &Path, location: &str, @@ -1000,6 +1053,122 @@ return android.package_version { assert_eq!(package.installed.len(), 1); } + #[test] + fn package_directory_can_read_package_local_files() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(package_dir.join("files/nested")).unwrap(); + fs::write(package_dir.join("files/nested/data.txt"), b"hello\xff").unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local body = read_package_file("nested/data.txt") +return package_version { name = "bytes:" .. tostring(#body) } +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let package = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap(); + + assert_eq!(package.name, "bytes:6"); + } + + #[test] + fn read_package_file_rejects_paths_outside_package_files() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(package_dir.join("files")).unwrap(); + fs::write(package_dir.join("outside.txt"), "outside").unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local ok = pcall(read_package_file, "../outside.txt") +return package_version { name = ok and "leaked" or "blocked" } +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let package = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap(); + + assert_eq!(package.name, "blocked"); + } + + #[test] + fn getter_builtin_exposes_package_file_reader_to_package_directory_lua() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(package_dir.join("files")).unwrap(); + fs::write(package_dir.join("files/name.txt"), "from builtin").unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { name = getter_builtin.read_package_file("name.txt") } +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let package = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap(); + + assert_eq!(package.name, "from builtin"); + } + #[test] fn rejects_package_id_that_does_not_match_path() { let (_temp, layout, package_path) = fixture_repo(); From 139d1ff02b80ce64f753f1b24e8fbe7e4111a96d Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 02:24:59 +0800 Subject: [PATCH 34/72] fix(repository): hash package-local file inputs --- crates/getter-core/src/diagnostics.rs | 3 + crates/getter-core/src/repository.rs | 143 ++++++++++++++++++++++++-- 2 files changed, 139 insertions(+), 7 deletions(-) diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index 48f36a3..a78ff21 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -215,6 +215,9 @@ fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDi path, format!("failed to hash package file: {source}"), ), + RepositoryLoadError::InvalidPackageLocalFile { path, reason } => { + ("package.local_file", path, reason) + } RepositoryLoadError::ReadPackageMetadata { path, source } => ( "package.read_metadata", path, diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index f85c81a..f5f0e99 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -18,6 +18,7 @@ pub const REPOSITORY_SELF_METADATA_DIR: &str = ".metadata"; pub const REPOSITORY_LUACLASS_DIR: &str = "luaclass"; pub const PACKAGE_METADATA_FILE: &str = "metadata.jsonc"; pub const PACKAGE_MANIFEST_FILE: &str = "Manifest"; +pub const PACKAGE_LOCAL_FILES_DIR: &str = "files"; pub const LUA_SCRIPT_EXTENSION: &str = "lua"; pub const LUA_API_SHEBANG_V1: &str = "#!/bin/upa-lua v1"; pub const LOCAL_REPOSITORY_ALIAS: &str = "local"; @@ -405,6 +406,8 @@ pub enum RepositoryLoadError { #[source] source: std::io::Error, }, + #[error("invalid package-local file {path}: {reason}")] + InvalidPackageLocalFile { path: PathBuf, reason: String }, #[error("failed to read package metadata at {path}: {source}")] ReadPackageMetadata { path: PathBuf, @@ -840,13 +843,7 @@ pub fn package_directory_cache_key( package: &PackageDirectory, script: &PackageVersionScript, ) -> Result { - let package_file_hash = format!( - "metadata={};script={};manifest={}", - package_file_content_hash(&package.metadata_path)?, - package_file_content_hash(&script.path)?, - optional_file_content_hash(&package.path.join(PACKAGE_MANIFEST_FILE))? - .unwrap_or_else(|| "missing".to_owned()) - ); + let package_file_hash = package_directory_dependency_hash(package, script)?; Ok(RepositoryPackageCacheKey { repository_id: repository_id.clone(), package_id: package.id.clone(), @@ -855,6 +852,99 @@ pub fn package_directory_cache_key( }) } +fn package_directory_dependency_hash( + package: &PackageDirectory, + script: &PackageVersionScript, +) -> Result { + let mut entries = vec![ + format!( + "metadata.jsonc={}", + package_file_content_hash(&package.metadata_path)? + ), + format!( + "{}={}", + script.file_name, + package_file_content_hash(&script.path)? + ), + format!( + "Manifest={}", + optional_file_content_hash(&package.path.join(PACKAGE_MANIFEST_FILE))? + .unwrap_or_else(|| "missing".to_owned()) + ), + ]; + for file in package_local_files(&package.path)? { + let hash = package_file_content_hash(&file.path)?; + entries.push(format!("files/{}={hash}", file.relative_path)); + } + Ok(content_hash(entries.join("\n"))) +} + +struct PackageLocalFile { + relative_path: String, + path: PathBuf, +} + +fn package_local_files(package_dir: &Path) -> Result, RepositoryLoadError> { + let files_root = package_dir.join(PACKAGE_LOCAL_FILES_DIR); + if !files_root.exists() { + return Ok(Vec::new()); + } + if !files_root.is_dir() { + return Err(RepositoryLoadError::InvalidPackageLocalFile { + path: files_root, + reason: "package-local files entry must be a directory".to_owned(), + }); + } + let mut files = Vec::new(); + collect_package_local_files(&files_root, &files_root, &mut files)?; + files.sort_by(|left, right| left.relative_path.cmp(&right.relative_path)); + Ok(files) +} + +fn collect_package_local_files( + files_root: &Path, + current: &Path, + out: &mut Vec, +) -> Result<(), RepositoryLoadError> { + for entry in read_dir_entries(current)? { + let path = entry.path(); + let file_type = entry_file_type(&entry, current)?; + if file_type.is_dir() { + collect_package_local_files(files_root, &path, out)?; + } else if file_type.is_file() { + let relative = path.strip_prefix(files_root).map_err(|_| { + RepositoryLoadError::InvalidPackageLocalFile { + path: path.clone(), + reason: format!("path is not under {}", files_root.display()), + } + })?; + out.push(PackageLocalFile { + relative_path: logical_relative_path(&path, relative)?, + path, + }); + } + } + Ok(()) +} + +fn logical_relative_path(path: &Path, relative: &Path) -> Result { + let value = relative + .to_string_lossy() + .replace(std::path::MAIN_SEPARATOR, "/"); + if value.is_empty() { + return Err(RepositoryLoadError::InvalidPackageLocalFile { + path: path.to_path_buf(), + reason: "missing relative file path".to_owned(), + }); + } + Ok(value) +} + +fn content_hash(content: impl AsRef<[u8]>) -> String { + let hash = Sha512::digest(content.as_ref()); + format!("sha512:{hash:x}") +} + fn optional_file_content_hash(path: &Path) -> Result, RepositoryLoadError> { if path.exists() { package_file_content_hash(path).map(Some) @@ -1261,6 +1351,45 @@ api_version = "getter.repo.v1" assert_ne!(first.package_file_hash, second.package_file_hash); } + #[test] + fn package_directory_cache_key_changes_when_local_files_change() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + let package_dir = root.join("android/app/example"); + fs::create_dir_all(package_dir.join(PACKAGE_LOCAL_FILES_DIR)).unwrap(); + fs::write( + package_dir.join(PACKAGE_METADATA_FILE), + r#"{ "type": "android:app", "android": { "package_name": "example" } }"#, + ) + .unwrap(); + fs::write(package_dir.join("9999.lua"), "return {}").unwrap(); + fs::write( + package_dir.join(PACKAGE_LOCAL_FILES_DIR).join("data.txt"), + "one", + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + let package = &layout.packages[0]; + let script = layout.unambiguous_version_script(package).unwrap(); + let first = + package_directory_cache_key(&RepositoryId::new("repo").unwrap(), package, script) + .unwrap(); + + fs::write( + package_dir.join(PACKAGE_LOCAL_FILES_DIR).join("data.txt"), + "two", + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + let package = &layout.packages[0]; + let script = layout.unambiguous_version_script(package).unwrap(); + let second = + package_directory_cache_key(&RepositoryId::new("repo").unwrap(), package, script) + .unwrap(); + + assert_ne!(first.package_file_hash, second.package_file_hash); + } + #[test] fn highest_priority_selects_larger_number() { let priorities = [ From edbebceefc960caf20a03eb9a5a9e5210f111949 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 02:56:13 +0800 Subject: [PATCH 35/72] feat(cache): add provider refresh primitives --- crates/getter-operations/src/lib.rs | 1 + .../getter-operations/src/provider_cache.rs | 268 +++++++++++++++ crates/getter-storage/src/lib.rs | 312 ++++++++++++++++++ 3 files changed, 581 insertions(+) create mode 100644 crates/getter-operations/src/provider_cache.rs diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 4bd9337..a2e7afe 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -7,5 +7,6 @@ pub mod autogen; pub mod legacy_room; +pub mod provider_cache; pub mod read_model; pub mod runtime; diff --git a/crates/getter-operations/src/provider_cache.rs b/crates/getter-operations/src/provider_cache.rs new file mode 100644 index 0000000..771cd81 --- /dev/null +++ b/crates/getter-operations/src/provider_cache.rs @@ -0,0 +1,268 @@ +//! Shared provider/source cache refresh helper. +//! +//! Provider implementations can use this small helper to keep ADR-0010/0012 +//! forced-refresh semantics consistent: a successful refresh replaces the cache, +//! while a failed forced refresh may return old cache only with explicit stale +//! diagnostics. + +use getter_storage::{CacheDb, ProviderResponseUpsert, StorageError, StoredProviderResponse}; +use serde_json::Value; + +pub const CACHE_REFRESH_FAILED: &str = "cache.refresh_failed"; +pub const USED_STALE_CACHE: &str = "used_stale_cache"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ProviderCacheMode { + UseCached, + ForceRefresh, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ProviderCacheSource { + Cache, + Refreshed, + Stale, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ProviderCacheRequest<'a> { + pub cache_key: &'a str, + pub provider: &'a str, + pub mode: ProviderCacheMode, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProviderCacheDiagnostic { + pub code: String, + pub message: String, + pub cache_key: String, + pub provider: String, + pub stale_fetched_at_unix: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProviderCacheResult { + pub response: StoredProviderResponse, + pub source: ProviderCacheSource, + pub diagnostics: Vec, +} + +#[derive(Debug, thiserror::Error)] +pub enum ProviderCacheOperationError { + #[error("storage error: {0}")] + Storage(#[from] StorageError), + #[error("provider refresh failed: {0}")] + RefreshFailed(String), +} + +pub fn read_or_refresh_provider_response( + db: &CacheDb, + request: ProviderCacheRequest<'_>, + refresh: F, +) -> Result +where + F: FnOnce() -> Result, +{ + let cached = db.provider_response(request.cache_key)?; + if request.mode == ProviderCacheMode::UseCached { + if let Some(response) = cached { + return Ok(ProviderCacheResult { + response, + source: ProviderCacheSource::Cache, + diagnostics: Vec::new(), + }); + } + } + + match refresh() { + Ok(response_json) => { + let response = db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: request.cache_key.to_owned(), + provider: request.provider.to_owned(), + response_json, + })?; + Ok(ProviderCacheResult { + response, + source: ProviderCacheSource::Refreshed, + diagnostics: Vec::new(), + }) + } + Err(detail) => match cached { + Some(response) => { + let stale_fetched_at_unix = Some(response.fetched_at_unix); + Ok(ProviderCacheResult { + response, + source: ProviderCacheSource::Stale, + diagnostics: vec![ + ProviderCacheDiagnostic { + code: CACHE_REFRESH_FAILED.to_owned(), + message: detail, + cache_key: request.cache_key.to_owned(), + provider: request.provider.to_owned(), + stale_fetched_at_unix, + }, + ProviderCacheDiagnostic { + code: USED_STALE_CACHE.to_owned(), + message: "using stale provider cache after refresh failure".to_owned(), + cache_key: request.cache_key.to_owned(), + provider: request.provider.to_owned(), + stale_fetched_at_unix, + }, + ], + }) + } + None => Err(ProviderCacheOperationError::RefreshFailed(detail)), + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + use std::cell::Cell; + + #[test] + fn uses_cached_provider_response_without_refreshing() { + let db = CacheDb::open_in_memory().unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: "fdroid:official:index".to_owned(), + provider: "fdroid".to_owned(), + response_json: json!({ "revision": "cached" }), + }) + .unwrap(); + let refreshed = Cell::new(false); + + let result = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "fdroid:official:index", + provider: "fdroid", + mode: ProviderCacheMode::UseCached, + }, + || { + refreshed.set(true); + Ok(json!({ "revision": "fresh" })) + }, + ) + .unwrap(); + + assert!(!refreshed.get()); + assert_eq!(result.source, ProviderCacheSource::Cache); + assert_eq!(result.response.response_json["revision"], "cached"); + assert!(result.diagnostics.is_empty()); + } + + #[test] + fn cache_miss_refreshes_and_stores_provider_response() { + let db = CacheDb::open_in_memory().unwrap(); + + let result = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "github:f-droid/fdroidclient:releases", + provider: "github", + mode: ProviderCacheMode::UseCached, + }, + || Ok(json!({ "etag": "fresh" })), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.response.response_json["etag"], "fresh"); + assert_eq!( + db.provider_response("github:f-droid/fdroidclient:releases") + .unwrap() + .unwrap() + .response_json["etag"], + "fresh" + ); + } + + #[test] + fn forced_refresh_replaces_cached_provider_response() { + let db = CacheDb::open_in_memory().unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: "fdroid:official:index".to_owned(), + provider: "fdroid".to_owned(), + response_json: json!({ "revision": "old" }), + }) + .unwrap(); + + let result = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "fdroid:official:index", + provider: "fdroid", + mode: ProviderCacheMode::ForceRefresh, + }, + || Ok(json!({ "revision": "new" })), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.response.response_json["revision"], "new"); + assert_eq!( + db.provider_response("fdroid:official:index") + .unwrap() + .unwrap() + .response_json["revision"], + "new" + ); + } + + #[test] + fn forced_refresh_failure_returns_stale_cache_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: "github:f-droid/fdroidclient:releases".to_owned(), + provider: "github".to_owned(), + response_json: json!({ "etag": "old" }), + }) + .unwrap(); + + let result = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "github:f-droid/fdroidclient:releases", + provider: "github", + mode: ProviderCacheMode::ForceRefresh, + }, + || Err("HTTP 503".to_owned()), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!(result.response.response_json["etag"], "old"); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + assert!(result + .diagnostics + .iter() + .all(|diagnostic| diagnostic.stale_fetched_at_unix.is_some())); + } + + #[test] + fn refresh_failure_without_stale_cache_is_an_error() { + let db = CacheDb::open_in_memory().unwrap(); + let error = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "fdroid:official:index", + provider: "fdroid", + mode: ProviderCacheMode::ForceRefresh, + }, + || Err("network unavailable".to_owned()), + ) + .unwrap_err(); + + assert!(matches!( + error, + ProviderCacheOperationError::RefreshFailed(_) + )); + } +} diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index e8d7b02..2af994e 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -10,6 +10,7 @@ use getter_core::task::{ }; use getter_core::{PackageId, RepositoryId, RepositoryPriority, UpdateAction}; use rusqlite::{params, Connection, OptionalExtension, Params, Transaction}; +use serde_json::Value; use std::path::Path; use std::str::FromStr; @@ -884,6 +885,161 @@ CREATE TABLE IF NOT EXISTS provider_responses ( )?; Ok(()) } + + pub fn upsert_provider_response( + &self, + response: &ProviderResponseUpsert, + ) -> Result { + let response_json = serde_json::to_string(&response.response_json)?; + self.conn.execute( + r#" +INSERT INTO provider_responses(cache_key, provider, response_json) +VALUES (?1, ?2, ?3) +ON CONFLICT(cache_key) DO UPDATE SET + provider = excluded.provider, + response_json = excluded.response_json, + fetched_at_unix = unixepoch() +"#, + params![response.cache_key, response.provider, response_json], + )?; + self.provider_response(&response.cache_key)?.ok_or_else(|| { + StorageError::Invariant(format!( + "provider response '{}' was not readable after upsert", + response.cache_key + )) + }) + } + + pub fn provider_response( + &self, + cache_key: &str, + ) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + r#" +SELECT cache_key, provider, response_json, fetched_at_unix +FROM provider_responses +WHERE cache_key = ?1 +"#, + )?; + let row = stmt + .query_row(params![cache_key], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, i64>(3)?, + )) + }) + .optional()?; + row.map( + |(cache_key, provider, response_json, fetched_at_unix)| -> Result<_, StorageError> { + Ok(StoredProviderResponse { + cache_key, + provider, + response_json: serde_json::from_str(&response_json)?, + fetched_at_unix, + }) + }, + ) + .transpose() + } + + pub fn upsert_evaluated_package( + &self, + package: &EvaluatedPackageUpsert, + ) -> Result { + let evaluated_json = serde_json::to_string(&package.evaluated_json)?; + self.conn.execute( + r#" +INSERT INTO evaluated_packages( + cache_key, + repository_id, + package_id, + package_file_hash, + schema_version, + evaluated_json +) +VALUES (?1, ?2, ?3, ?4, ?5, ?6) +ON CONFLICT(cache_key) DO UPDATE SET + repository_id = excluded.repository_id, + package_id = excluded.package_id, + package_file_hash = excluded.package_file_hash, + schema_version = excluded.schema_version, + evaluated_json = excluded.evaluated_json, + evaluated_at_unix = unixepoch() +"#, + params![ + package.cache_key, + package.repository_id.as_str(), + package.package_id.to_string(), + package.package_file_hash, + package.schema_version, + evaluated_json, + ], + )?; + self.evaluated_package(&package.cache_key)?.ok_or_else(|| { + StorageError::Invariant(format!( + "evaluated package '{}' was not readable after upsert", + package.cache_key + )) + }) + } + + pub fn evaluated_package( + &self, + cache_key: &str, + ) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + r#" +SELECT + cache_key, + repository_id, + package_id, + package_file_hash, + schema_version, + evaluated_json, + evaluated_at_unix +FROM evaluated_packages +WHERE cache_key = ?1 +"#, + )?; + let row = stmt + .query_row(params![cache_key], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, String>(3)?, + row.get::<_, String>(4)?, + row.get::<_, String>(5)?, + row.get::<_, i64>(6)?, + )) + }) + .optional()?; + row.map( + |( + cache_key, + repository_id, + package_id, + package_file_hash, + schema_version, + evaluated_json, + evaluated_at_unix, + )| + -> Result<_, StorageError> { + Ok(StoredEvaluatedPackage { + cache_key, + repository_id: RepositoryId::new(repository_id)?, + package_id: package_id.parse()?, + package_file_hash, + schema_version, + evaluated_json: serde_json::from_str(&evaluated_json)?, + evaluated_at_unix, + }) + }, + ) + .transpose() + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -896,6 +1052,42 @@ pub struct StoredRepository { pub revision: Option, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProviderResponseUpsert { + pub cache_key: String, + pub provider: String, + pub response_json: Value, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StoredProviderResponse { + pub cache_key: String, + pub provider: String, + pub response_json: Value, + pub fetched_at_unix: i64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EvaluatedPackageUpsert { + pub cache_key: String, + pub repository_id: RepositoryId, + pub package_id: PackageId, + pub package_file_hash: String, + pub schema_version: String, + pub evaluated_json: Value, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StoredEvaluatedPackage { + pub cache_key: String, + pub repository_id: RepositoryId, + pub package_id: PackageId, + pub package_file_hash: String, + pub schema_version: String, + pub evaluated_json: Value, + pub evaluated_at_unix: i64, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct TrackedPackageUpsert { pub package_id: PackageId, @@ -1498,4 +1690,124 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); fn cache_db_migrates_schema() { let _db = CacheDb::open_in_memory().unwrap(); } + + #[test] + fn cache_db_round_trips_provider_response_cache_entries() { + let db = CacheDb::open_in_memory().unwrap(); + + let stored = db + .upsert_provider_response(&ProviderResponseUpsert { + cache_key: "fdroid:official:index:v1".to_owned(), + provider: "fdroid".to_owned(), + response_json: serde_json::json!({ + "endpoint": "official", + "packages": ["org.fdroid.fdroid"] + }), + }) + .unwrap(); + + assert_eq!(stored.cache_key, "fdroid:official:index:v1"); + assert_eq!(stored.provider, "fdroid"); + assert_eq!(stored.response_json["endpoint"], "official"); + assert_eq!( + db.provider_response("fdroid:official:index:v1") + .unwrap() + .unwrap(), + stored + ); + } + + #[test] + fn cache_db_replaces_provider_response_for_same_cache_key() { + let db = CacheDb::open_in_memory().unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: "github:f-droid/fdroidclient:releases".to_owned(), + provider: "github".to_owned(), + response_json: serde_json::json!({ "etag": "old" }), + }) + .unwrap(); + + let stored = db + .upsert_provider_response(&ProviderResponseUpsert { + cache_key: "github:f-droid/fdroidclient:releases".to_owned(), + provider: "github".to_owned(), + response_json: serde_json::json!({ "etag": "new" }), + }) + .unwrap(); + + assert_eq!(stored.response_json["etag"], "new"); + assert_eq!( + db.provider_response("github:f-droid/fdroidclient:releases") + .unwrap() + .unwrap() + .response_json["etag"], + "new" + ); + } + + #[test] + fn cache_db_round_trips_evaluated_package_cache_entries() { + let db = CacheDb::open_in_memory().unwrap(); + let stored = db + .upsert_evaluated_package(&EvaluatedPackageUpsert { + cache_key: "official:android/app/org.fdroid.fdroid:sha512:123".to_owned(), + repository_id: RepositoryId::new("official").unwrap(), + package_id: "android/app/org.fdroid.fdroid".parse().unwrap(), + package_file_hash: "sha512:123".to_owned(), + schema_version: "getter.package.v1".to_owned(), + evaluated_json: serde_json::json!({ + "name": "F-Droid", + "updates": [] + }), + }) + .unwrap(); + + assert_eq!(stored.repository_id.as_str(), "official"); + assert_eq!( + stored.package_id.to_string(), + "android/app/org.fdroid.fdroid" + ); + assert_eq!(stored.evaluated_json["name"], "F-Droid"); + assert_eq!( + db.evaluated_package("official:android/app/org.fdroid.fdroid:sha512:123") + .unwrap() + .unwrap(), + stored + ); + } + + #[test] + fn cache_db_replaces_evaluated_package_for_same_cache_key() { + let db = CacheDb::open_in_memory().unwrap(); + let cache_key = "official:android/app/org.fdroid.fdroid:sha512:123"; + db.upsert_evaluated_package(&EvaluatedPackageUpsert { + cache_key: cache_key.to_owned(), + repository_id: RepositoryId::new("official").unwrap(), + package_id: "android/app/org.fdroid.fdroid".parse().unwrap(), + package_file_hash: "sha512:old".to_owned(), + schema_version: "getter.package.v1".to_owned(), + evaluated_json: serde_json::json!({ "name": "Old" }), + }) + .unwrap(); + + let stored = db + .upsert_evaluated_package(&EvaluatedPackageUpsert { + cache_key: cache_key.to_owned(), + repository_id: RepositoryId::new("official").unwrap(), + package_id: "android/app/org.fdroid.fdroid".parse().unwrap(), + package_file_hash: "sha512:new".to_owned(), + schema_version: "getter.package.v1".to_owned(), + evaluated_json: serde_json::json!({ "name": "New" }), + }) + .unwrap(); + + assert_eq!(stored.package_file_hash, "sha512:new"); + assert_eq!( + db.evaluated_package(cache_key) + .unwrap() + .unwrap() + .evaluated_json["name"], + "New" + ); + } } From bc94040e4facb44cd3d5a2a9cd65b83902959b6d Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 03:33:44 +0800 Subject: [PATCH 36/72] feat(providers): parse F-Droid catalog fixtures --- Cargo.lock | 11 + crates/getter-core/src/lib.rs | 6 + crates/getter-core/src/lua.rs | 44 ++++ crates/getter-core/src/update.rs | 4 + crates/getter-operations/src/runtime.rs | 3 + crates/getter-providers/Cargo.toml | 2 + crates/getter-providers/src/lib.rs | 299 +++++++++++++++++++++++- 7 files changed, 364 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 34622f9..c43d98e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -636,6 +636,8 @@ name = "getter-providers" version = "0.1.0" dependencies = [ "getter-core", + "roxmltree", + "thiserror 1.0.69", ] [[package]] @@ -1178,6 +1180,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "roxmltree" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" +dependencies = [ + "memchr", +] + [[package]] name = "rusqlite" version = "0.32.1" diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index f02c4b0..957386c 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -281,6 +281,8 @@ pub struct PackagePermissions { pub struct UpdateCandidate { pub version: String, #[serde(default)] + pub version_code: Option, + #[serde(default)] pub channel: Option, #[serde(default)] pub source: Option, @@ -294,6 +296,10 @@ pub struct UpdateArtifact { pub url: String, #[serde(default)] pub file_name: Option, + #[serde(default)] + pub sha256: Option, + #[serde(default)] + pub size: Option, } /// Candidate selected for update after package/user-state policy. diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 8d6f74e..8e33718 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -690,6 +690,7 @@ fn parse_update_candidates( let artifacts = parse_update_artifacts(path, object.get("artifacts"))?; Ok(UpdateCandidate { version: required_string(path, object, "version")?.to_owned(), + version_code: optional_i64(path, object, "version_code")?, channel: optional_string(object, "channel"), source: optional_string(object, "source"), artifacts, @@ -720,6 +721,8 @@ fn parse_update_artifacts( name: required_string(path, object, "name")?.to_owned(), url: required_string(path, object, "url")?.to_owned(), file_name: optional_string(object, "file_name"), + sha256: optional_string(object, "sha256"), + size: optional_u64(path, object, "size")?, }) }) .collect() @@ -732,6 +735,38 @@ fn optional_string(object: &Map, field: &str) -> Option, + field: &str, +) -> Result, LuaPackageError> { + object + .get(field) + .map(|value| { + value.as_i64().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("field '{field}' must be an integer"), + }) + }) + .transpose() +} + +fn optional_u64( + path: &Path, + object: &Map, + field: &str, +) -> Result, LuaPackageError> { + object + .get(field) + .map(|value| { + value.as_u64().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("field '{field}' must be an unsigned integer"), + }) + }) + .transpose() +} + fn parse_permissions( path: &Path, value: Option<&JsonValue>, @@ -822,6 +857,7 @@ return package_def { updates = { { version = "1.2.0", + version_code = 120, channel = "stable", source = "fixture", artifacts = { @@ -829,6 +865,8 @@ return package_def { name = "app.apk", url = "https://example.invalid/app.apk", file_name = "fdroid.apk", + sha256 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + size = 12345, }, }, }, @@ -852,12 +890,18 @@ return package_def { assert_eq!(package.source_priority, vec!["github", "fdroid"]); assert_eq!(package.updates.len(), 1); assert_eq!(package.updates[0].version, "1.2.0"); + assert_eq!(package.updates[0].version_code, Some(120)); assert_eq!(package.updates[0].channel.as_deref(), Some("stable")); assert_eq!(package.updates[0].source.as_deref(), Some("fixture")); assert_eq!( package.updates[0].artifacts[0].file_name.as_deref(), Some("fdroid.apk") ); + assert_eq!( + package.updates[0].artifacts[0].sha256.as_deref(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + assert_eq!(package.updates[0].artifacts[0].size, Some(12345)); } #[test] diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs index 09952ef..6e9260c 100644 --- a/crates/getter-core/src/update.rs +++ b/crates/getter-core/src/update.rs @@ -504,6 +504,7 @@ mod tests { Some("1.0.0".to_owned()), vec![UpdateCandidate { version: "1.2.0".to_owned(), + version_code: None, channel: None, source: None, artifacts: Vec::new(), @@ -539,12 +540,15 @@ mod tests { fn candidate(version: &str) -> UpdateCandidate { UpdateCandidate { version: version.to_owned(), + version_code: None, channel: None, source: None, artifacts: vec![UpdateArtifact { name: "APK".to_owned(), url: format!("https://example.invalid/{version}.apk"), file_name: Some("app.apk".to_owned()), + sha256: None, + size: None, }], } } diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index d9e53f1..3721eed 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -795,12 +795,15 @@ return package_version { .into_iter() .map(|version| UpdateCandidate { version: version.to_owned(), + version_code: None, channel: None, source: None, artifacts: vec![UpdateArtifact { name: "app.apk".to_owned(), url: "https://example.invalid/app.apk".to_owned(), file_name: Some("app.apk".to_owned()), + sha256: None, + size: None, }], }) .collect(), diff --git a/crates/getter-providers/Cargo.toml b/crates/getter-providers/Cargo.toml index f5ae23c..9424784 100644 --- a/crates/getter-providers/Cargo.toml +++ b/crates/getter-providers/Cargo.toml @@ -5,3 +5,5 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core", default-features = false } +roxmltree = "0.21" +thiserror = "1" diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index ee714b9..4d3d02a 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -1,13 +1,13 @@ //! Provider executor scaffolding for the UpgradeAll getter rewrite. //! -//! Real network/provider execution is intentionally deferred to a later ADR. -//! The first Phase D bridge uses package-declared static update candidates as a -//! mock provider so the rest of the runtime can exercise getter-owned update -//! selection and opaque action issuance without direct network side effects. +//! Real network/provider execution is intentionally deferred to later slices. +//! The current provider code is fixture-backed and keeps parsing/normalization in +//! Rust getter so Flutter and Android adapter glue do not learn provider formats. pub use getter_core as core; -use getter_core::{ResolvedPackage, UpdateCandidate}; +use getter_core::{ResolvedPackage, UpdateArtifact, UpdateCandidate}; +use roxmltree::{Document, Node}; /// Mock provider that returns the static `updates` candidates materialized from /// a resolved Lua package table. @@ -24,11 +24,242 @@ impl StaticPackageUpdatesProvider { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidCatalog { + pub endpoint: FdroidEndpoint, + pub apps: Vec, +} + +impl FdroidCatalog { + pub fn app(&self, package_name: &str) -> Option<&FdroidApp> { + self.apps + .iter() + .find(|app| app.package_name == package_name) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidEndpoint { + pub name: Option, + pub url: Option, + pub timestamp: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidApp { + pub package_name: String, + pub name: Option, + pub summary: Option, + pub packages: Vec, +} + +impl FdroidApp { + pub fn update_candidates(&self, endpoint: &FdroidEndpoint) -> Vec { + self.packages + .iter() + .map(|release| release.update_candidate(endpoint)) + .collect() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidRelease { + pub version: String, + pub version_code: Option, + pub apk_name: String, + pub sha256: Option, + pub size: Option, +} + +impl FdroidRelease { + fn update_candidate(&self, endpoint: &FdroidEndpoint) -> UpdateCandidate { + UpdateCandidate { + version: self.version.clone(), + version_code: self.version_code, + channel: None, + source: Some("fdroid".to_owned()), + artifacts: vec![UpdateArtifact { + name: self.apk_name.clone(), + url: endpoint + .url + .as_deref() + .map(|base| fdroid_artifact_url(base, &self.apk_name)) + .unwrap_or_else(|| self.apk_name.clone()), + file_name: Some(self.apk_name.clone()), + sha256: self.sha256.clone(), + size: self.size, + }], + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum FdroidCatalogError { + #[error("failed to parse F-Droid catalog XML: {0}")] + Xml(#[from] roxmltree::Error), + #[error("F-Droid catalog is missing required field {field} for {context}")] + MissingField { + context: String, + field: &'static str, + }, + #[error("F-Droid catalog field {field} for {context} is invalid: {value}")] + InvalidField { + context: String, + field: &'static str, + value: String, + }, +} + +pub fn parse_fdroid_index_xml(xml: &str) -> Result { + let document = Document::parse(xml)?; + let root = document.root_element(); + let repo = child_element(root, "repo"); + let endpoint = FdroidEndpoint { + name: repo + .and_then(|node| node.attribute("name")) + .map(str::to_owned), + url: repo + .and_then(|node| node.attribute("url")) + .map(str::to_owned), + timestamp: repo + .and_then(|node| node.attribute("timestamp")) + .map(str::to_owned), + }; + let apps = root + .children() + .filter(|node| node.has_tag_name("application")) + .map(parse_fdroid_app) + .collect::>()?; + + Ok(FdroidCatalog { endpoint, apps }) +} + +fn parse_fdroid_app(app: Node<'_, '_>) -> Result { + let package_name = app + .attribute("id") + .map(str::to_owned) + .or_else(|| child_text(app, "id")) + .filter(|id| !id.trim().is_empty()) + .ok_or_else(|| FdroidCatalogError::MissingField { + context: "application".to_owned(), + field: "id", + })?; + let packages = app + .children() + .filter(|node| node.has_tag_name("package")) + .map(|package| parse_fdroid_release(&package_name, package)) + .collect::>()?; + + Ok(FdroidApp { + package_name, + name: child_text(app, "name"), + summary: child_text(app, "summary"), + packages, + }) +} + +fn parse_fdroid_release( + package_name: &str, + package: Node<'_, '_>, +) -> Result { + let version = required_child_text(package, package_name, "version")?; + let apk_name = required_child_text(package, package_name, "apkname")?; + let version_code = child_text(package, "versioncode") + .map(|value| parse_i64(package_name, "versioncode", &value)) + .transpose()?; + let size = child_text(package, "size") + .map(|value| parse_u64(package_name, "size", &value)) + .transpose()?; + let sha256 = package + .children() + .find(|node| node.has_tag_name("hash") && node.attribute("type") == Some("sha256")) + .and_then(|node| node.text()) + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(str::to_owned); + + Ok(FdroidRelease { + version, + version_code, + apk_name, + sha256, + size, + }) +} + +fn required_child_text( + parent: Node<'_, '_>, + context: &str, + field: &'static str, +) -> Result { + child_text(parent, field).ok_or_else(|| FdroidCatalogError::MissingField { + context: context.to_owned(), + field, + }) +} + +fn child_text(parent: Node<'_, '_>, tag: &str) -> Option { + child_element(parent, tag) + .and_then(|node| node.text()) + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(str::to_owned) +} + +fn child_element<'a>(parent: Node<'a, 'a>, tag: &str) -> Option> { + parent.children().find(|node| node.has_tag_name(tag)) +} + +fn parse_i64(context: &str, field: &'static str, value: &str) -> Result { + value.parse().map_err(|_| FdroidCatalogError::InvalidField { + context: context.to_owned(), + field, + value: value.to_owned(), + }) +} + +fn parse_u64(context: &str, field: &'static str, value: &str) -> Result { + value.parse().map_err(|_| FdroidCatalogError::InvalidField { + context: context.to_owned(), + field, + value: value.to_owned(), + }) +} + +fn fdroid_artifact_url(base: &str, apk_name: &str) -> String { + format!("{}/{}", base.trim_end_matches('/'), apk_name) +} + #[cfg(test)] mod tests { use super::*; use getter_core::PackagePermissions; + const FDROID_FIXTURE: &str = r#" + + + + org.fdroid.fdroid + F-Droid + App repository client + + 1.20.0 + 1020000 + org.fdroid.fdroid_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + 1.19.0 + 1019000 + org.fdroid.fdroid_1019000.apk + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + 1111111 + + + +"#; + #[test] fn static_provider_returns_package_declared_update_candidates() { let package = ResolvedPackage { @@ -40,6 +271,7 @@ mod tests { source_priority: Vec::new(), updates: vec![UpdateCandidate { version: "1.2.0".to_owned(), + version_code: None, channel: Some("stable".to_owned()), source: Some("fixture".to_owned()), artifacts: Vec::new(), @@ -52,4 +284,61 @@ mod tests { assert_eq!(candidates[0].version, "1.2.0"); assert_eq!(candidates[0].source.as_deref(), Some("fixture")); } + + #[test] + fn parses_fdroid_index_xml_into_catalog_facts() { + let catalog = parse_fdroid_index_xml(FDROID_FIXTURE).unwrap(); + + assert_eq!(catalog.endpoint.name.as_deref(), Some("F-Droid")); + assert_eq!( + catalog.endpoint.url.as_deref(), + Some("https://f-droid.org/repo") + ); + let app = catalog.app("org.fdroid.fdroid").unwrap(); + assert_eq!(app.name.as_deref(), Some("F-Droid")); + assert_eq!(app.summary.as_deref(), Some("App repository client")); + assert_eq!(app.packages.len(), 2); + assert_eq!(app.packages[0].version, "1.20.0"); + assert_eq!(app.packages[0].version_code, Some(1020000)); + assert_eq!( + app.packages[0].sha256.as_deref(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + assert_eq!(app.packages[0].size, Some(1234567)); + } + + #[test] + fn normalizes_fdroid_releases_to_update_candidates() { + let catalog = parse_fdroid_index_xml(FDROID_FIXTURE).unwrap(); + let app = catalog.app("org.fdroid.fdroid").unwrap(); + + let candidates = app.update_candidates(&catalog.endpoint); + + assert_eq!(candidates.len(), 2); + assert_eq!(candidates[0].version, "1.20.0"); + assert_eq!(candidates[0].version_code, Some(1020000)); + assert_eq!(candidates[0].source.as_deref(), Some("fdroid")); + let artifact = &candidates[0].artifacts[0]; + assert_eq!(artifact.name, "org.fdroid.fdroid_1020000.apk"); + assert_eq!( + artifact.url, + "https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk" + ); + assert_eq!( + artifact.file_name.as_deref(), + Some("org.fdroid.fdroid_1020000.apk") + ); + assert_eq!( + artifact.sha256.as_deref(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + assert_eq!(artifact.size, Some(1234567)); + } + + #[test] + fn malformed_fdroid_index_reports_parse_error() { + let error = parse_fdroid_index_xml("").unwrap_err(); + + assert!(matches!(error, FdroidCatalogError::Xml(_))); + } } From d60cc75ff804edab69a1335a9f52bb205d5896f1 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 04:04:22 +0800 Subject: [PATCH 37/72] feat(operations): cache F-Droid catalog fixtures --- Cargo.lock | 2 + crates/getter-operations/Cargo.toml | 1 + .../getter-operations/src/fdroid_catalog.rs | 336 ++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + crates/getter-providers/Cargo.toml | 1 + crates/getter-providers/src/lib.rs | 9 +- 6 files changed, 346 insertions(+), 4 deletions(-) create mode 100644 crates/getter-operations/src/fdroid_catalog.rs diff --git a/Cargo.lock b/Cargo.lock index c43d98e..039770c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -620,6 +620,7 @@ dependencies = [ "json_comments", "serde", "serde_json", + "sha2", "tempfile", "thiserror 1.0.69", ] @@ -637,6 +638,7 @@ version = "0.1.0" dependencies = [ "getter-core", "roxmltree", + "serde", "thiserror 1.0.69", ] diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 26863cb..ed85e8c 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -14,6 +14,7 @@ getter-providers = { path = "../getter-providers" } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" +sha2 = "0.10" thiserror = "1" [dev-dependencies] diff --git a/crates/getter-operations/src/fdroid_catalog.rs b/crates/getter-operations/src/fdroid_catalog.rs new file mode 100644 index 0000000..3a301f5 --- /dev/null +++ b/crates/getter-operations/src/fdroid_catalog.rs @@ -0,0 +1,336 @@ +//! Fixture-backed F-Droid catalog cache/query operations. +//! +//! This is the first getter-owned F-Droid operation slice after the provider +//! parser. It wires parsed catalog facts through `cache.db` refresh semantics +//! without introducing live HTTP, Flutter-owned provider parsing, or downloader +//! task state. + +use crate::provider_cache::{ + read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, + ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, +}; +use getter_providers::{parse_fdroid_index_xml, FdroidApp, FdroidCatalog, FdroidCatalogError}; +use getter_storage::{CacheDb, StorageError}; +use serde::Deserialize; +use serde_json::{json, Value}; +use sha2::{Digest, Sha512}; + +pub const FDROID_PROVIDER_ID: &str = "fdroid"; +pub const FDROID_CATALOG_CACHE_VERSION: &str = "fdroid-index-v1"; +pub const DEFAULT_FDROID_ENDPOINT_ID: &str = "official"; +pub const DEFAULT_FDROID_ENDPOINT_URL: &str = "https://f-droid.org/repo"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidEndpointConfig { + pub endpoint_id: String, + pub endpoint_url: String, +} + +impl Default for FdroidEndpointConfig { + fn default() -> Self { + Self { + endpoint_id: DEFAULT_FDROID_ENDPOINT_ID.to_owned(), + endpoint_url: DEFAULT_FDROID_ENDPOINT_URL.to_owned(), + } + } +} + +impl FdroidEndpointConfig { + pub fn cache_key(&self) -> String { + format!( + "{FDROID_PROVIDER_ID}:{FDROID_CATALOG_CACHE_VERSION}:{}:{}", + self.endpoint_id, + digest_hex(&self.endpoint_url), + ) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidCatalogResult { + pub endpoint: FdroidEndpointConfig, + pub cache_key: String, + pub catalog: FdroidCatalog, + pub source: ProviderCacheSource, + pub diagnostics: Vec, +} + +impl FdroidCatalogResult { + pub fn app(&self, package_name: &str) -> Option<&FdroidApp> { + self.catalog.app(package_name) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum FdroidCatalogOperationError { + #[error("provider cache operation failed: {0}")] + Cache(#[from] ProviderCacheOperationError), + #[error("storage error: {0}")] + Storage(#[from] StorageError), + #[error("F-Droid catalog parse failed: {0}")] + Catalog(#[from] FdroidCatalogError), + #[error("F-Droid catalog serialization failed: {0}")] + Serialization(#[from] serde_json::Error), + #[error("invalid F-Droid catalog request: {0}")] + InvalidRequest(String), +} + +pub fn read_or_refresh_fdroid_catalog( + db: &CacheDb, + endpoint: FdroidEndpointConfig, + mode: ProviderCacheMode, + refresh_xml: F, +) -> Result +where + F: FnOnce() -> Result, +{ + let cache_key = endpoint.cache_key(); + let cache_result = read_or_refresh_provider_response( + db, + ProviderCacheRequest { + cache_key: &cache_key, + provider: FDROID_PROVIDER_ID, + mode, + }, + || { + let xml = refresh_xml()?; + let catalog = parse_fdroid_index_xml(&xml).map_err(|source| source.to_string())?; + serde_json::to_value(catalog).map_err(|source| source.to_string()) + }, + )?; + let catalog = serde_json::from_value(cache_result.response.response_json)?; + + Ok(FdroidCatalogResult { + endpoint, + cache_key, + catalog, + source: cache_result.source, + diagnostics: cache_result.diagnostics, + }) +} + +pub fn fdroid_catalog_json( + db: &CacheDb, + request_json: &str, +) -> Result { + let request: FdroidCatalogJsonRequest = serde_json::from_str(request_json)?; + let endpoint = FdroidEndpointConfig { + endpoint_id: request + .endpoint_id + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), + endpoint_url: request + .endpoint_url + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), + }; + let mode = match request.mode.as_deref() { + Some("force_refresh") => ProviderCacheMode::ForceRefresh, + Some("use_cached") | None => ProviderCacheMode::UseCached, + Some(other) => { + return Err(FdroidCatalogOperationError::InvalidRequest(format!( + "unknown mode '{other}'" + ))) + } + }; + let result = read_or_refresh_fdroid_catalog(db, endpoint, mode, || { + request + .index_xml + .ok_or_else(|| "fixture-backed F-Droid catalog refresh requires index_xml".to_owned()) + })?; + let package_matches: Vec = request + .package_names + .iter() + .filter_map(|package_name| result.app(package_name)) + .map(serde_json::to_value) + .collect::>()?; + + Ok(json!({ + "operation": "fdroid.catalog", + "provider": FDROID_PROVIDER_ID, + "endpoint_id": result.endpoint.endpoint_id, + "endpoint_url": result.endpoint.endpoint_url, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "catalog": result.catalog, + "matches": package_matches, + "diagnostics": result.diagnostics.iter().map(diagnostic_json).collect::>(), + })) +} + +#[derive(Debug, Deserialize)] +struct FdroidCatalogJsonRequest { + #[serde(default)] + endpoint_id: Option, + #[serde(default)] + endpoint_url: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + index_xml: Option, + #[serde(default)] + package_names: Vec, +} + +fn provider_source_json(source: ProviderCacheSource) -> &'static str { + match source { + ProviderCacheSource::Cache => "cache", + ProviderCacheSource::Refreshed => "refreshed", + ProviderCacheSource::Stale => "stale", + } +} + +fn diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) +} + +fn digest_hex(value: &str) -> String { + let mut hasher = Sha512::new(); + hasher.update(value.as_bytes()); + format!("{:x}", hasher.finalize()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::provider_cache::{CACHE_REFRESH_FAILED, USED_STALE_CACHE}; + use serde_json::json; + use std::cell::Cell; + + const FDROID_FIXTURE: &str = r#" + + + + F-Droid + App repository client + + 1.20.0 + 1020000 + org.fdroid.fdroid_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + +"#; + + fn endpoint() -> FdroidEndpointConfig { + FdroidEndpointConfig::default() + } + + #[test] + fn cache_miss_parses_and_stores_fixture_catalog() { + let db = CacheDb::open_in_memory().unwrap(); + let endpoint = endpoint(); + let cache_key = endpoint.cache_key(); + + let result = + read_or_refresh_fdroid_catalog(&db, endpoint, ProviderCacheMode::UseCached, || { + Ok(FDROID_FIXTURE.to_owned()) + }) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!( + result + .catalog + .app("org.fdroid.fdroid") + .unwrap() + .packages + .len(), + 1 + ); + let cached = db.provider_response(&cache_key).unwrap().unwrap(); + assert_eq!(cached.provider, FDROID_PROVIDER_ID); + assert_eq!( + cached.response_json["apps"][0]["package_name"], + "org.fdroid.fdroid" + ); + } + + #[test] + fn cache_hit_avoids_refreshing_fixture_catalog() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_fdroid_catalog(&db, endpoint(), ProviderCacheMode::UseCached, || { + Ok(FDROID_FIXTURE.to_owned()) + }) + .unwrap(); + let refreshed = Cell::new(false); + + let result = + read_or_refresh_fdroid_catalog(&db, endpoint(), ProviderCacheMode::UseCached, || { + refreshed.set(true); + Err("should not refresh".to_owned()) + }) + .unwrap(); + + assert!(!refreshed.get()); + assert_eq!(result.source, ProviderCacheSource::Cache); + assert_eq!(result.catalog.endpoint.name.as_deref(), Some("F-Droid")); + } + + #[test] + fn forced_refresh_failure_returns_stale_catalog_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_fdroid_catalog(&db, endpoint(), ProviderCacheMode::UseCached, || { + Ok(FDROID_FIXTURE.to_owned()) + }) + .unwrap(); + + let result = read_or_refresh_fdroid_catalog( + &db, + endpoint(), + ProviderCacheMode::ForceRefresh, + || Err("HTTP 503".to_owned()), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!( + result + .catalog + .app("org.fdroid.fdroid") + .unwrap() + .name + .as_deref(), + Some("F-Droid") + ); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + } + + #[test] + fn json_operation_returns_package_name_matches_from_cached_catalog() { + let db = CacheDb::open_in_memory().unwrap(); + let first = fdroid_catalog_json( + &db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid", "missing.package"] + }) + .to_string(), + ) + .unwrap(); + assert_eq!(first["source"], "refreshed"); + + let second = fdroid_catalog_json( + &db, + &json!({ + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(second["source"], "cache"); + assert_eq!(second["matches"].as_array().unwrap().len(), 1); + assert_eq!(second["matches"][0]["package_name"], "org.fdroid.fdroid"); + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index a2e7afe..ffe59eb 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -6,6 +6,7 @@ //! generated package files, manifests, and tracked state live here. pub mod autogen; +pub mod fdroid_catalog; pub mod legacy_room; pub mod provider_cache; pub mod read_model; diff --git a/crates/getter-providers/Cargo.toml b/crates/getter-providers/Cargo.toml index 9424784..31d2fc4 100644 --- a/crates/getter-providers/Cargo.toml +++ b/crates/getter-providers/Cargo.toml @@ -6,4 +6,5 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core", default-features = false } roxmltree = "0.21" +serde = { version = "1", features = ["derive"] } thiserror = "1" diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index 4d3d02a..518d5ba 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -8,6 +8,7 @@ pub use getter_core as core; use getter_core::{ResolvedPackage, UpdateArtifact, UpdateCandidate}; use roxmltree::{Document, Node}; +use serde::{Deserialize, Serialize}; /// Mock provider that returns the static `updates` candidates materialized from /// a resolved Lua package table. @@ -24,7 +25,7 @@ impl StaticPackageUpdatesProvider { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FdroidCatalog { pub endpoint: FdroidEndpoint, pub apps: Vec, @@ -38,14 +39,14 @@ impl FdroidCatalog { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FdroidEndpoint { pub name: Option, pub url: Option, pub timestamp: Option, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FdroidApp { pub package_name: String, pub name: Option, @@ -62,7 +63,7 @@ impl FdroidApp { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FdroidRelease { pub version: String, pub version_code: Option, From de63a6b515aa8292eeee957db8531b08c4cc467a Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 10:24:47 +0800 Subject: [PATCH 38/72] feat(autogen): generate F-Droid package directories --- crates/getter-cli/src/lib.rs | 116 ++- crates/getter-cli/tests/bdd_cli.rs | 134 +++- .../features/cli/autogen_installed.feature | 35 + crates/getter-core/src/autogen.rs | 14 +- crates/getter-operations/src/autogen.rs | 71 +- .../getter-operations/src/fdroid_autogen.rs | 661 ++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + 7 files changed, 1017 insertions(+), 15 deletions(-) create mode 100644 crates/getter-operations/src/fdroid_autogen.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 0cb5e6f..1f3d406 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -22,6 +22,7 @@ use getter_downloader::{ cancel_download_task, record_install_result, run_fake_download_task, submit_fake_download_task, }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; +use getter_operations::fdroid_autogen; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; use getter_operations::runtime as runtime_operations; use getter_storage::legacy_room::{ @@ -126,6 +127,14 @@ pub enum CliCommand { preview: PathBuf, acceptance: AutogenAcceptance, }, + AutogenFdroidPreview { + index: PathBuf, + package_names: Vec, + }, + AutogenFdroidApply { + preview: PathBuf, + acceptance: AutogenAcceptance, + }, LegacyImportRoomBundle { bundle: PathBuf, }, @@ -526,6 +535,26 @@ where inventory: PathBuf::from(inventory), } } + [domain, subject, action, rest @ ..] + if domain == "autogen" && subject == "fdroid" && action == "preview" => + { + let (index, package_names) = parse_fdroid_autogen_preview_args(rest)?; + CliCommand::AutogenFdroidPreview { + index, + package_names, + } + } + [domain, subject, action, flag, preview, rest @ ..] + if domain == "autogen" + && subject == "fdroid" + && action == "apply" + && flag == "--preview" => + { + CliCommand::AutogenFdroidApply { + preview: PathBuf::from(preview), + acceptance: parse_autogen_acceptance(rest)?, + } + } [domain, subject, action, flag, preview, rest @ ..] if domain == "autogen" && subject == "cleanup" @@ -727,6 +756,39 @@ fn execute(invocation: CliInvocation) -> Result { autogen::apply_cleanup_preview(&invocation.data_dir, &db, &preview, &acceptance) .map_err(CliError::from) } + CliCommand::AutogenFdroidPreview { + index, + package_names, + } => { + let db = open_main_db(&invocation.data_dir)?; + let cache_db = open_cache_db(&invocation.data_dir)?; + let index_xml = read_fdroid_index(&index)?; + let request = json!({ + "index_xml": index_xml, + "package_names": package_names, + }); + fdroid_autogen::preview_fdroid_packages_json( + &invocation.data_dir, + &db, + &cache_db, + &request.to_string(), + ) + .map_err(CliError::from) + } + CliCommand::AutogenFdroidApply { + preview, + acceptance, + } => { + let db = open_main_db(&invocation.data_dir)?; + let preview = read_autogen_preview(&preview, "fdroid.autogen.preview")?; + fdroid_autogen::apply_fdroid_preview_json( + &invocation.data_dir, + &db, + &preview, + &acceptance, + ) + .map_err(CliError::from) + } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { @@ -845,6 +907,11 @@ fn open_main_db(data_dir: &Path) -> Result { Ok(MainDb::open(main_db_path(data_dir))?) } +fn open_cache_db(data_dir: &Path) -> Result { + initialize_storage(data_dir)?; + Ok(CacheDb::open(cache_db_path(data_dir))?) +} + fn parse_repository_id(value: &str) -> Result { RepositoryId::new(value).map_err(|source| CliError::Usage(source.to_string())) } @@ -894,6 +961,46 @@ fn parse_priority(value: &str) -> Result { .map_err(|source| CliError::Usage(format!("invalid repository priority: {source}"))) } +fn parse_fdroid_autogen_preview_args(args: &[String]) -> Result<(PathBuf, Vec), CliError> { + let mut index = None; + let mut package_names = Vec::new(); + let mut position = 0; + while position < args.len() { + match args[position].as_str() { + "--index" => { + let path = args.get(position + 1).ok_or_else(|| { + CliError::Usage("autogen fdroid preview --index requires a path".to_owned()) + })?; + index = Some(PathBuf::from(path)); + position += 2; + } + "--package" => { + let package_name = args.get(position + 1).ok_or_else(|| { + CliError::Usage( + "autogen fdroid preview --package requires a package name".to_owned(), + ) + })?; + package_names.push(package_name.clone()); + position += 2; + } + other => { + return Err(CliError::Usage(format!( + "unsupported autogen fdroid preview argument '{other}'" + ))) + } + } + } + let index = index.ok_or_else(|| { + CliError::Usage("autogen fdroid preview requires --index ".to_owned()) + })?; + if package_names.is_empty() { + return Err(CliError::Usage( + "autogen fdroid preview requires at least one --package ".to_owned(), + )); + } + Ok((index, package_names)) +} + fn parse_autogen_acceptance(args: &[String]) -> Result { match args { [flag] if flag == "--accept-all" => Ok(AutogenAcceptance::AcceptAll), @@ -1065,6 +1172,11 @@ fn evaluate_package_directory( .map_err(|error| CliError::PackageEval(error.to_string())) } +fn read_fdroid_index(path: &Path) -> Result { + fs::read_to_string(path) + .map_err(|source| CliError::Autogen(format!("failed to read F-Droid index: {source}"))) +} + fn read_installed_inventory(path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; @@ -1594,7 +1706,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index --package [--package ...]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1661,6 +1773,8 @@ impl CliCommand { Self::AutogenInstalledApply { .. } => "autogen installed apply", Self::AutogenCleanupPreview { .. } => "autogen cleanup preview", Self::AutogenCleanupApply { .. } => "autogen cleanup apply", + Self::AutogenFdroidPreview { .. } => "autogen fdroid preview", + Self::AutogenFdroidApply { .. } => "autogen fdroid apply", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", Self::LegacyImportRoomDb { .. } => "legacy import-room-db", Self::LegacyReportList => "legacy report-list", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 35754d5..5fc72ac 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -15,6 +15,7 @@ struct CliWorld { inventory: Option, autogen_preview: Option, update_fixture: Option, + fdroid_index: Option, task_request: Option, runtime_script: Option, remembered_task_id: Option, @@ -142,6 +143,35 @@ fn installed_inventory_with_android_app(world: &mut CliWorld, package_name: Stri world.inventory = Some(inventory); } +#[given(expr = "a fixture F-Droid catalog index with package {string}")] +fn fixture_fdroid_catalog_index_with_package(world: &mut CliWorld, package_name: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let index = temp.path().join("fdroid-index.xml"); + fs::write( + &index, + format!( + r#" + + + + F-Droid + App repository client + + 1.20.0 + 1020000 + {package_name}_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + +"# + ), + ) + .expect("write F-Droid fixture index"); + world.fdroid_index = Some(index); +} + #[given("an empty installed inventory")] fn empty_installed_inventory(world: &mut CliWorld) { let temp = world.temp.as_ref().expect("tempdir exists"); @@ -824,6 +854,46 @@ fn run_getter_autogen_installed_apply_accept_all(world: &mut CliWorld) { world.json = None; } +#[when(expr = "I run getter autogen fdroid preview for package {string}")] +fn run_getter_autogen_fdroid_preview(world: &mut CliWorld, package_name: String) { + let index = world.fdroid_index.as_ref().expect("F-Droid index exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "fdroid".to_owned(), + "preview".to_owned(), + "--index".to_owned(), + index.to_string_lossy().to_string(), + "--package".to_owned(), + package_name, + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter autogen fdroid apply for that preview with accept-all")] +fn run_getter_autogen_fdroid_apply_accept_all(world: &mut CliWorld) { + let preview = world + .autogen_preview + .as_ref() + .expect("autogen preview exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "fdroid".to_owned(), + "apply".to_owned(), + "--preview".to_owned(), + preview.to_string_lossy().to_string(), + "--accept-all".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen cleanup preview for that inventory")] fn run_getter_autogen_cleanup_preview(world: &mut CliWorld) { let inventory = world.inventory.as_ref().expect("inventory exists"); @@ -924,7 +994,7 @@ fn command_fails_with_autogen_error(world: &mut CliWorld) { assert_eq!(json["ok"], false); assert!(matches!( json["command"].as_str(), - Some("autogen cleanup apply" | "autogen installed apply") + Some("autogen cleanup apply" | "autogen installed apply" | "autogen fdroid apply") )); assert_eq!(json["error"]["code"], "autogen.error"); world.json = Some(json); @@ -1272,6 +1342,22 @@ fn package_eval_name_is(world: &mut CliWorld, package_name: String) { assert_eq!(json["data"]["package"]["name"], package_name); } +#[then(expr = "the package eval contains update version_code {int}")] +fn package_eval_contains_update_version_code(world: &mut CliWorld, version_code: i64) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "package eval"); + let updates = json["data"]["package"]["updates"] + .as_array() + .expect("updates array"); + assert!( + updates + .iter() + .any(|update| update["version_code"].as_i64() == Some(version_code)), + "package eval should contain version_code {version_code}: {updates:?}" + ); +} + #[then(expr = "the pinned package version is {string}")] fn pinned_package_version_is(world: &mut CliWorld, version: String) { let json = current_json(world); @@ -1322,6 +1408,23 @@ fn update_check_has_no_selected_update(world: &mut CliWorld) { assert_eq!(json["data"]["actions"], Value::Array(Vec::new())); } +#[then(expr = "the F-Droid autogen preview contains candidate {string}")] +fn fdroid_autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen fdroid preview"); + assert_eq!(json["data"]["operation"], "fdroid.autogen.preview"); + let candidates = json["data"]["candidates"] + .as_array() + .expect("candidates array"); + assert!( + candidates + .iter() + .any(|candidate| candidate["package_id"].as_str() == Some(package_id.as_str())), + "preview should contain {package_id}: {candidates:?}" + ); +} + #[then(expr = "the autogen preview contains candidate {string}")] fn autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { let json = current_json(world); @@ -1363,6 +1466,15 @@ fn save_autogen_preview_to_file(world: &mut CliWorld) { world.autogen_preview = Some(preview); } +#[then(expr = "the autogen repository contains generated F-Droid package {string}")] +fn autogen_repository_contains_generated_fdroid_package(world: &mut CliWorld, package_id: String) { + autogen_repository_contains_generated_package(world, package_id.clone()); + let path = autogen_repo_path(world).join(package_relative_path(&package_id)); + let content = fs::read_to_string(path.join("9999.lua")).expect("generated package readable"); + assert!(content.contains("return fdroid.package")); + assert!(content.contains("version_code = 1020000")); +} + #[then(expr = "the autogen repository contains generated package {string}")] fn autogen_repository_contains_generated_package(world: &mut CliWorld, package_id: String) { let path = autogen_repo_path(world).join(package_relative_path(&package_id)); @@ -1393,6 +1505,26 @@ fn app_list_contains_autogen_tracked_package(world: &mut CliWorld, package_id: S assert_eq!(app["package_resolution"], "generate_local_package"); } +#[then( + expr = "the F-Droid autogen preview skips package {string} because repository {string} covers it" +)] +fn fdroid_autogen_preview_skips_package_because_repository_covers_it( + world: &mut CliWorld, + package_id: String, + repository_id: String, +) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen fdroid preview"); + let skipped = json["data"]["skipped"].as_array().expect("skipped array"); + let skip = skipped + .iter() + .find(|skip| skip["package_id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| panic!("skipped should contain {package_id}: {skipped:?}")); + assert_eq!(skip["reason"], "covered_by_higher_priority_repo"); + assert_eq!(skip["covering_repo_id"], repository_id); +} + #[then(expr = "the autogen preview skips package {string} because repository {string} covers it")] fn autogen_preview_skips_package_because_repository_covers_it( world: &mut CliWorld, diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 6a0838d..50f6b6a 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -1,5 +1,40 @@ @getter-cli @autogen Feature: Installed app autogen + Scenario: User previews explicit F-Droid package generation without writing files + Given an initialized getter data directory + And a fixture F-Droid catalog index with package "org.fdroid.fdroid" + When I run getter autogen fdroid preview for package "org.fdroid.fdroid" + Then the command succeeds + And the output is valid JSON + And the F-Droid autogen preview contains candidate "android/f-droid/app/org.fdroid.fdroid" + And the autogen repository has not been written + + Scenario: User applies explicit F-Droid autogen and validates the generated package directory + Given an initialized getter data directory + And a fixture F-Droid catalog index with package "org.fdroid.fdroid" + When I run getter autogen fdroid preview for package "org.fdroid.fdroid" + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen fdroid apply for that preview with accept-all + Then the command succeeds + And the autogen repository contains generated F-Droid package "android/f-droid/app/org.fdroid.fdroid" + And the app list contains autogen tracked package "android/f-droid/app/org.fdroid.fdroid" + When I run getter repo validate for autogen + Then the output reports a valid repository without network + When I run getter package eval for package "android/f-droid/app/org.fdroid.fdroid" + Then the command succeeds + And the package eval contains update version_code 1020000 + + Scenario: Higher-priority repositories suppress explicit F-Droid autogen candidates + Given an initialized getter data directory + And a package-directory repository "official" with package "android/f-droid/app/org.fdroid.fdroid" + And a fixture F-Droid catalog index with package "org.fdroid.fdroid" + When I run getter repo add for that repository with priority 0 + Then the command succeeds + When I run getter autogen fdroid preview for package "org.fdroid.fdroid" + Then the command succeeds + And the F-Droid autogen preview skips package "android/f-droid/app/org.fdroid.fdroid" because repository "official" covers it + Scenario: User previews installed app fallback generation without writing files Given an initialized getter data directory And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index 8a30220..6a97c50 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -15,6 +15,7 @@ pub const INSTALLED_INVENTORY_VERSION: u32 = 1; pub const AUTOGEN_RECORD_FILE: &str = ".autogen.jsonc"; pub const AUTOGEN_RECORD_VERSION: u32 = 1; pub const INSTALLED_AUTOGEN_GENERATOR: &str = "installed-inventory"; +pub const FDROID_AUTOGEN_GENERATOR: &str = "fdroid-catalog"; pub const DEFAULT_AUTOGEN_REPOSITORY_ID: &str = "autogen"; pub const DEFAULT_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Autogen"; pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter autogen"; @@ -105,8 +106,17 @@ pub struct AutogenRecord { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "kind", rename_all = "snake_case")] pub enum AutogenRecordInput { - InstalledAndroidPackage { package_name: String }, - InstalledMagiskModule { module_id: String }, + InstalledAndroidPackage { + package_name: String, + }, + InstalledMagiskModule { + module_id: String, + }, + FdroidPackage { + endpoint_id: String, + endpoint_url: String, + package_name: String, + }, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs index 0c0b5f0..0b597f4 100644 --- a/crates/getter-operations/src/autogen.rs +++ b/crates/getter-operations/src/autogen.rs @@ -128,7 +128,12 @@ pub fn cleanup_preview_json( let mut diagnostics = Vec::new(); for package in layout.packages { let relative_path = relative_package_path(&repo_path, &package.path)?; - let ownership = match read_owned_record(&package.path, &package.id, &relative_path) { + let ownership = match read_owned_record( + &package.path, + &package.id, + &relative_path, + INSTALLED_AUTOGEN_GENERATOR, + ) { Ok(ownership) => ownership, Err(error) => { diagnostics.push(autogen_diagnostic( @@ -173,10 +178,33 @@ pub fn apply_installed_preview( preview: &Value, acceptance: &AutogenAcceptance, ) -> AutogenOperationResult { + apply_preview( + data_dir, + db, + preview, + acceptance, + "installed.preview", + INSTALLED_AUTOGEN_GENERATOR, + ) +} + +pub(crate) fn apply_preview( + data_dir: &Path, + db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, + expected_operation: &str, + expected_generator: &str, +) -> AutogenOperationResult { + if preview.get("operation").and_then(Value::as_str) != Some(expected_operation) { + return Err(AutogenOperationError::Autogen(format!( + "autogen preview operation must be '{expected_operation}'" + ))); + } let (target_alias, repo_path, target_priority) = generated_repository_config(data_dir)?; if preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) { return Err(AutogenOperationError::Autogen(format!( - "installed preview target_repo_id must be '{}'", + "{expected_operation} target_repo_id must be '{}'", target_alias.as_str() ))); } @@ -187,10 +215,11 @@ pub fn apply_installed_preview( for candidate in accepted { let package_id = preview_package_id(candidate)?; let relative_path = preview_relative_path(candidate)?; - let payload = preview_candidate_payload(candidate, &package_id, &relative_path)?; + let payload = + preview_candidate_payload(candidate, &package_id, &relative_path, expected_generator)?; let target_dir = safe_join(&repo_path, &relative_path)?; if target_dir.exists() { - read_owned_record(&target_dir, &package_id, &relative_path)?; + read_owned_record(&target_dir, &package_id, &relative_path, expected_generator)?; clear_directory_contents(&target_dir)?; } else { fs::create_dir_all(&target_dir).map_err(|source| { @@ -243,7 +272,12 @@ pub fn apply_cleanup_preview( ) })?; let target_dir = safe_join(&repo_path, &relative_path)?; - let ownership = read_owned_record(&target_dir, &package_id, &relative_path)?; + let ownership = read_owned_record( + &target_dir, + &package_id, + &relative_path, + INSTALLED_AUTOGEN_GENERATOR, + )?; if ownership.content_hash != expected_hash { return Err(AutogenOperationError::Autogen(format!( "cleanup preview candidate {package_id} does not match current autogen record" @@ -286,7 +320,7 @@ pub fn default_autogen_repo_path(data_dir: &Path) -> PathBuf { .repository_path(&RepositoryId::new(DEFAULT_AUTOGEN_REPOSITORY_ID).expect("valid id")) } -fn generated_repository_config( +pub(crate) fn generated_repository_config( data_dir: &Path, ) -> AutogenOperationResult<(RepositoryId, PathBuf, RepositoryPriority)> { let layout = GetterDataDirLayout::new(data_dir); @@ -300,7 +334,7 @@ fn generated_repository_config( Ok((alias, path, priority)) } -fn higher_priority_package_coverage( +pub(crate) fn higher_priority_package_coverage( db: &MainDb, target_alias: &RepositoryId, target_priority: RepositoryPriority, @@ -465,6 +499,7 @@ fn preview_candidate_payload( candidate: &Value, package_id: &PackageId, relative_path: &Path, + expected_generator: &str, ) -> AutogenOperationResult { let record_content = candidate .get("autogen_record_content") @@ -489,7 +524,13 @@ fn preview_candidate_payload( } let files = preview_generated_files(candidate)?; let record = parse_record_content(&record_content)?; - validate_record(&record, package_id, relative_path, &files)?; + validate_record( + &record, + package_id, + relative_path, + &files, + expected_generator, + )?; Ok(PreviewCandidatePayload { record_content, files, @@ -590,6 +631,7 @@ fn read_owned_record( package_dir: &Path, package_id: &PackageId, relative_path: &Path, + expected_generator: &str, ) -> AutogenOperationResult { let record_path = package_dir.join(AUTOGEN_RECORD_FILE); if !record_path.is_file() { @@ -614,7 +656,13 @@ fn read_owned_record( )) })?; let files = read_recorded_files(package_dir, &record)?; - validate_record(&record, package_id, relative_path, &files)?; + validate_record( + &record, + package_id, + relative_path, + &files, + expected_generator, + )?; Ok(LoadedAutogenRecord { content_hash: content_hash_bytes(&bytes), }) @@ -674,6 +722,7 @@ fn validate_record( package_id: &PackageId, relative_path: &Path, files: &[GeneratedPackageFile], + expected_generator: &str, ) -> AutogenOperationResult<()> { if record.version != AUTOGEN_RECORD_VERSION { return Err(AutogenOperationError::Autogen(format!( @@ -681,9 +730,9 @@ fn validate_record( record.version ))); } - if record.generator != INSTALLED_AUTOGEN_GENERATOR { + if record.generator != expected_generator { return Err(AutogenOperationError::Autogen(format!( - "autogen record generator '{}' does not match '{INSTALLED_AUTOGEN_GENERATOR}'", + "autogen record generator '{}' does not match '{expected_generator}'", record.generator ))); } diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs new file mode 100644 index 0000000..2968d1f --- /dev/null +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -0,0 +1,661 @@ +//! Fixture-backed F-Droid explicit autogen preview/apply operations. +//! +//! This consumes getter-owned cached F-Droid catalog facts and writes ordinary +//! generated package directories into the configured generated repository. It is +//! intentionally still fixture/offline friendly: live HTTP, downloader state, +//! installer semantics, and Flutter/Kotlin provider logic remain outside this +//! slice. + +use crate::autogen::{self, AutogenAcceptance, AutogenOperationError, AutogenOperationResult}; +use crate::fdroid_catalog::{ + read_or_refresh_fdroid_catalog, FdroidEndpointConfig, DEFAULT_FDROID_ENDPOINT_ID, + DEFAULT_FDROID_ENDPOINT_URL, +}; +use crate::provider_cache::ProviderCacheMode; +use getter_core::autogen::{ + content_hash, package_relative_path, record_file_key, render_autogen_record, AutogenRecord, + AutogenRecordInput, GeneratedPackageFile, AUTOGEN_RECORD_VERSION, FDROID_AUTOGEN_GENERATOR, +}; +use getter_core::{InstalledTarget, PackageId, PackageKind}; +use getter_providers::{FdroidApp, FdroidRelease}; +use getter_storage::{CacheDb, MainDb}; +use serde::Deserialize; +use serde_json::{json, Value}; +use std::path::{Path, PathBuf}; + +pub fn preview_fdroid_packages_json( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + request_json: &str, +) -> AutogenOperationResult { + let request: FdroidAutogenPreviewRequest = + serde_json::from_str(request_json).map_err(|source| { + AutogenOperationError::Autogen(format!( + "invalid F-Droid autogen preview request: {source}" + )) + })?; + let endpoint = request.endpoint_config(); + let catalog = read_or_refresh_fdroid_catalog(cache_db, endpoint, request.cache_mode()?, || { + request + .index_xml + .ok_or_else(|| "fixture-backed F-Droid autogen refresh requires index_xml".to_owned()) + }) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + let (target_alias, target_path, target_priority) = + autogen::generated_repository_config(data_dir)?; + let covered = + autogen::higher_priority_package_coverage(main_db, &target_alias, target_priority)?; + let mut candidates = Vec::new(); + let mut skipped = Vec::new(); + let mut diagnostics: Vec = catalog + .diagnostics + .iter() + .map(|diagnostic| { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) + }) + .collect(); + + for package_name in unique_requested_packages(&request.package_names) { + let package_id = fdroid_package_id(&package_name)?; + if let Some(repository_id) = covered.get(&package_id) { + skipped.push(json!({ + "package_id": package_id.to_string(), + "reason": "covered_by_higher_priority_repo", + "covering_repo_id": repository_id.as_str(), + })); + continue; + } + let Some(app) = catalog.app(&package_name) else { + diagnostics.push(json!({ + "code": "provider.fdroid.package_not_found", + "message": format!("F-Droid package '{package_name}' was not found in endpoint '{}'", catalog.endpoint.endpoint_id), + "package_id": package_id.to_string(), + "provider": "fdroid", + "endpoint_id": catalog.endpoint.endpoint_id, + })); + skipped.push(json!({ + "package_id": package_id.to_string(), + "reason": "provider_package_not_found", + })); + continue; + }; + let candidate = fdroid_candidate_json( + &catalog.endpoint.endpoint_id, + &catalog.endpoint.endpoint_url, + &package_id, + app, + )?; + candidates.push(candidate); + } + + Ok(json!({ + "operation": "fdroid.autogen.preview", + "provider": "fdroid", + "endpoint_id": catalog.endpoint.endpoint_id, + "endpoint_url": catalog.endpoint.endpoint_url, + "cache_key": catalog.cache_key, + "source": match catalog.source { + crate::provider_cache::ProviderCacheSource::Cache => "cache", + crate::provider_cache::ProviderCacheSource::Refreshed => "refreshed", + crate::provider_cache::ProviderCacheSource::Stale => "stale", + }, + "target_repo_id": target_alias.as_str(), + "target_repo_path": target_path, + "summary": { + "candidate_count": candidates.len(), + "skipped_count": skipped.len(), + "write_count": candidates.len(), + "delete_count": 0, + }, + "candidates": candidates, + "skipped": skipped, + "diagnostics": diagnostics, + })) +} + +pub fn apply_fdroid_preview_json( + data_dir: &Path, + main_db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> AutogenOperationResult { + autogen::apply_preview( + data_dir, + main_db, + preview, + acceptance, + "fdroid.autogen.preview", + FDROID_AUTOGEN_GENERATOR, + ) +} + +#[derive(Debug, Deserialize)] +struct FdroidAutogenPreviewRequest { + #[serde(default)] + endpoint_id: Option, + #[serde(default)] + endpoint_url: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + index_xml: Option, + #[serde(default)] + package_names: Vec, +} + +impl FdroidAutogenPreviewRequest { + fn endpoint_config(&self) -> FdroidEndpointConfig { + FdroidEndpointConfig { + endpoint_id: self + .endpoint_id + .clone() + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), + endpoint_url: self + .endpoint_url + .clone() + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), + } + } + + fn cache_mode(&self) -> AutogenOperationResult { + match self.mode.as_deref() { + Some("force_refresh") => Ok(ProviderCacheMode::ForceRefresh), + Some("use_cached") | None => Ok(ProviderCacheMode::UseCached), + Some(other) => Err(AutogenOperationError::Autogen(format!( + "unknown F-Droid autogen preview mode '{other}'" + ))), + } + } +} + +fn unique_requested_packages(package_names: &[String]) -> Vec { + let mut packages = package_names + .iter() + .map(|package| package.trim()) + .filter(|package| !package.is_empty()) + .map(str::to_owned) + .collect::>(); + packages.sort(); + packages.dedup(); + packages +} + +fn fdroid_package_id(package_name: &str) -> AutogenOperationResult { + PackageId::new(PackageKind::Android, format!("f-droid/app/{package_name}")) + .map_err(|source| AutogenOperationError::Autogen(source.to_string())) +} + +fn fdroid_candidate_json( + endpoint_id: &str, + endpoint_url: &str, + package_id: &PackageId, + app: &FdroidApp, +) -> AutogenOperationResult { + let relative_path = package_relative_path(package_id); + let display_name = app + .name + .as_deref() + .filter(|name| !name.trim().is_empty()) + .unwrap_or(&app.package_name); + let files = fdroid_generated_files(endpoint_url, app)?; + let record = AutogenRecord { + version: AUTOGEN_RECORD_VERSION, + generator: FDROID_AUTOGEN_GENERATOR.to_owned(), + package_id: package_id.clone(), + output_relative_path: relative_path.clone(), + input: AutogenRecordInput::FdroidPackage { + endpoint_id: endpoint_id.to_owned(), + endpoint_url: endpoint_url.to_owned(), + package_name: app.package_name.clone(), + }, + files: files + .iter() + .map(|file| { + ( + record_file_key(&file.relative_path), + file.content_hash.clone(), + ) + }) + .collect(), + }; + let record_content = render_autogen_record(&record) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + let content_hash = content_hash(&record_content); + let file_json: Vec = files + .iter() + .map(|file| { + json!({ + "relative_path": file.relative_path, + "content_hash": file.content_hash, + "content": file.content, + }) + }) + .collect(); + + Ok(json!({ + "package_id": package_id.to_string(), + "kind": package_id.kind().as_str(), + "display_name": display_name, + "installed_target": InstalledTarget::AndroidPackage { package_name: app.package_name.clone() }, + "action": "create", + "output_relative_path": relative_path, + "content_hash": content_hash, + "content": record_content, + "autogen_record_content": record_content, + "files": file_json, + "provider": "fdroid", + "endpoint_id": endpoint_id, + "upstream_id": app.package_name, + })) +} + +fn fdroid_generated_files( + endpoint_url: &str, + app: &FdroidApp, +) -> AutogenOperationResult> { + let metadata = render_pretty_json(json!({ + "type": "android:app", + "android": { "package_name": app.package_name }, + }))?; + let manifest = String::new(); + let version_lua = fdroid_version_lua(endpoint_url, app); + Ok(vec![ + generated_file("metadata.jsonc", metadata), + generated_file("Manifest", manifest), + generated_file("9999.lua", version_lua), + ]) +} + +fn fdroid_version_lua(endpoint_url: &str, app: &FdroidApp) -> String { + let mut releases = app.packages.clone(); + releases.sort_by(|left, right| { + right + .version_code + .cmp(&left.version_code) + .then_with(|| right.version.cmp(&left.version)) + .then_with(|| left.apk_name.cmp(&right.apk_name)) + }); + let updates = releases + .iter() + .map(|release| fdroid_update_lua(endpoint_url, release)) + .collect::>() + .join(",\n"); + format!( + "#!/bin/upa-lua v1\n-- @generated by UpgradeAll getter autogen (F-Droid catalog fixture)\nlocal catalog_package = {{\n package_name = {},\n updates = {{{updates}\n }},\n}}\n\nlocal fdroid = {{}}\nfunction fdroid.package(spec)\n if spec.package_name ~= catalog_package.package_name then\n error(\"F-Droid generated package_name mismatch\")\n end\n return package_version {{ updates = catalog_package.updates }}\nend\n\nreturn fdroid.package {{\n package_name = {},\n}}\n", + lua_string(&app.package_name), + lua_string(&app.package_name), + ) +} + +fn fdroid_update_lua(endpoint_url: &str, release: &FdroidRelease) -> String { + let version_code = release + .version_code + .map(|value| format!("\n version_code = {value},")) + .unwrap_or_default(); + let sha256 = release + .sha256 + .as_deref() + .map(|value| format!("\n sha256 = {},", lua_string(value))) + .unwrap_or_default(); + let size = release + .size + .map(|value| format!("\n size = {value},")) + .unwrap_or_default(); + format!( + "\n {{\n version = {},{version_code}\n source = \"fdroid\",\n artifacts = {{\n {{\n name = {},\n url = {},\n file_name = {},{sha256}{size}\n }},\n }},\n }}", + lua_string(&release.version), + lua_string(&release.apk_name), + lua_string(&fdroid_artifact_url(endpoint_url, &release.apk_name)), + lua_string(&release.apk_name), + ) +} + +fn fdroid_artifact_url(endpoint_url: &str, apk_name: &str) -> String { + format!("{}/{}", endpoint_url.trim_end_matches('/'), apk_name) +} + +fn render_pretty_json(value: Value) -> AutogenOperationResult { + serde_json::to_string_pretty(&value) + .map(|mut content| { + content.push('\n'); + content + }) + .map_err(|source| AutogenOperationError::Autogen(source.to_string())) +} + +fn generated_file(relative_path: &str, content: String) -> GeneratedPackageFile { + GeneratedPackageFile { + relative_path: PathBuf::from(relative_path), + content_hash: content_hash(&content), + content, + } +} + +fn lua_string(value: &str) -> String { + let mut escaped = String::from("\""); + for ch in value.chars() { + match ch { + '\\' => escaped.push_str("\\\\"), + '"' => escaped.push_str("\\\""), + '\n' => escaped.push_str("\\n"), + '\r' => escaped.push_str("\\r"), + '\t' => escaped.push_str("\\t"), + other => escaped.push(other), + } + } + escaped.push('"'); + escaped +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::lua::evaluate_package_directory_script; + use getter_core::repository::{ + RepositoryMetadata, RepositoryPackageDirectoryLayout, REPO_API_VERSION_V1, + }; + use getter_core::RepositoryPriority; + + const FDROID_FIXTURE: &str = r#" + + + + F-Droid + App repository client + + 1.20.0 + 1020000 + org.fdroid.fdroid_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + 1.19.0 + 1019000 + org.fdroid.fdroid_1019000.apk + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + 1111111 + + + + Other + + 2.0 + org.example.other_2.apk + + + +"#; + + #[test] + fn preview_generates_fdroid_package_directory_from_cached_catalog() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid", "missing.package"] + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(preview["operation"], "fdroid.autogen.preview"); + assert_eq!(preview["target_repo_id"], "autogen"); + assert_eq!(preview["source"], "refreshed"); + assert_eq!(preview["candidates"].as_array().unwrap().len(), 1); + let candidate = &preview["candidates"][0]; + assert_eq!( + candidate["package_id"], + "android/f-droid/app/org.fdroid.fdroid" + ); + assert_eq!( + candidate["output_relative_path"], + "android/f-droid/app/org.fdroid.fdroid" + ); + let record: Value = + serde_json::from_str(candidate["autogen_record_content"].as_str().unwrap()).unwrap(); + assert_eq!(record["generator"], "fdroid-catalog"); + assert_eq!(record["input"]["kind"], "fdroid_package"); + assert_eq!(record["input"]["endpoint_id"], "official"); + assert_eq!(record["input"]["endpoint_url"], "https://f-droid.org/repo"); + assert_eq!(record["input"]["package_name"], "org.fdroid.fdroid"); + let files = candidate["files"].as_array().unwrap(); + assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" + && file["content"] + .as_str() + .unwrap() + .contains("version_code = 1020000"))); + assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" + && file["content"] + .as_str() + .unwrap() + .contains("return fdroid.package"))); + assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" + && file["content"] + .as_str() + .unwrap() + .contains("package_name = \"org.fdroid.fdroid\""))); + assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" + && file["content"] + .as_str() + .unwrap() + .contains("https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk"))); + assert!(files.iter().any(|file| file["relative_path"] == "Manifest" + && file["content"].as_str().unwrap().is_empty())); + assert_eq!( + preview["skipped"][0]["reason"], + "provider_package_not_found" + ); + assert_eq!( + preview["diagnostics"][0]["code"], + "provider.fdroid.package_not_found" + ); + assert!(!temp.path().join("repo/autogen").exists()); + } + + #[test] + fn preview_skips_fdroid_package_covered_by_higher_priority_repo() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let official_root = temp.path().join("repo/official"); + let package_dir = official_root.join("android/f-droid/app/org.fdroid.fdroid"); + std::fs::create_dir_all(&package_dir).unwrap(); + std::fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .unwrap(); + main_db + .upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::DEFAULT, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&official_root), + None, + ) + .unwrap(); + + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + + assert!(preview["candidates"].as_array().unwrap().is_empty()); + assert_eq!( + preview["skipped"][0]["package_id"], + "android/f-droid/app/org.fdroid.fdroid" + ); + assert_eq!( + preview["skipped"][0]["reason"], + "covered_by_higher_priority_repo" + ); + assert_eq!(preview["skipped"][0]["covering_repo_id"], "official"); + } + + #[test] + fn apply_writes_valid_fdroid_package_directory() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + + let result = apply_fdroid_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap(); + + assert_eq!(result["applied_count"], 1); + let repo_root = temp.path().join("repo/autogen"); + let package_dir = repo_root.join("android/f-droid/app/org.fdroid.fdroid"); + assert!(package_dir.join("metadata.jsonc").is_file()); + assert!(package_dir.join("Manifest").is_file()); + assert!(package_dir.join("9999.lua").is_file()); + assert!(package_dir.join(".autogen.jsonc").is_file()); + let layout = RepositoryPackageDirectoryLayout::load(&repo_root).unwrap(); + let package_dir = layout + .package(&"android/f-droid/app/org.fdroid.fdroid".parse().unwrap()) + .unwrap(); + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + let package = evaluate_package_directory_script( + &"autogen".parse().unwrap(), + package_dir, + &metadata, + script, + ) + .unwrap(); + assert_eq!(package.name, "android/f-droid/app/org.fdroid.fdroid"); + assert_eq!(package.installed.len(), 1); + assert_eq!(package.updates.len(), 2); + assert_eq!(package.updates[0].version_code, Some(1020000)); + assert_eq!( + package.updates[0].artifacts[0].sha256.as_deref(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + } + + #[test] + fn apply_rejects_existing_fdroid_directory_owned_by_another_generator() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + apply_fdroid_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap(); + let record_path = temp + .path() + .join("repo/autogen/android/f-droid/app/org.fdroid.fdroid/.autogen.jsonc"); + let mut record: Value = + serde_json::from_str(&std::fs::read_to_string(&record_path).unwrap()) + .expect("record JSON parses"); + record["generator"] = Value::String("installed-inventory".to_owned()); + std::fs::write( + &record_path, + serde_json::to_string_pretty(&record).unwrap() + "\n", + ) + .unwrap(); + + let error = apply_fdroid_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("does not match 'fdroid-catalog'")) + ); + } + + #[test] + fn apply_rejects_existing_fdroid_directory_without_ownership() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let existing = temp + .path() + .join("repo/autogen/android/f-droid/app/org.fdroid.fdroid"); + std::fs::create_dir_all(&existing).unwrap(); + std::fs::write( + existing.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .unwrap(); + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + + let error = apply_fdroid_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("missing .autogen.jsonc")) + ); + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index ffe59eb..ba9281b 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -6,6 +6,7 @@ //! generated package files, manifests, and tracked state live here. pub mod autogen; +pub mod fdroid_autogen; pub mod fdroid_catalog; pub mod legacy_room; pub mod provider_cache; From 6d91ce50f3c5ae41a70633f7a1798cbf64c564e3 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 11:25:32 +0800 Subject: [PATCH 39/72] feat(autogen): match F-Droid installed inventory --- crates/getter-cli/src/lib.rs | 33 +++++-- crates/getter-cli/tests/bdd_cli.rs | 20 +++++ .../features/cli/autogen_installed.feature | 10 +++ .../getter-operations/src/fdroid_autogen.rs | 88 +++++++++++++++++-- 4 files changed, 138 insertions(+), 13 deletions(-) diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 1f3d406..730767b 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -129,6 +129,7 @@ pub enum CliCommand { }, AutogenFdroidPreview { index: PathBuf, + inventory: Option, package_names: Vec, }, AutogenFdroidApply { @@ -538,9 +539,10 @@ where [domain, subject, action, rest @ ..] if domain == "autogen" && subject == "fdroid" && action == "preview" => { - let (index, package_names) = parse_fdroid_autogen_preview_args(rest)?; + let (index, inventory, package_names) = parse_fdroid_autogen_preview_args(rest)?; CliCommand::AutogenFdroidPreview { index, + inventory, package_names, } } @@ -758,14 +760,20 @@ fn execute(invocation: CliInvocation) -> Result { } CliCommand::AutogenFdroidPreview { index, + inventory, package_names, } => { let db = open_main_db(&invocation.data_dir)?; let cache_db = open_cache_db(&invocation.data_dir)?; let index_xml = read_fdroid_index(&index)?; + let installed_inventory = inventory + .as_deref() + .map(read_installed_inventory) + .transpose()?; let request = json!({ "index_xml": index_xml, "package_names": package_names, + "installed_inventory": installed_inventory, }); fdroid_autogen::preview_fdroid_packages_json( &invocation.data_dir, @@ -961,8 +969,11 @@ fn parse_priority(value: &str) -> Result { .map_err(|source| CliError::Usage(format!("invalid repository priority: {source}"))) } -fn parse_fdroid_autogen_preview_args(args: &[String]) -> Result<(PathBuf, Vec), CliError> { +fn parse_fdroid_autogen_preview_args( + args: &[String], +) -> Result<(PathBuf, Option, Vec), CliError> { let mut index = None; + let mut inventory = None; let mut package_names = Vec::new(); let mut position = 0; while position < args.len() { @@ -974,6 +985,15 @@ fn parse_fdroid_autogen_preview_args(args: &[String]) -> Result<(PathBuf, Vec { + let path = args.get(position + 1).ok_or_else(|| { + CliError::Usage( + "autogen fdroid preview --inventory requires an inventory path".to_owned(), + ) + })?; + inventory = Some(PathBuf::from(path)); + position += 2; + } "--package" => { let package_name = args.get(position + 1).ok_or_else(|| { CliError::Usage( @@ -993,12 +1013,13 @@ fn parse_fdroid_autogen_preview_args(args: &[String]) -> Result<(PathBuf, Vec".to_owned()) })?; - if package_names.is_empty() { + if package_names.is_empty() && inventory.is_none() { return Err(CliError::Usage( - "autogen fdroid preview requires at least one --package ".to_owned(), + "autogen fdroid preview requires --package or --inventory " + .to_owned(), )); } - Ok((index, package_names)) + Ok((index, inventory, package_names)) } fn parse_autogen_acceptance(args: &[String]) -> Result { @@ -1706,7 +1727,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index --package [--package ...]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 5fc72ac..6e82d68 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -873,6 +873,26 @@ fn run_getter_autogen_fdroid_preview(world: &mut CliWorld, package_name: String) world.json = None; } +#[when("I run getter autogen fdroid preview for that inventory")] +fn run_getter_autogen_fdroid_preview_for_inventory(world: &mut CliWorld) { + let index = world.fdroid_index.as_ref().expect("F-Droid index exists"); + let inventory = world.inventory.as_ref().expect("inventory exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "fdroid".to_owned(), + "preview".to_owned(), + "--index".to_owned(), + index.to_string_lossy().to_string(), + "--inventory".to_owned(), + inventory.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen fdroid apply for that preview with accept-all")] fn run_getter_autogen_fdroid_apply_accept_all(world: &mut CliWorld) { let preview = world diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 50f6b6a..26ce9c3 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -25,6 +25,16 @@ Feature: Installed app autogen Then the command succeeds And the package eval contains update version_code 1020000 + Scenario: User previews F-Droid autogen from installed inventory + Given an initialized getter data directory + And a fixture F-Droid catalog index with package "org.fdroid.fdroid" + And an installed inventory with Android app "org.fdroid.fdroid" labeled "F-Droid" + When I run getter autogen fdroid preview for that inventory + Then the command succeeds + And the output is valid JSON + And the F-Droid autogen preview contains candidate "android/f-droid/app/org.fdroid.fdroid" + And the autogen repository has not been written + Scenario: Higher-priority repositories suppress explicit F-Droid autogen candidates Given an initialized getter data directory And a package-directory repository "official" with package "android/f-droid/app/org.fdroid.fdroid" diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs index 2968d1f..3026de4 100644 --- a/crates/getter-operations/src/fdroid_autogen.rs +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -13,8 +13,9 @@ use crate::fdroid_catalog::{ }; use crate::provider_cache::ProviderCacheMode; use getter_core::autogen::{ - content_hash, package_relative_path, record_file_key, render_autogen_record, AutogenRecord, - AutogenRecordInput, GeneratedPackageFile, AUTOGEN_RECORD_VERSION, FDROID_AUTOGEN_GENERATOR, + content_hash, package_relative_path, record_file_key, render_autogen_record, + validate_installed_inventory, AutogenRecord, AutogenRecordInput, GeneratedPackageFile, + InstalledInventory, InstalledInventoryItem, AUTOGEN_RECORD_VERSION, FDROID_AUTOGEN_GENERATOR, }; use getter_core::{InstalledTarget, PackageId, PackageKind}; use getter_providers::{FdroidApp, FdroidRelease}; @@ -36,7 +37,9 @@ pub fn preview_fdroid_packages_json( )) })?; let endpoint = request.endpoint_config(); - let catalog = read_or_refresh_fdroid_catalog(cache_db, endpoint, request.cache_mode()?, || { + let mode = request.cache_mode()?; + let requested_package_names = requested_fdroid_package_names(&request)?; + let catalog = read_or_refresh_fdroid_catalog(cache_db, endpoint, mode, || { request .index_xml .ok_or_else(|| "fixture-backed F-Droid autogen refresh requires index_xml".to_owned()) @@ -62,7 +65,7 @@ pub fn preview_fdroid_packages_json( }) .collect(); - for package_name in unique_requested_packages(&request.package_names) { + for package_name in requested_package_names { let package_id = fdroid_package_id(&package_name)?; if let Some(repository_id) = covered.get(&package_id) { skipped.push(json!({ @@ -148,6 +151,8 @@ struct FdroidAutogenPreviewRequest { index_xml: Option, #[serde(default)] package_names: Vec, + #[serde(default)] + installed_inventory: Option, } impl FdroidAutogenPreviewRequest { @@ -175,16 +180,32 @@ impl FdroidAutogenPreviewRequest { } } -fn unique_requested_packages(package_names: &[String]) -> Vec { - let mut packages = package_names +fn requested_fdroid_package_names( + request: &FdroidAutogenPreviewRequest, +) -> AutogenOperationResult> { + if let Some(inventory) = &request.installed_inventory { + validate_installed_inventory(inventory) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + } + let mut packages = request + .package_names .iter() .map(|package| package.trim()) .filter(|package| !package.is_empty()) .map(str::to_owned) .collect::>(); + if let Some(inventory) = &request.installed_inventory { + packages.extend(inventory.items.iter().filter_map(|item| match item { + InstalledInventoryItem::AndroidPackage { package_name, .. } => { + let package_name = package_name.trim(); + (!package_name.is_empty()).then(|| package_name.to_owned()) + } + InstalledInventoryItem::MagiskModule { .. } => None, + })); + } packages.sort(); packages.dedup(); - packages + Ok(packages) } fn fdroid_package_id(package_name: &str) -> AutogenOperationResult { @@ -658,4 +679,57 @@ mod tests { matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("missing .autogen.jsonc")) ); } + + #[test] + fn preview_matches_installed_android_inventory_against_fdroid_catalog() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "installed_inventory": { + "format": "upgradeall-installed-inventory", + "version": 1, + "items": [ + { + "kind": "android", + "package_name": "org.fdroid.fdroid", + "label": "F-Droid" + }, + { + "kind": "android", + "package_name": "missing.package", + "label": "Missing" + }, + { + "kind": "magisk", + "module_id": "zygisk-next" + } + ] + } + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(preview["operation"], "fdroid.autogen.preview"); + assert_eq!(preview["candidates"].as_array().unwrap().len(), 1); + assert_eq!( + preview["candidates"][0]["package_id"], + "android/f-droid/app/org.fdroid.fdroid" + ); + assert_eq!( + preview["skipped"][0]["package_id"], + "android/f-droid/app/missing.package" + ); + assert_eq!( + preview["skipped"][0]["reason"], + "provider_package_not_found" + ); + } } From 9001bb93b23245c7b2347a722157fffd18f64ca6 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 12:31:03 +0800 Subject: [PATCH 40/72] feat(providers): add GitHub release fixtures --- Cargo.lock | 2 + crates/getter-cli/src/lib.rs | 189 +++++++- crates/getter-cli/tests/bdd_cli.rs | 123 +++++ .../cli/provider_github_releases.feature | 10 + .../getter-operations/src/github_releases.rs | 438 ++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + crates/getter-providers/Cargo.toml | 2 + crates/getter-providers/src/lib.rs | 309 ++++++++++++ 8 files changed, 1071 insertions(+), 3 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/provider_github_releases.feature create mode 100644 crates/getter-operations/src/github_releases.rs diff --git a/Cargo.lock b/Cargo.lock index 039770c..c072fcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -637,8 +637,10 @@ name = "getter-providers" version = "0.1.0" dependencies = [ "getter-core", + "regex", "roxmltree", "serde", + "serde_json", "thiserror 1.0.69", ] diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 730767b..8306d7c 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -23,6 +23,7 @@ use getter_downloader::{ }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; use getter_operations::fdroid_autogen; +use getter_operations::github_releases::{self, GithubReleaseOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; use getter_operations::runtime as runtime_operations; use getter_storage::legacy_room::{ @@ -136,6 +137,15 @@ pub enum CliCommand { preview: PathBuf, acceptance: AutogenAcceptance, }, + ProviderGithubReleases { + owner: String, + repo: String, + releases: PathBuf, + asset_include: Option, + asset_exclude: Option, + include_prereleases: bool, + refresh: bool, + }, LegacyImportRoomBundle { bundle: PathBuf, }, @@ -152,6 +162,7 @@ pub enum ExitCode { Usage = 2, Storage = 10, Migration = 20, + Provider = 30, Download = 40, } @@ -179,6 +190,8 @@ pub enum CliError { Runtime(String), #[error("autogen error: {0}")] Autogen(String), + #[error("provider error: {0}")] + Provider(String), #[error("Legacy Room export bundle is invalid")] InvalidLegacyBundle { report_path: PathBuf }, #[error("Legacy Room bundle import is not implemented yet")] @@ -199,6 +212,7 @@ impl CliError { | Self::Update(_) | Self::Runtime(_) | Self::Autogen(_) => ExitCode::GenericFailure, + Self::Provider(_) => ExitCode::Provider, Self::Download(_) => ExitCode::Download, Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } @@ -217,6 +231,7 @@ impl CliError { Self::Download(_) => "download.task_error", Self::Runtime(_) => "runtime.error", Self::Autogen(_) => "autogen.error", + Self::Provider(_) => "provider.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", Self::InvalidLegacyDb { .. } => "migration.invalid_db", @@ -234,6 +249,7 @@ impl CliError { Self::Download(_) => "Getter download task operation failed", Self::Runtime(_) => "Getter runtime operation failed", Self::Autogen(_) => "Getter autogen operation failed", + Self::Provider(_) => "Getter provider operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { "Legacy Room bundle import is not implemented yet" @@ -252,7 +268,8 @@ impl CliError { | Self::Update(detail) | Self::Download(detail) | Self::Runtime(detail) - | Self::Autogen(detail) => Some(detail.as_str()), + | Self::Autogen(detail) + | Self::Provider(detail) => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } | Self::InvalidLegacyDb { .. } @@ -273,7 +290,8 @@ impl CliError { | Self::Update(_) | Self::Download(_) | Self::Runtime(_) - | Self::Autogen(_) => None, + | Self::Autogen(_) + | Self::Provider(_) => None, } } } @@ -311,6 +329,16 @@ impl From for CliError { } } +impl From for CliError { + fn from(value: GithubReleaseOperationError) -> Self { + match value { + GithubReleaseOperationError::Storage(source) => Self::Storage(source.to_string()), + GithubReleaseOperationError::InvalidRequest(detail) => Self::Usage(detail), + other => Self::Provider(other.to_string()), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliOutput { pub exit_code: ExitCode, @@ -557,6 +585,20 @@ where acceptance: parse_autogen_acceptance(rest)?, } } + [domain, provider, action, rest @ ..] + if domain == "provider" && provider == "github" && action == "releases" => + { + let args = parse_provider_github_releases_args(rest)?; + CliCommand::ProviderGithubReleases { + owner: args.owner, + repo: args.repo, + releases: args.releases, + asset_include: args.asset_include, + asset_exclude: args.asset_exclude, + include_prereleases: args.include_prereleases, + refresh: args.refresh, + } + } [domain, subject, action, flag, preview, rest @ ..] if domain == "autogen" && subject == "cleanup" @@ -797,6 +839,30 @@ fn execute(invocation: CliInvocation) -> Result { ) .map_err(CliError::from) } + CliCommand::ProviderGithubReleases { + owner, + repo, + releases, + asset_include, + asset_exclude, + include_prereleases, + refresh, + } => { + let db = open_cache_db(&invocation.data_dir)?; + let releases_json = read_github_releases_fixture(&releases)?; + let request = json!({ + "owner": owner, + "repo": repo, + "mode": if refresh { "force_refresh" } else { "use_cached" }, + "releases_json": releases_json, + "include_prereleases": include_prereleases, + "asset": { + "include": asset_include, + "exclude": asset_exclude, + }, + }); + github_releases::github_releases_json(&db, &request.to_string()).map_err(CliError::from) + } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { @@ -1022,6 +1088,116 @@ fn parse_fdroid_autogen_preview_args( Ok((index, inventory, package_names)) } +#[derive(Debug, Default, PartialEq, Eq)] +struct ProviderGithubReleasesArgs { + owner: String, + repo: String, + releases: PathBuf, + asset_include: Option, + asset_exclude: Option, + include_prereleases: bool, + refresh: bool, +} + +fn parse_provider_github_releases_args( + args: &[String], +) -> Result { + let mut parsed = ProviderGithubReleasesArgs::default(); + let mut position = 0; + while position < args.len() { + match args[position].as_str() { + "--owner" => { + parsed.owner = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github releases --owner requires an owner".to_owned(), + ) + })? + .clone(); + position += 2; + } + "--repo" => { + parsed.repo = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github releases --repo requires a repo".to_owned(), + ) + })? + .clone(); + position += 2; + } + "--releases" => { + let path = args.get(position + 1).ok_or_else(|| { + CliError::Usage( + "provider github releases --releases requires a fixture path".to_owned(), + ) + })?; + parsed.releases = PathBuf::from(path); + position += 2; + } + "--asset-include" => { + parsed.asset_include = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github releases --asset-include requires a regex" + .to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--asset-exclude" => { + parsed.asset_exclude = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github releases --asset-exclude requires a regex" + .to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--include-prereleases" => { + parsed.include_prereleases = true; + position += 1; + } + "--refresh" => { + parsed.refresh = true; + position += 1; + } + other => { + return Err(CliError::Usage(format!( + "unsupported provider github releases argument '{other}'" + ))) + } + } + } + + if parsed.owner.trim().is_empty() { + return Err(CliError::Usage( + "provider github releases requires --owner ".to_owned(), + )); + } + if parsed.repo.trim().is_empty() { + return Err(CliError::Usage( + "provider github releases requires --repo ".to_owned(), + )); + } + if parsed.releases.as_os_str().is_empty() { + return Err(CliError::Usage( + "provider github releases requires --releases ".to_owned(), + )); + } + + Ok(parsed) +} + fn parse_autogen_acceptance(args: &[String]) -> Result { match args { [flag] if flag == "--accept-all" => Ok(AutogenAcceptance::AcceptAll), @@ -1198,6 +1374,12 @@ fn read_fdroid_index(path: &Path) -> Result { .map_err(|source| CliError::Autogen(format!("failed to read F-Droid index: {source}"))) } +fn read_github_releases_fixture(path: &Path) -> Result { + fs::read_to_string(path).map_err(|source| { + CliError::Provider(format!("failed to read GitHub releases fixture: {source}")) + }) +} + fn read_installed_inventory(path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; @@ -1727,7 +1909,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1796,6 +1978,7 @@ impl CliCommand { Self::AutogenCleanupApply { .. } => "autogen cleanup apply", Self::AutogenFdroidPreview { .. } => "autogen fdroid preview", Self::AutogenFdroidApply { .. } => "autogen fdroid apply", + Self::ProviderGithubReleases { .. } => "provider github releases", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", Self::LegacyImportRoomDb { .. } => "legacy import-room-db", Self::LegacyReportList => "legacy report-list", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 6e82d68..54b584a 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -16,6 +16,7 @@ struct CliWorld { autogen_preview: Option, update_fixture: Option, fdroid_index: Option, + github_releases: Option, task_request: Option, runtime_script: Option, remembered_task_id: Option, @@ -172,6 +173,73 @@ fn fixture_fdroid_catalog_index_with_package(world: &mut CliWorld, package_name: world.fdroid_index = Some(index); } +#[given(expr = "a fixture GitHub releases response for {string}")] +fn fixture_github_releases_response(world: &mut CliWorld, project: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let releases = temp.path().join("github-releases.json"); + fs::write( + &releases, + serde_json::to_vec_pretty(&serde_json::json!([ + { + "tag_name": "v1.2.0", + "name": format!("{project} 1.2.0"), + "body": "Release notes", + "draft": false, + "prerelease": false, + "published_at": "2026-06-01T00:00:00Z", + "assets": [ + { + "name": "app-release.apk", + "content_type": "application/vnd.android.package-archive", + "size": 1234, + "browser_download_url": "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.2.0/app-release.apk", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "name": "app-debug.apk", + "content_type": "application/vnd.android.package-archive", + "size": 2345, + "browser_download_url": "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.2.0/app-debug.apk" + }, + { + "name": "notes.txt", + "content_type": "text/plain", + "size": 345, + "browser_download_url": "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.2.0/notes.txt" + } + ] + }, + { + "tag_name": "v1.3.0-beta1", + "draft": false, + "prerelease": true, + "assets": [ + { + "name": "app-beta.apk", + "size": 456, + "browser_download_url": "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.3.0-beta1/app-beta.apk" + } + ] + }, + { + "tag_name": "v1.4.0-draft", + "draft": true, + "prerelease": false, + "assets": [ + { + "name": "app-draft.apk", + "size": 567, + "browser_download_url": "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.4.0-draft/app-draft.apk" + } + ] + } + ])) + .expect("GitHub releases serializes"), + ) + .expect("write GitHub releases fixture"); + world.github_releases = Some(releases); +} + #[given("an empty installed inventory")] fn empty_installed_inventory(world: &mut CliWorld) { let temp = world.temp.as_ref().expect("tempdir exists"); @@ -893,6 +961,34 @@ fn run_getter_autogen_fdroid_preview_for_inventory(world: &mut CliWorld) { world.json = None; } +#[when(expr = "I run getter provider github releases for owner {string} repo {string}")] +fn run_getter_provider_github_releases(world: &mut CliWorld, owner: String, repo: String) { + let releases = world + .github_releases + .as_ref() + .expect("GitHub releases fixture exists"); + let output = run_getter( + world, + [ + "provider".to_owned(), + "github".to_owned(), + "releases".to_owned(), + "--owner".to_owned(), + owner, + "--repo".to_owned(), + repo, + "--releases".to_owned(), + releases.to_string_lossy().to_string(), + "--asset-include".to_owned(), + r"\.apk$".to_owned(), + "--asset-exclude".to_owned(), + "debug".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen fdroid apply for that preview with accept-all")] fn run_getter_autogen_fdroid_apply_accept_all(world: &mut CliWorld) { let preview = world @@ -1486,6 +1582,33 @@ fn save_autogen_preview_to_file(world: &mut CliWorld) { world.autogen_preview = Some(preview); } +#[then(expr = "the GitHub release provider returns candidate {string} with artifact {string}")] +fn github_release_provider_returns_candidate( + world: &mut CliWorld, + version: String, + artifact_name: String, +) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "provider github releases"); + assert_eq!(json["data"]["operation"], "github.releases"); + assert_eq!(json["data"]["provider"], "github"); + assert_eq!(json["data"]["source"], "refreshed"); + let candidates = json["data"]["candidates"] + .as_array() + .expect("candidates array"); + assert_eq!(candidates.len(), 1); + assert_eq!(candidates[0]["version"], version); + assert_eq!(candidates[0]["source"], "github"); + assert_eq!(candidates[0]["artifacts"][0]["name"], artifact_name); + assert_eq!(candidates[0]["artifacts"][0]["file_name"], artifact_name); + assert_eq!( + candidates[0]["artifacts"][0]["url"], + "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.2.0/app-release.apk" + ); + assert!(json["data"]["diagnostics"].as_array().unwrap().is_empty()); +} + #[then(expr = "the autogen repository contains generated F-Droid package {string}")] fn autogen_repository_contains_generated_fdroid_package(world: &mut CliWorld, package_id: String) { autogen_repository_contains_generated_package(world, package_id.clone()); diff --git a/crates/getter-cli/tests/features/cli/provider_github_releases.feature b/crates/getter-cli/tests/features/cli/provider_github_releases.feature new file mode 100644 index 0000000..c0e6a81 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/provider_github_releases.feature @@ -0,0 +1,10 @@ +@getter-cli @provider @github +Feature: GitHub release provider fixtures + Scenario: User queries fixture-backed GitHub releases without writing generated packages + Given an initialized getter data directory + And a fixture GitHub releases response for "DUpdateSystem/UpgradeAll" + When I run getter provider github releases for owner "DUpdateSystem" repo "UpgradeAll" + Then the command succeeds + And the output is valid JSON + And the GitHub release provider returns candidate "v1.2.0" with artifact "app-release.apk" + And the autogen repository has not been written diff --git a/crates/getter-operations/src/github_releases.rs b/crates/getter-operations/src/github_releases.rs new file mode 100644 index 0000000..9f151c2 --- /dev/null +++ b/crates/getter-operations/src/github_releases.rs @@ -0,0 +1,438 @@ +//! Fixture-backed GitHub release cache/query operations. +//! +//! This is the first getter-owned GitHub provider slice. It parses controlled +//! GitHub REST release fixtures, runs them through the shared provider cache, +//! and normalizes release assets into getter update candidates without adding +//! live HTTP, Flutter/Kotlin provider parsing, downloader, or installer logic. + +use crate::provider_cache::{ + read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, + ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, +}; +use getter_providers::{ + github_release_update_candidates, parse_github_releases_json, GithubAssetFilter, + GithubProviderError, GithubRelease, GithubReleaseCandidateOptions, +}; +use getter_storage::{CacheDb, StorageError}; +use serde::Deserialize; +use serde_json::{json, Value}; +use sha2::{Digest, Sha512}; + +pub const GITHUB_PROVIDER_ID: &str = "github"; +pub const GITHUB_RELEASES_CACHE_VERSION: &str = "github-releases-v1"; +pub const DEFAULT_GITHUB_API_BASE_URL: &str = "https://api.github.com"; +pub const GITHUB_ASSET_NOT_FOUND: &str = "provider.github.asset_not_found"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GithubReleaseConfig { + pub api_base_url: String, + pub owner: String, + pub repo: String, +} + +impl GithubReleaseConfig { + pub fn cache_key(&self) -> String { + format!( + "{GITHUB_PROVIDER_ID}:{GITHUB_RELEASES_CACHE_VERSION}:releases:{}:{}/{}", + digest_hex(&self.api_base_url), + self.owner, + self.repo, + ) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GithubReleaseResult { + pub config: GithubReleaseConfig, + pub cache_key: String, + pub releases: Vec, + pub source: ProviderCacheSource, + pub diagnostics: Vec, +} + +#[derive(Debug, thiserror::Error)] +pub enum GithubReleaseOperationError { + #[error("provider cache operation failed: {0}")] + Cache(#[from] ProviderCacheOperationError), + #[error("storage error: {0}")] + Storage(#[from] StorageError), + #[error("GitHub provider parse failed: {0}")] + Provider(#[from] GithubProviderError), + #[error("GitHub provider serialization failed: {0}")] + Serialization(#[from] serde_json::Error), + #[error("invalid GitHub release request: {0}")] + InvalidRequest(String), +} + +pub fn read_or_refresh_github_releases( + db: &CacheDb, + config: GithubReleaseConfig, + mode: ProviderCacheMode, + refresh_json: F, +) -> Result +where + F: FnOnce() -> Result, +{ + let cache_key = config.cache_key(); + let cache_result = read_or_refresh_provider_response( + db, + ProviderCacheRequest { + cache_key: &cache_key, + provider: GITHUB_PROVIDER_ID, + mode, + }, + || { + let json = refresh_json()?; + let releases = + parse_github_releases_json(&json).map_err(|source| source.to_string())?; + serde_json::to_value(releases).map_err(|source| source.to_string()) + }, + )?; + let releases = serde_json::from_value(cache_result.response.response_json)?; + + Ok(GithubReleaseResult { + config, + cache_key, + releases, + source: cache_result.source, + diagnostics: cache_result.diagnostics, + }) +} + +pub fn github_releases_json( + db: &CacheDb, + request_json: &str, +) -> Result { + let request: GithubReleasesJsonRequest = serde_json::from_str(request_json)?; + let owner = required_non_empty(request.owner, "owner")?; + let repo = required_non_empty(request.repo, "repo")?; + let config = GithubReleaseConfig { + api_base_url: request + .api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()), + owner, + repo, + }; + let mode = match request.mode.as_deref() { + Some("force_refresh") => ProviderCacheMode::ForceRefresh, + Some("use_cached") | None => ProviderCacheMode::UseCached, + Some(other) => { + return Err(GithubReleaseOperationError::InvalidRequest(format!( + "unknown mode '{other}'" + ))) + } + }; + let asset_filter = request.asset.unwrap_or_default(); + let options = GithubReleaseCandidateOptions { + include_prereleases: request.include_prereleases, + asset_filter: GithubAssetFilter { + include: asset_filter.include, + exclude: asset_filter.exclude, + }, + }; + let result = read_or_refresh_github_releases(db, config, mode, || { + request.releases_json.ok_or_else(|| { + "fixture-backed GitHub release refresh requires releases_json".to_owned() + }) + })?; + let candidates = github_release_update_candidates(&result.releases, &options)?; + let mut diagnostics = result + .diagnostics + .iter() + .map(diagnostic_json) + .collect::>(); + if candidates.is_empty() && github_eligible_release_count(&result.releases, &options) > 0 { + diagnostics.push(json!({ + "code": GITHUB_ASSET_NOT_FOUND, + "message": "no GitHub release assets matched the requested filters", + "provider": GITHUB_PROVIDER_ID, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + })); + } + + Ok(json!({ + "operation": "github.releases", + "provider": GITHUB_PROVIDER_ID, + "api_base_url": result.config.api_base_url, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "releases": result.releases, + "candidates": candidates, + "diagnostics": diagnostics, + })) +} + +#[derive(Debug, Deserialize)] +struct GithubReleasesJsonRequest { + #[serde(default)] + api_base_url: Option, + #[serde(default)] + owner: Option, + #[serde(default)] + repo: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + releases_json: Option, + #[serde(default)] + include_prereleases: bool, + #[serde(default)] + asset: Option, +} + +#[derive(Debug, Default, Deserialize)] +struct GithubAssetFilterRequest { + #[serde(default)] + include: Option, + #[serde(default)] + exclude: Option, +} + +fn required_non_empty( + value: Option, + field: &'static str, +) -> Result { + value + .filter(|value| !value.trim().is_empty()) + .ok_or_else(|| GithubReleaseOperationError::InvalidRequest(format!("missing {field}"))) +} + +fn github_eligible_release_count( + releases: &[GithubRelease], + options: &GithubReleaseCandidateOptions, +) -> usize { + releases + .iter() + .filter(|release| !release.draft) + .filter(|release| options.include_prereleases || !release.prerelease) + .count() +} + +fn provider_source_json(source: ProviderCacheSource) -> &'static str { + match source { + ProviderCacheSource::Cache => "cache", + ProviderCacheSource::Refreshed => "refreshed", + ProviderCacheSource::Stale => "stale", + } +} + +fn diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) +} + +fn digest_hex(value: &str) -> String { + let mut hasher = Sha512::new(); + hasher.update(value.as_bytes()); + format!("{:x}", hasher.finalize()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::provider_cache::{CACHE_REFRESH_FAILED, USED_STALE_CACHE}; + use getter_storage::ProviderResponseUpsert; + use serde_json::json; + use std::cell::Cell; + + const GITHUB_RELEASES_FIXTURE: &str = r#"[ + { + "tag_name": "v1.2.0", + "name": "Release 1.2.0", + "body": "Release notes", + "draft": false, + "prerelease": false, + "published_at": "2026-06-01T00:00:00Z", + "assets": [ + { + "name": "app-release.apk", + "content_type": "application/vnd.android.package-archive", + "size": 1234, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/app-release.apk", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "name": "app-debug.apk", + "content_type": "application/vnd.android.package-archive", + "size": 2345, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/app-debug.apk" + } + ] + } +]"#; + + const UPDATED_RELEASES_FIXTURE: &str = r#"[ + { + "tag_name": "v1.3.0", + "draft": false, + "prerelease": false, + "assets": [ + { + "name": "app-release.apk", + "size": 3456, + "browser_download_url": "https://github.com/example/app/releases/download/v1.3.0/app-release.apk" + } + ] + } +]"#; + + fn config() -> GithubReleaseConfig { + GithubReleaseConfig { + api_base_url: DEFAULT_GITHUB_API_BASE_URL.to_owned(), + owner: "example".to_owned(), + repo: "app".to_owned(), + } + } + + #[test] + fn cache_miss_parses_and_stores_fixture_releases() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + let cache_key = config.cache_key(); + + let result = + read_or_refresh_github_releases(&db, config, ProviderCacheMode::UseCached, || { + Ok(GITHUB_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + let cached = db.provider_response(&cache_key).unwrap().unwrap(); + assert_eq!(cached.provider, GITHUB_PROVIDER_ID); + assert_eq!(cached.response_json[0]["tag_name"], "v1.2.0"); + } + + #[test] + fn cache_hit_avoids_refreshing_fixture_releases() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_releases(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + let refreshed = Cell::new(false); + + let result = + read_or_refresh_github_releases(&db, config(), ProviderCacheMode::UseCached, || { + refreshed.set(true); + Ok(UPDATED_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + + assert!(!refreshed.get()); + assert_eq!(result.source, ProviderCacheSource::Cache); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + } + + #[test] + fn forced_refresh_replaces_cached_fixture_releases() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_releases(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + + let result = + read_or_refresh_github_releases(&db, config(), ProviderCacheMode::ForceRefresh, || { + Ok(UPDATED_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.releases[0].tag_name, "v1.3.0"); + } + + #[test] + fn forced_refresh_failure_returns_stale_cache_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + let cache_key = config.cache_key(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key, + provider: GITHUB_PROVIDER_ID.to_owned(), + response_json: serde_json::from_str(GITHUB_RELEASES_FIXTURE).unwrap(), + }) + .unwrap(); + + let result = + read_or_refresh_github_releases(&db, config, ProviderCacheMode::ForceRefresh, || { + Err("HTTP 503".to_owned()) + }) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + } + + #[test] + fn json_operation_returns_candidates_and_asset_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + let request = json!({ + "owner": "example", + "repo": "app", + "releases_json": GITHUB_RELEASES_FIXTURE, + "asset": { "include": "\\.apk$", "exclude": "debug" } + }); + + let response = github_releases_json(&db, &request.to_string()).unwrap(); + + assert_eq!(response["operation"], "github.releases"); + assert_eq!(response["provider"], "github"); + assert_eq!(response["source"], "refreshed"); + assert_eq!(response["candidates"][0]["version"], "v1.2.0"); + assert_eq!( + response["candidates"][0]["artifacts"][0]["name"], + "app-release.apk" + ); + assert!(response["diagnostics"].as_array().unwrap().is_empty()); + } + + #[test] + fn json_operation_reports_asset_not_found_diagnostic() { + let db = CacheDb::open_in_memory().unwrap(); + let request = json!({ + "owner": "example", + "repo": "app", + "releases_json": GITHUB_RELEASES_FIXTURE, + "asset": { "include": "\\.aab$" } + }); + + let response = github_releases_json(&db, &request.to_string()).unwrap(); + + assert!(response["candidates"].as_array().unwrap().is_empty()); + assert_eq!(response["diagnostics"][0]["code"], GITHUB_ASSET_NOT_FOUND); + } + + #[test] + fn malformed_fixture_json_reports_provider_error() { + let db = CacheDb::open_in_memory().unwrap(); + let request = json!({ + "owner": "example", + "repo": "app", + "releases_json": "not-json" + }); + + let error = github_releases_json(&db, &request.to_string()).unwrap_err(); + + assert!(matches!( + error, + GithubReleaseOperationError::Cache(ProviderCacheOperationError::RefreshFailed(_)) + )); + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index ba9281b..46511ea 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -8,6 +8,7 @@ pub mod autogen; pub mod fdroid_autogen; pub mod fdroid_catalog; +pub mod github_releases; pub mod legacy_room; pub mod provider_cache; pub mod read_model; diff --git a/crates/getter-providers/Cargo.toml b/crates/getter-providers/Cargo.toml index 31d2fc4..c8be0da 100644 --- a/crates/getter-providers/Cargo.toml +++ b/crates/getter-providers/Cargo.toml @@ -5,6 +5,8 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core", default-features = false } +regex = "1" roxmltree = "0.21" serde = { version = "1", features = ["derive"] } +serde_json = "1" thiserror = "1" diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index 518d5ba..6a9ebfb 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -231,6 +231,160 @@ fn fdroid_artifact_url(base: &str, apk_name: &str) -> String { format!("{}/{}", base.trim_end_matches('/'), apk_name) } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubRelease { + pub tag_name: String, + #[serde(default)] + pub name: Option, + #[serde(default)] + pub body: Option, + #[serde(default)] + pub draft: bool, + #[serde(default)] + pub prerelease: bool, + #[serde(default)] + pub published_at: Option, + #[serde(default)] + pub assets: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubReleaseAsset { + pub name: String, + pub browser_download_url: String, + #[serde(default)] + pub label: Option, + #[serde(default)] + pub content_type: Option, + #[serde(default)] + pub size: Option, + #[serde(default)] + pub digest: Option, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct GithubReleaseCandidateOptions { + pub include_prereleases: bool, + pub asset_filter: GithubAssetFilter, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct GithubAssetFilter { + pub include: Option, + pub exclude: Option, +} + +#[derive(Debug, thiserror::Error)] +pub enum GithubProviderError { + #[error("failed to parse GitHub releases JSON: {0}")] + Json(#[from] serde_json::Error), + #[error("invalid GitHub asset {filter_kind} regex '{pattern}': {source}")] + AssetFilterRegex { + filter_kind: &'static str, + pattern: String, + #[source] + source: regex::Error, + }, +} + +pub fn parse_github_releases_json(json: &str) -> Result, GithubProviderError> { + Ok(serde_json::from_str(json)?) +} + +pub fn github_release_update_candidates( + releases: &[GithubRelease], + options: &GithubReleaseCandidateOptions, +) -> Result, GithubProviderError> { + let filter = CompiledGithubAssetFilter::new(&options.asset_filter)?; + Ok(releases + .iter() + .filter(|release| !release.draft) + .filter(|release| options.include_prereleases || !release.prerelease) + .filter_map(|release| github_release_update_candidate(release, &filter)) + .collect()) +} + +fn github_release_update_candidate( + release: &GithubRelease, + filter: &CompiledGithubAssetFilter, +) -> Option { + let artifacts = release + .assets + .iter() + .filter(|asset| filter.matches(asset)) + .map(github_asset_update_artifact) + .collect::>(); + if artifacts.is_empty() { + return None; + } + + Some(UpdateCandidate { + version: release.tag_name.clone(), + version_code: None, + channel: release.prerelease.then(|| "prerelease".to_owned()), + source: Some("github".to_owned()), + artifacts, + }) +} + +fn github_asset_update_artifact(asset: &GithubReleaseAsset) -> UpdateArtifact { + UpdateArtifact { + name: asset.name.clone(), + url: asset.browser_download_url.clone(), + file_name: Some(asset.name.clone()), + sha256: github_asset_sha256(asset.digest.as_deref()), + size: asset.size, + } +} + +fn github_asset_sha256(digest: Option<&str>) -> Option { + digest + .and_then(|value| value.strip_prefix("sha256:")) + .filter(|value| { + value.len() == 64 && value.chars().all(|character| character.is_ascii_hexdigit()) + }) + .map(str::to_owned) +} + +struct CompiledGithubAssetFilter { + include: Option, + exclude: Option, +} + +impl CompiledGithubAssetFilter { + fn new(filter: &GithubAssetFilter) -> Result { + Ok(Self { + include: compile_github_asset_regex("include", filter.include.as_deref())?, + exclude: compile_github_asset_regex("exclude", filter.exclude.as_deref())?, + }) + } + + fn matches(&self, asset: &GithubReleaseAsset) -> bool { + self.include + .as_ref() + .is_none_or(|include| include.is_match(&asset.name)) + && !self + .exclude + .as_ref() + .is_some_and(|exclude| exclude.is_match(&asset.name)) + } +} + +fn compile_github_asset_regex( + filter_kind: &'static str, + pattern: Option<&str>, +) -> Result, GithubProviderError> { + pattern + .map(|pattern| { + regex::Regex::new(pattern).map_err(|source| GithubProviderError::AssetFilterRegex { + filter_kind, + pattern: pattern.to_owned(), + source, + }) + }) + .transpose() +} + #[cfg(test)] mod tests { use super::*; @@ -342,4 +496,159 @@ mod tests { assert!(matches!(error, FdroidCatalogError::Xml(_))); } + + #[test] + fn parses_github_releases_json_into_provider_facts() { + let releases = parse_github_releases_json(GITHUB_RELEASES_FIXTURE).unwrap(); + + assert_eq!(releases.len(), 3); + let release = &releases[0]; + assert_eq!(release.tag_name, "v1.2.0"); + assert_eq!(release.name.as_deref(), Some("Release 1.2.0")); + assert_eq!(release.body.as_deref(), Some("Release notes")); + assert!(!release.draft); + assert!(!release.prerelease); + assert_eq!( + release.published_at.as_deref(), + Some("2026-06-01T00:00:00Z") + ); + assert_eq!(release.assets.len(), 3); + let asset = &release.assets[0]; + assert_eq!(asset.name, "app-release.apk"); + assert_eq!( + asset.content_type.as_deref(), + Some("application/vnd.android.package-archive") + ); + assert_eq!(asset.size, Some(1234)); + assert_eq!( + asset.browser_download_url, + "https://github.com/example/app/releases/download/v1.2.0/app-release.apk" + ); + assert_eq!( + asset.digest.as_deref(), + Some("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + } + + #[test] + fn normalizes_github_release_assets_to_update_candidates_with_filters() { + let releases = parse_github_releases_json(GITHUB_RELEASES_FIXTURE).unwrap(); + let options = GithubReleaseCandidateOptions { + asset_filter: GithubAssetFilter { + include: Some(r"\.apk$".to_owned()), + exclude: Some("debug".to_owned()), + }, + ..Default::default() + }; + + let candidates = github_release_update_candidates(&releases, &options).unwrap(); + + assert_eq!(candidates.len(), 1); + assert_eq!(candidates[0].version, "v1.2.0"); + assert_eq!(candidates[0].source.as_deref(), Some("github")); + assert_eq!(candidates[0].artifacts.len(), 1); + let artifact = &candidates[0].artifacts[0]; + assert_eq!(artifact.name, "app-release.apk"); + assert_eq!( + artifact.url, + "https://github.com/example/app/releases/download/v1.2.0/app-release.apk" + ); + assert_eq!(artifact.file_name.as_deref(), Some("app-release.apk")); + assert_eq!( + artifact.sha256.as_deref(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + assert_eq!(artifact.size, Some(1234)); + } + + #[test] + fn ignores_unrecognized_github_asset_digests() { + let releases = parse_github_releases_json(GITHUB_INVALID_DIGEST_FIXTURE).unwrap(); + + let candidates = github_release_update_candidates( + &releases, + &GithubReleaseCandidateOptions { + asset_filter: GithubAssetFilter { + include: Some(r"\.apk$".to_owned()), + exclude: None, + }, + ..Default::default() + }, + ) + .unwrap(); + + assert_eq!(candidates[0].artifacts[0].sha256, None); + } + + const GITHUB_RELEASES_FIXTURE: &str = r#"[ + { + "tag_name": "v1.2.0", + "name": "Release 1.2.0", + "body": "Release notes", + "draft": false, + "prerelease": false, + "published_at": "2026-06-01T00:00:00Z", + "assets": [ + { + "name": "app-release.apk", + "content_type": "application/vnd.android.package-archive", + "size": 1234, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/app-release.apk", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "name": "app-debug.apk", + "content_type": "application/vnd.android.package-archive", + "size": 2345, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/app-debug.apk" + }, + { + "name": "notes.txt", + "content_type": "text/plain", + "size": 345, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/notes.txt" + } + ] + }, + { + "tag_name": "v1.3.0-beta1", + "draft": false, + "prerelease": true, + "assets": [ + { + "name": "app-beta.apk", + "size": 456, + "browser_download_url": "https://github.com/example/app/releases/download/v1.3.0-beta1/app-beta.apk" + } + ] + }, + { + "tag_name": "v1.4.0-draft", + "draft": true, + "prerelease": false, + "assets": [ + { + "name": "app-draft.apk", + "size": 567, + "browser_download_url": "https://github.com/example/app/releases/download/v1.4.0-draft/app-draft.apk" + } + ] + } +]"#; + + const GITHUB_INVALID_DIGEST_FIXTURE: &str = r#"[ + { + "tag_name": "v1.2.0", + "draft": false, + "prerelease": false, + "assets": [ + { + "name": "app-release.apk", + "size": 1234, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/app-release.apk", + "digest": "sha256:not-a-valid-sha256" + } + ] + } +]"#; } From 32e0fb337afa3c773547645b17553deb70282767 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 13:20:43 +0800 Subject: [PATCH 41/72] refactor(repository): remove flat Lua repository layout --- README.md | 2 +- crates/getter-cli/src/lib.rs | 44 +-- crates/getter-cli/tests/bdd_cli.rs | 119 +++---- .../tests/features/cli/repo_validate.feature | 8 +- crates/getter-core/src/diagnostics.rs | 166 ++------- crates/getter-core/src/lua.rs | 320 ++++-------------- crates/getter-core/src/repository.rs | 248 +------------- crates/getter-operations/src/autogen.rs | 26 +- crates/getter-operations/src/read_model.rs | 75 +--- crates/getter-operations/src/runtime.rs | 108 +----- 10 files changed, 158 insertions(+), 958 deletions(-) diff --git a/README.md b/README.md index 6fc932d..095e838 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This repository is intentionally usable outside the UpgradeAll UI. The UpgradeAl - Rust workspace split into `getter-core`, `getter-storage`, `getter-cli`, and placeholder provider/downloader/RPC/FFI crates. - Package IDs are readable, for example `android/org.fdroid.fdroid`. -- Lua package repositories use `repo.toml` plus `packages/`, `lib/`, and `templates/` directories. +- Lua package repositories are repository alias directories under `repo/`; packages are directories containing `metadata.jsonc`, optional `Manifest`, and direct child version scripts such as `1.20.0.lua` or `9999.lua`. Shared Lua classes live under repository-root `luaclass/`. - SQLite storage uses a main DB and a separate cache DB. - `getter-cli` exposes JSON command contracts for init, app list, repository registration/evaluation, package evaluation, storage validation, legacy bridge-bundle import, and sanitized legacy report listing. diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 8306d7c..7c7efbe 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -6,9 +6,9 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; -use getter_core::lua::{evaluate_package_directory_script, evaluate_package_file}; +use getter_core::lua::evaluate_package_directory_script; use getter_core::repository::{ - default_repository_priority, GetterDataDirLayout, RepositoryLayout, RepositoryMetadata, + default_repository_priority, GetterDataDirLayout, RepositoryMetadata, RepositoryPackageDirectoryLayout, REPOSITORY_ROOT_METADATA_FILE, REPO_API_VERSION_V1, }; use getter_core::runtime::{GetterRuntime, SealedActionPlan}; @@ -1225,10 +1225,6 @@ fn parse_autogen_acceptance(args: &[String]) -> Result Result { - RepositoryLayout::load(path).map_err(|source| CliError::Repository(source.to_string())) -} - fn load_package_directory_layout( path: &Path, ) -> Result { @@ -1241,20 +1237,6 @@ fn load_repository_metadata( path: &Path, priority: Option, ) -> Result { - if path.join("repo.toml").is_file() { - let layout = load_repository_layout(path)?; - if &layout.metadata.id != id { - return Err(CliError::Repository(format!( - "repo.toml id '{}' does not match requested id '{}'", - layout.metadata.id, id - ))); - } - return Ok(RepositoryMetadata { - priority: priority.unwrap_or(layout.metadata.priority), - ..layout.metadata - }); - } - RepositoryPackageDirectoryLayout::load(path) .map_err(|source| CliError::Repository(source.to_string()))?; Ok(RepositoryMetadata { @@ -1312,18 +1294,6 @@ fn evaluate_repository_packages( repo: &StoredRepository, ) -> Result, CliError> { let path = repo_path(repo)?; - if path.join("repo.toml").is_file() { - let layout = load_repository_layout(&path)?; - return layout - .packages - .iter() - .map(|package_file| { - evaluate_package_file(&layout, &package_file.path) - .map_err(|error| CliError::PackageEval(error.to_string())) - }) - .collect(); - } - let layout = load_package_directory_layout(&path)?; layout .packages @@ -1337,16 +1307,6 @@ fn evaluate_package_in_repository( package_id: &PackageId, ) -> Result, CliError> { let path = repo_path(repo)?; - if path.join("repo.toml").is_file() { - let layout = load_repository_layout(&path)?; - let Some(package_file) = layout.package_file(package_id) else { - return Ok(None); - }; - return evaluate_package_file(&layout, &package_file.path) - .map(Some) - .map_err(|error| CliError::PackageEval(error.to_string())); - } - let layout = load_package_directory_layout(&path)?; let Some(package) = layout.package(package_id) else { return Ok(None); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 54b584a..f5f0887 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -507,8 +507,8 @@ fn fixture_lua_repository_invalid_lua(world: &mut CliWorld, repo_id: String, pac create_custom_fixture_lua_repository( world, repo_id, - package_id.clone(), - format!("return package_def {{ id = \"{package_id}\", name = "), + package_id, + "#!/bin/upa-lua v1\nreturn package_version { name = ".to_owned(), ); } @@ -521,8 +521,8 @@ fn fixture_lua_repository_invalid_schema( create_custom_fixture_lua_repository( world, repo_id, - package_id.clone(), - format!("return {{ id = \"{package_id}\" }}"), + package_id, + "#!/bin/upa-lua v1\nreturn package_version { source_priority = \"fdroid\" }".to_owned(), ); } @@ -536,7 +536,7 @@ fn fixture_lua_repository_mismatched_path( world, repo_id, package_id, - "return { id = \"android/com.termux\", name = \"Termux\" }".to_owned(), + "#!/bin/upa-lua v1\nreturn package_version { id = \"android/com.termux\", name = \"Termux\" }".to_owned(), ); } @@ -544,14 +544,10 @@ fn fixture_lua_repository_mismatched_path( fn incomplete_lua_repository(world: &mut CliWorld, repo_id: String) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); - fs::create_dir_all(&repo_path).expect("create incomplete repo dir"); - fs::write( - repo_path.join("repo.toml"), - format!( - "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" - ), - ) - .expect("write repo.toml"); + let package_dir = repo_path.join("android/app/broken"); + fs::create_dir_all(&package_dir).expect("create incomplete package dir"); + fs::write(package_dir.join("metadata.jsonc"), "{not-json") + .expect("write invalid package metadata"); world.fixture_repo_id = Some(repo_id); world.fixture_repo_path = Some(repo_path); world.fixture_package_id = None; @@ -1402,7 +1398,7 @@ fn output_reports_repository_diagnostic(world: &mut CliWorld, code: String) { assert!(diagnostic["location"]["path"].as_str().is_some()); if code == "package.schema" { assert_eq!(diagnostic["package_id"], "android/org.fdroid.fdroid"); - assert_eq!(diagnostic["location"]["field"], "name"); + assert!(diagnostic["location"]["field"].as_str().is_some()); } } @@ -1958,61 +1954,34 @@ fn create_fixture_lua_repository( ) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); - if let Some(package_name_path) = package_id.strip_prefix("android/app/") { - let package_dir = repo_path.join(package_relative_path(&package_id)); - fs::create_dir_all(&package_dir).expect("create package dir"); - fs::write( - package_dir.join("metadata.jsonc"), - format!( - r#"{{ "type": "android:app", "display_name": {name:?}, "android": {{ "package_name": {package_name_path:?} }} }}"#, - name = package_name, - ), - ) - .expect("write package metadata"); - fs::write(package_dir.join("Manifest"), "").expect("write Manifest"); - fs::write( - package_dir.join("9999.lua"), - format!( - r#"#!/bin/upa-lua v1 + let android_package_name = package_id + .strip_prefix("android/app/") + .or_else(|| package_id.strip_prefix("android/")) + .expect("fixture package id should be android package id"); + let package_dir = repo_path.join(package_relative_path(&package_id)); + fs::create_dir_all(&package_dir).expect("create package dir"); + fs::write( + package_dir.join("metadata.jsonc"), + format!( + r#"{{ "type": "android:app", "display_name": {name:?}, "android": {{ "package_name": {android_package_name:?} }} }}"#, + name = package_name, + ), + ) + .expect("write package metadata"); + fs::write(package_dir.join("Manifest"), "").expect("write Manifest"); + fs::write( + package_dir.join("9999.lua"), + format!( + r#"#!/bin/upa-lua v1 return package_version {{ installed = {{ - {{ kind = "android_package", package_name = "{package_name_path}" }}, - }}, -}} -"# - ), - ) - .expect("write package Lua"); - } else { - let package_name_path = package_id - .strip_prefix("android/") - .expect("fixture package id should be android package id"); - fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); - fs::create_dir(repo_path.join("lib")).expect("create lib dir"); - fs::create_dir(repo_path.join("templates")).expect("create templates dir"); - fs::write( - repo_path.join("repo.toml"), - format!( - "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" - ), - ) - .expect("write repo.toml"); - fs::write( - repo_path.join(format!("packages/android/{package_name_path}.lua")), - format!( - r#" -return package_def {{ - id = "{package_id}", - name = "{package_name}", - installed = {{ - {{ kind = "android_package", package_name = "{package_name_path}" }}, + {{ kind = "android_package", package_name = "{android_package_name}" }}, }}, }} "# - ), - ) - .expect("write package Lua"); - } + ), + ) + .expect("write package Lua"); world.fixture_repo_id = Some(repo_id); world.fixture_repo_path = Some(repo_path); @@ -2184,24 +2153,14 @@ fn create_custom_fixture_lua_repository( ) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); - let package_name_path = package_id - .strip_prefix("android/") - .expect("fixture package id should be android package id"); - fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); - fs::create_dir(repo_path.join("lib")).expect("create lib dir"); - fs::create_dir(repo_path.join("templates")).expect("create templates dir"); - fs::write( - repo_path.join("repo.toml"), - format!( - "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" - ), - ) - .expect("write repo.toml"); + let package_dir = repo_path.join(package_id.replace('/', std::path::MAIN_SEPARATOR_STR)); + fs::create_dir_all(&package_dir).expect("create package dir"); fs::write( - repo_path.join(format!("packages/android/{package_name_path}.lua")), - package_source, + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "display_name": "F-Droid", "android": { "package_name": "org.fdroid.fdroid" } }"#, ) - .expect("write package Lua"); + .expect("write package metadata"); + fs::write(package_dir.join("1.20.0.lua"), package_source).expect("write package Lua"); world.fixture_repo_id = Some(repo_id); world.fixture_repo_path = Some(repo_path); diff --git a/crates/getter-cli/tests/features/cli/repo_validate.feature b/crates/getter-cli/tests/features/cli/repo_validate.feature index 9c6d1a1..55604f6 100644 --- a/crates/getter-cli/tests/features/cli/repo_validate.feature +++ b/crates/getter-cli/tests/features/cli/repo_validate.feature @@ -27,16 +27,16 @@ Feature: Getter CLI repository validation Then the command succeeds And the output reports repository diagnostic "package.schema" - Scenario: User receives diagnostics for package path mismatch + Scenario: User receives diagnostics for package scripts declaring an id Given an initialized getter data directory And a fixture Lua repository "broken" with mismatched package path "android/org.fdroid.fdroid" When I run getter repo validate for that repository Then the command succeeds - And the output reports repository diagnostic "package.domain" + And the output reports repository diagnostic "package.schema" - Scenario: User receives diagnostics for an incomplete repository layout + Scenario: User receives diagnostics for invalid package metadata Given an initialized getter data directory And an incomplete Lua repository "broken" When I run getter repo validate for that repository Then the command succeeds - And the output reports repository diagnostic "repository.missing_directory" + And the output reports repository diagnostic "package.metadata" diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index a78ff21..0fe663c 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -3,10 +3,9 @@ //! Diagnostics are getter-owned DTOs used by CLI and future app bridges. They //! describe what getter observed; Flutter should only render them. -use crate::lua::{evaluate_package_directory_script, evaluate_package_file, LuaPackageError}; +use crate::lua::{evaluate_package_directory_script, LuaPackageError}; use crate::repository::{ - package_cache_key, InvalidPackageDirectory, RepositoryLayout, RepositoryLoadError, - RepositoryPackageDirectoryLayout, + InvalidPackageDirectory, RepositoryLoadError, RepositoryPackageDirectoryLayout, }; use crate::PackageId; use serde::{Deserialize, Serialize}; @@ -64,36 +63,7 @@ impl RepositoryValidationReport { /// This intentionally loads and evaluates local Lua package files only. It must /// not perform provider/network checks. pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationReport { - let root = path.as_ref(); - if root.join("repo.toml").is_file() { - validate_legacy_repository_path(root) - } else { - validate_package_directory_repository_path(root) - } -} - -fn validate_legacy_repository_path(root: &Path) -> RepositoryValidationReport { - let layout = match RepositoryLayout::load(root) { - Ok(layout) => layout, - Err(error) => { - return RepositoryValidationReport::new(0, vec![repository_load_diagnostic(error)]) - } - }; - - let mut diagnostics = Vec::new(); - let mut package_count = 0usize; - for package_file in &layout.packages { - if let Err(error) = package_cache_key(&layout, package_file) { - diagnostics.push(repository_load_diagnostic(error)); - continue; - } - match evaluate_package_file(&layout, &package_file.path) { - Ok(_) => package_count += 1, - Err(error) => diagnostics.push(lua_diagnostic(error, Some(package_file.id.clone()))), - } - } - - RepositoryValidationReport::new(package_count, diagnostics) + validate_package_directory_repository_path(path.as_ref()) } fn validate_package_directory_repository_path(root: &Path) -> RepositoryValidationReport { @@ -167,40 +137,20 @@ fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDi path, format!("failed to read repository root: {source}"), ), - RepositoryLoadError::MissingGeneratedRepository { alias, path } => ( - "repository.missing_generated_repository", - path, - format!("configured generated repository '{alias}' does not exist"), - ), - RepositoryLoadError::ReadRepoToml { path, source } => ( - "repository.read_repo_toml", - path, - format!("failed to read repo.toml: {source}"), - ), - RepositoryLoadError::ParseRepoToml { path, source } => ( - "repository.parse_repo_toml", - path, - format!("failed to parse repo.toml: {source}"), - ), RepositoryLoadError::RepositoryId(source) => ( "repository.invalid_id", - PathBuf::from("repo.toml"), + PathBuf::from("repo"), source.to_string(), ), - RepositoryLoadError::UnsupportedApiVersion(version) => ( - "repository.unsupported_api_version", - PathBuf::from("repo.toml"), - format!("unsupported repository api_version '{version}'"), - ), - RepositoryLoadError::MissingDirectory { path, directory } => ( - "repository.missing_directory", + RepositoryLoadError::MissingGeneratedRepository { alias, path } => ( + "repository.missing_generated_repository", path, - format!("missing required directory '{directory}'"), + format!("configured generated repository '{alias}' does not exist"), ), RepositoryLoadError::ReadPackagesDir { path, source } => ( - "repository.read_packages_dir", + "repository.read_package_directory", path, - format!("failed to read packages directory: {source}"), + format!("failed to read package directory: {source}"), ), RepositoryLoadError::InvalidPackagePath { path, reason } => { ("repository.invalid_package_path", path, reason) @@ -351,40 +301,6 @@ mod tests { use super::*; use std::fs; - fn fixture_repo() -> tempfile::TempDir { - let temp = tempfile::tempdir().unwrap(); - let root = temp.path(); - fs::write( - root.join("repo.toml"), - r#"id = "official" -name = "Official" -priority = 0 -api_version = "getter.repo.v1" -"#, - ) - .unwrap(); - fs::create_dir_all(root.join("packages/android")).unwrap(); - fs::create_dir(root.join("lib")).unwrap(); - fs::create_dir(root.join("templates")).unwrap(); - temp - } - - #[test] - fn valid_repository_has_no_diagnostics() { - let temp = fixture_repo(); - fs::write( - temp.path().join("packages/android/org.fdroid.fdroid.lua"), - r#"return package_def { id = "android/org.fdroid.fdroid", name = "F-Droid" }"#, - ) - .unwrap(); - - let report = validate_repository_path(temp.path()); - assert!(report.valid, "{report:?}"); - assert_eq!(report.package_count, 1); - assert!(report.diagnostics.is_empty()); - assert!(!report.network_required); - } - #[test] fn package_directory_repository_has_no_diagnostics() { let temp = tempfile::tempdir().unwrap(); @@ -478,79 +394,55 @@ api_version = "getter.repo.v1" } #[test] - fn missing_directory_is_stable_repository_diagnostic() { + fn lua_schema_error_is_stable_package_diagnostic() { let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( - temp.path().join("repo.toml"), - r#"id = "official" -name = "Official" -api_version = "getter.repo.v1" -"#, + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, ) .unwrap(); - - let report = validate_repository_path(temp.path()); - assert!(!report.valid); - assert_eq!(report.diagnostics[0].code, "repository.missing_directory"); - } - - #[test] - fn lua_schema_error_is_stable_package_diagnostic() { - let temp = fixture_repo(); fs::write( - temp.path().join("packages/android/org.fdroid.fdroid.lua"), - r#"return { id = "android/org.fdroid.fdroid" }"#, + package_dir.join("9999.lua"), + "#!/bin/upa-lua v1\nreturn package_version { source_priority = \"fdroid\" }", ) .unwrap(); let report = validate_repository_path(temp.path()); assert!(!report.valid); assert_eq!(report.diagnostics[0].code, "package.schema"); - assert_eq!( - report.diagnostics[0].location.field.as_deref(), - Some("name") - ); assert_eq!( report.diagnostics[0] .package_id .as_ref() .unwrap() .to_string(), - "android/org.fdroid.fdroid" + "android/app/org.fdroid.fdroid" ); } #[test] - fn package_id_path_mismatch_is_domain_diagnostic() { - let temp = fixture_repo(); + fn package_version_script_id_field_is_schema_diagnostic() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( - temp.path().join("packages/android/org.fdroid.fdroid.lua"), - r#"return { id = "android/com.termux", name = "Termux" }"#, + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, ) .unwrap(); - - let report = validate_repository_path(temp.path()); - assert!(!report.valid); - assert_eq!(report.diagnostics[0].code, "package.domain"); - } - - #[test] - fn unsupported_api_version_is_stable_repository_diagnostic() { - let temp = tempfile::tempdir().unwrap(); fs::write( - temp.path().join("repo.toml"), - r#"id = "official" -name = "Official" -api_version = "getter.repo.v2" -"#, + package_dir.join("9999.lua"), + "#!/bin/upa-lua v1\nreturn package_version { id = \"android/app/org.fdroid.fdroid\" }", ) .unwrap(); let report = validate_repository_path(temp.path()); assert!(!report.valid); - assert_eq!( - report.diagnostics[0].code, - "repository.unsupported_api_version" - ); + assert_eq!(report.diagnostics[0].code, "package.schema"); + assert!(report.diagnostics[0] + .message + .contains("must not be declared")); } } diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 8e33718..2507fd7 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -2,8 +2,7 @@ use crate::repository::{ PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageTypeMetadata, - PackageVersionScript, RepositoryLayout, RepositoryLoadError, LUA_API_SHEBANG_V1, - REPOSITORY_LUACLASS_DIR, + PackageVersionScript, RepositoryLoadError, LUA_API_SHEBANG_V1, REPOSITORY_LUACLASS_DIR, }; use crate::{ InstalledTarget, PackageId, PackagePermissions, RepositoryId, ResolvedPackage, UpdateArtifact, @@ -48,35 +47,6 @@ pub enum LuaPackageError { }, } -/// Evaluate and validate a Lua package file from a repository layout. -pub fn evaluate_package_file( - repository: &RepositoryLayout, - path: impl AsRef, -) -> Result { - let path = path.as_ref(); - let source = fs::read_to_string(path).map_err(|source| LuaPackageError::ReadFile { - path: path.to_path_buf(), - source, - })?; - evaluate_package_source(repository, path, &source) -} - -/// Evaluate package source text. This is public for focused tests and future CLI -/// plumbing; repository callers should normally use [`evaluate_package_file`]. -pub fn evaluate_package_source( - repository: &RepositoryLayout, - path: impl AsRef, - source: &str, -) -> Result { - let path = path.as_ref().to_path_buf(); - let json = evaluate_package_source_to_json( - &LuaRepositoryEnvironment::Legacy(repository), - &path, - source, - )?; - validate_package_json(repository, &path, json) -} - pub fn evaluate_package_directory_script( repository_id: &RepositoryId, package: &PackageDirectory, @@ -140,7 +110,6 @@ fn evaluate_package_source_to_json( } enum LuaRepositoryEnvironment<'a> { - Legacy(&'a RepositoryLayout), PackageDirectory { package: &'a PackageDirectory }, } @@ -152,17 +121,6 @@ fn configure_package_path( package.set("cpath", "")?; package.set("loadlib", Value::Nil)?; match environment { - LuaRepositoryEnvironment::Legacy(repository) => { - let lib_pattern = repository.lib_dir.join("?.lua"); - let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); - let new_path = format!( - "{};{}", - lib_pattern.to_string_lossy(), - nested_lib_pattern.to_string_lossy() - ); - package.set("path", new_path)?; - install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone())?; - } LuaRepositoryEnvironment::PackageDirectory { package: package_dir, } => { @@ -207,16 +165,6 @@ fn remove_unsafe_globals(lua: &Lua) -> mlua::Result<()> { Ok(()) } -fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> mlua::Result<()> { - install_prefixed_file_searcher( - lua, - package, - "lib.", - lib_dir, - "constrained repository lib searcher only handles lib.* modules", - ) -} - fn install_luaclass_prefix_searcher( lua: &Lua, package: &Table, @@ -315,9 +263,8 @@ fn install_helpers(lua: &Lua, environment: &LuaRepositoryEnvironment<'_>) -> mlu lua.globals().set("android_app", package_fn.clone())?; lua.globals().set("magisk_module", package_fn.clone())?; lua.globals().set("generic_package", package_fn)?; - if let LuaRepositoryEnvironment::PackageDirectory { package } = environment { - install_package_file_helpers(lua, package)?; - } + let LuaRepositoryEnvironment::PackageDirectory { package } = environment; + install_package_file_helpers(lua, package)?; Ok(()) } @@ -472,45 +419,6 @@ fn is_array_table(table: &Table) -> mlua::Result { Ok(count == len) } -fn validate_package_json( - repository: &RepositoryLayout, - path: &Path, - value: JsonValue, -) -> Result { - let object = value.as_object().ok_or_else(|| LuaPackageError::Schema { - path: path.to_path_buf(), - message: "package value must be an object".to_owned(), - })?; - - let id = required_string(path, object, "id")?; - let id: PackageId = id.parse().map_err(|source| LuaPackageError::Schema { - path: path.to_path_buf(), - message: format!("invalid package id: {source}"), - })?; - let path_id = crate::repository::package_id_from_path(&repository.packages_dir, path).map_err( - |source| LuaPackageError::Domain { - path: path.to_path_buf(), - message: format!("failed to derive package id from path: {source}"), - }, - )?; - if id != path_id { - return Err(LuaPackageError::Domain { - path: path.to_path_buf(), - message: format!("package id '{id}' does not match path-derived id '{path_id}'"), - }); - } - - let name = required_string(path, object, "name")?.to_owned(); - package_from_version_json( - repository.metadata.id.clone(), - id, - name, - PackagePermissions::default(), - path, - object, - ) -} - fn validate_package_directory_version_json( repository_id: &RepositoryId, package: &PackageDirectory, @@ -814,45 +722,48 @@ fn parse_string_array( #[cfg(test)] mod tests { use super::*; - use crate::repository::{RepositoryLayout, RepositoryPackageDirectoryLayout}; + use crate::repository::RepositoryPackageDirectoryLayout; use crate::RepositoryId; use std::fs; - fn fixture_repo() -> (tempfile::TempDir, RepositoryLayout, PathBuf) { - let temp = tempfile::tempdir().unwrap(); - let root = temp.path(); - fs::write( - root.join("repo.toml"), - r#"id = "official" -name = "UpgradeAll Official" -priority = 0 -api_version = "getter.repo.v1" -"#, + fn evaluate_single_package_directory( + root: &Path, + repository_id: &str, + ) -> Result { + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + evaluate_package_directory_script( + &RepositoryId::new(repository_id).unwrap(), + package, + &metadata, + script, ) - .unwrap(); - fs::create_dir(root.join("packages")).unwrap(); - fs::create_dir(root.join("packages/android")).unwrap(); - fs::create_dir(root.join("lib")).unwrap(); - fs::create_dir(root.join("templates")).unwrap(); - let package_path = root.join("packages/android/org.fdroid.fdroid.lua"); - fs::write(&package_path, "return {}").unwrap(); - let layout = RepositoryLayout::load(root).unwrap(); - (temp, layout, package_path) } #[test] fn evaluates_json_like_lua_package_table() { - let (_temp, layout, package_path) = fixture_repo(); + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( - &package_path, - r#" -return package_def { - id = "android/org.fdroid.fdroid", - name = "F-Droid", + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "F-Droid", + "android": { "package_name": "org.fdroid.fdroid" }, + "lua": { "9999.lua": { "permission": ["allow_free_network"] } } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { installed = { { kind = "android_package", package_name = "org.fdroid.fdroid" }, }, - permissions = { free_network = true }, source_priority = { "github", "fdroid" }, updates = { { @@ -876,8 +787,8 @@ return package_def { ) .unwrap(); - let package = evaluate_package_file(&layout, &package_path).unwrap(); - assert_eq!(package.id.to_string(), "android/org.fdroid.fdroid"); + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + assert_eq!(package.id.to_string(), "android/app/org.fdroid.fdroid"); assert_eq!(package.repository.as_str(), "official"); assert_eq!(package.name, "F-Droid"); assert_eq!( @@ -1213,130 +1124,26 @@ return package_version { name = getter_builtin.read_package_file("name.txt") } assert_eq!(package.name, "from builtin"); } - #[test] - fn rejects_package_id_that_does_not_match_path() { - let (_temp, layout, package_path) = fixture_repo(); - fs::write( - &package_path, - r#"return { id = "android/com.termux", name = "Termux" }"#, - ) - .unwrap(); - - let err = evaluate_package_file(&layout, &package_path).unwrap_err(); - assert!(matches!(err, LuaPackageError::Domain { .. })); - } - - #[test] - fn require_can_load_repository_lib_modules() { - let (_temp, layout, package_path) = fixture_repo(); - fs::write( - layout.lib_dir.join("android.lua"), - r#" -return { - local_app = function(input) - return { - id = input.id, - name = input.name, - installed = { - { kind = "android_package", package_name = input.package_name }, - }, - } - end -} -"#, - ) - .unwrap(); - fs::write( - &package_path, - r#" -local android = require("android") -return android.local_app { - id = "android/org.fdroid.fdroid", - name = "F-Droid", - package_name = "org.fdroid.fdroid", -} -"#, - ) - .unwrap(); - - let package = evaluate_package_file(&layout, &package_path).unwrap(); - assert_eq!(package.name, "F-Droid"); - assert_eq!(package.installed.len(), 1); - } - - #[test] - fn require_can_load_repository_lib_modules_with_lib_prefix() { - let (_temp, layout, package_path) = fixture_repo(); - fs::write( - layout.lib_dir.join("android.lua"), - r#" -return { - local_app = function(input) - return { - id = input.id, - name = input.name, - installed = { - { kind = "android_package", package_name = input.package_name }, - }, - } - end -} -"#, - ) - .unwrap(); - fs::write( - &package_path, - r#" -local android = require("lib.android") -return android.local_app { - id = "android/org.fdroid.fdroid", - name = "F-Droid", - package_name = "org.fdroid.fdroid", -} -"#, - ) - .unwrap(); - - let package = evaluate_package_file(&layout, &package_path).unwrap(); - assert_eq!(package.name, "F-Droid"); - assert_eq!(package.installed.len(), 1); - } - - #[test] - fn lib_prefixed_searcher_does_not_expose_repository_templates() { - let (_temp, layout, package_path) = fixture_repo(); - fs::write( - layout.templates_dir.join("android.lua"), - r#"return { leaked = true }"#, - ) - .unwrap(); - fs::write( - &package_path, - r#" -local ok = pcall(require, "templates.android") -return { - id = "android/org.fdroid.fdroid", - name = ok and "leaked" or "F-Droid", -} -"#, - ) - .unwrap(); - - let package = evaluate_package_file(&layout, &package_path).unwrap(); - assert_eq!(package.name, "F-Droid"); - } - #[test] fn lua_environment_does_not_expose_process_or_file_system_globals() { let temp = tempfile::tempdir().unwrap(); let side_effect = temp.path().join("side-effect"); - let (_repo_temp, layout, package_path) = fixture_repo(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( - &package_path, + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "F-Droid", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), format!( - r#" -return {{ - id = "android/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +return package_version {{ name = (os == nil and io == nil and debug == nil and dofile == nil and loadfile == nil and package == nil) and "F-Droid" or "leaked", side_effect = rawget(_G, "os") and os.execute("touch {}"), }} @@ -1346,40 +1153,43 @@ return {{ ) .unwrap(); - let package = evaluate_package_file(&layout, &package_path).unwrap(); + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); assert_eq!(package.name, "F-Droid"); assert!(!side_effect.exists()); } #[test] fn repository_root_is_not_exposed_even_when_cwd_is_repository_root() { - let (_temp, layout, package_path) = fixture_repo(); + let temp = tempfile::tempdir().unwrap(); fs::write( - layout.root.join("rootleak.lua"), + temp.path().join("rootleak.lua"), r#"return { leaked = true }"#, ) .unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( - layout.templates_dir.join("android.lua"), - r#"return { leaked = true }"#, + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "F-Droid", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, ) .unwrap(); fs::write( - &package_path, - r#" + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 local root_ok = pcall(require, "rootleak") local template_ok = pcall(require, "templates.android") -return { - id = "android/org.fdroid.fdroid", - name = (root_ok or template_ok) and "leaked" or "F-Droid", -} +return package_version { name = (root_ok or template_ok) and "leaked" or "F-Droid" } "#, ) .unwrap(); let original_cwd = std::env::current_dir().unwrap(); - std::env::set_current_dir(&layout.root).unwrap(); - let result = evaluate_package_file(&layout, &package_path); + std::env::set_current_dir(temp.path()).unwrap(); + let result = evaluate_single_package_directory(temp.path(), "official"); std::env::set_current_dir(original_cwd).unwrap(); let package = result.unwrap(); diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index f5f0e99..1ad1360 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -24,16 +24,6 @@ pub const LUA_API_SHEBANG_V1: &str = "#!/bin/upa-lua v1"; pub const LOCAL_REPOSITORY_ALIAS: &str = "local"; pub const DEFAULT_GENERATED_REPOSITORY_ALIAS: &str = "autogen"; -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct RepositoryLayout { - pub root: PathBuf, - pub metadata: RepositoryMetadata, - pub packages_dir: PathBuf, - pub lib_dir: PathBuf, - pub templates_dir: PathBuf, - pub packages: Vec, -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct RepositoryMetadata { pub id: RepositoryId, @@ -236,12 +226,6 @@ pub fn generated_repository_target( } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PackageFile { - pub id: PackageId, - pub path: PathBuf, -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct RepositoryPackageDirectoryLayout { pub root: PathBuf, @@ -365,27 +349,8 @@ pub enum RepositoryLoadError { }, #[error("configured generated repository '{alias}' does not exist at {path}")] MissingGeneratedRepository { alias: RepositoryId, path: PathBuf }, - #[error("failed to read repo.toml at {path}: {source}")] - ReadRepoToml { - path: PathBuf, - #[source] - source: std::io::Error, - }, - #[error("failed to parse repo.toml at {path}: {source}")] - ParseRepoToml { - path: PathBuf, - #[source] - source: toml::de::Error, - }, - #[error("invalid repository id in repo.toml: {0}")] + #[error("invalid repository id: {0}")] RepositoryId(#[from] RepositoryIdError), - #[error("unsupported repository api_version '{0}'")] - UnsupportedApiVersion(String), - #[error("repository path {path} is missing required directory '{directory}'")] - MissingDirectory { - path: PathBuf, - directory: &'static str, - }, #[error("failed to read packages directory {path}: {source}")] ReadPackagesDir { path: PathBuf, @@ -490,104 +455,6 @@ impl PackageDirectoryMetadata { } } -impl RepositoryLayout { - pub fn load(root: impl AsRef) -> Result { - let root = root.as_ref().to_path_buf(); - let repo_toml_path = root.join("repo.toml"); - let raw = fs::read_to_string(&repo_toml_path).map_err(|source| { - RepositoryLoadError::ReadRepoToml { - path: repo_toml_path.clone(), - source, - } - })?; - let raw_metadata: RawRepositoryMetadata = - toml::from_str(&raw).map_err(|source| RepositoryLoadError::ParseRepoToml { - path: repo_toml_path.clone(), - source, - })?; - let api_version = raw_metadata.api_version; - if api_version != REPO_API_VERSION_V1 { - return Err(RepositoryLoadError::UnsupportedApiVersion(api_version)); - } - let metadata = RepositoryMetadata { - id: RepositoryId::new(raw_metadata.id)?, - name: raw_metadata.name, - priority: RepositoryPriority::new(raw_metadata.priority.unwrap_or_default()), - api_version, - }; - - let packages_dir = root.join("packages"); - let lib_dir = root.join("lib"); - let templates_dir = root.join("templates"); - require_dir(&root, &packages_dir, "packages")?; - require_dir(&root, &lib_dir, "lib")?; - require_dir(&root, &templates_dir, "templates")?; - - let mut packages = Vec::new(); - collect_package_files(&packages_dir, &packages_dir, &mut packages)?; - packages.sort_by_key(|package| package.id.to_string()); - - Ok(Self { - root, - metadata, - packages_dir, - lib_dir, - templates_dir, - packages, - }) - } - - pub fn package_file(&self, id: &PackageId) -> Option<&PackageFile> { - self.packages.iter().find(|package| &package.id == id) - } -} - -#[derive(Debug, Deserialize)] -struct RawRepositoryMetadata { - id: String, - name: String, - #[serde(default)] - priority: Option, - api_version: String, -} - -fn require_dir( - root: &Path, - path: &Path, - directory: &'static str, -) -> Result<(), RepositoryLoadError> { - if path.is_dir() { - Ok(()) - } else { - Err(RepositoryLoadError::MissingDirectory { - path: root.to_path_buf(), - directory, - }) - } -} - -fn collect_package_files( - packages_root: &Path, - current: &Path, - out: &mut Vec, -) -> Result<(), RepositoryLoadError> { - for entry in read_dir_entries(current)? { - let path = entry.path(); - let file_type = entry_file_type(&entry, current)?; - if file_type.is_dir() { - collect_package_files(packages_root, &path, out)?; - } else if file_type.is_file() - && path - .extension() - .is_some_and(|ext| ext == LUA_SCRIPT_EXTENSION) - { - let id = package_id_from_path(packages_root, &path)?; - out.push(PackageFile { id, path }); - } - } - Ok(()) -} - fn collect_package_directories( repository_root: &Path, current: &Path, @@ -773,27 +640,6 @@ pub fn package_id_from_package_dir( package_id_from_relative_path(package_dir, relative) } -pub fn package_id_from_path( - packages_root: impl AsRef, - path: impl AsRef, -) -> Result { - let packages_root = packages_root.as_ref(); - let path = path.as_ref(); - let relative = - path.strip_prefix(packages_root) - .map_err(|_| RepositoryLoadError::InvalidPackagePath { - path: path.to_path_buf(), - reason: format!("path is not under {}", packages_root.display()), - })?; - if relative.extension().is_none_or(|ext| ext != "lua") { - return Err(RepositoryLoadError::InvalidPackagePath { - path: path.to_path_buf(), - reason: "package file must have .lua extension".to_owned(), - }); - } - package_id_from_relative_path(path, &relative.with_extension("")) -} - fn package_id_from_relative_path( path: &Path, relative: &Path, @@ -826,18 +672,6 @@ fn package_id_from_relative_path( }) } -pub fn package_cache_key( - repository: &RepositoryLayout, - package: &PackageFile, -) -> Result { - Ok(RepositoryPackageCacheKey { - repository_id: repository.metadata.id.clone(), - package_id: package.id.clone(), - api_version: repository.metadata.api_version.clone(), - package_file_hash: package_file_content_hash(&package.path)?, - }) -} - pub fn package_directory_cache_key( repository_id: &RepositoryId, package: &PackageDirectory, @@ -973,7 +807,6 @@ where #[cfg(test)] mod tests { use super::*; - use std::io::Write; #[test] fn data_dir_layout_uses_repo_and_rc_roots() { @@ -1123,14 +956,6 @@ mod tests { )); } - #[test] - fn derives_package_id_from_lua_path() { - let root = PathBuf::from("repo/packages"); - let id = - package_id_from_path(&root, "repo/packages/android/org.fdroid.fdroid.lua").unwrap(); - assert_eq!(id.to_string(), "android/org.fdroid.fdroid"); - } - #[test] fn derives_package_id_from_package_directory() { let root = PathBuf::from("repo/official"); @@ -1280,77 +1105,6 @@ mod tests { .contains("unsupported package kind")); } - #[test] - fn loads_repository_layout_with_required_directories() { - let temp = tempfile::tempdir().unwrap(); - let root = temp.path(); - fs::write( - root.join("repo.toml"), - r#"id = "official" -name = "UpgradeAll Official" -priority = 0 -api_version = "getter.repo.v1" -"#, - ) - .unwrap(); - fs::create_dir(root.join("packages")).unwrap(); - fs::create_dir(root.join("packages/android")).unwrap(); - fs::create_dir(root.join("lib")).unwrap(); - fs::create_dir(root.join("templates")).unwrap(); - let mut file = - fs::File::create(root.join("packages/android/org.fdroid.fdroid.lua")).unwrap(); - writeln!(file, "return {{ id = 'android/org.fdroid.fdroid' }}").unwrap(); - - let layout = RepositoryLayout::load(root).unwrap(); - assert_eq!(layout.metadata.id.as_str(), "official"); - assert_eq!(layout.metadata.priority, RepositoryPriority::DEFAULT); - assert_eq!(layout.packages.len(), 1); - assert_eq!( - layout.packages[0].id.to_string(), - "android/org.fdroid.fdroid" - ); - } - - #[test] - fn package_cache_key_changes_when_package_file_content_changes() { - let temp = tempfile::tempdir().unwrap(); - let root = temp.path(); - fs::write( - root.join("repo.toml"), - r#"id = "official" -name = "UpgradeAll Official" -priority = 0 -api_version = "getter.repo.v1" -"#, - ) - .unwrap(); - fs::create_dir(root.join("packages")).unwrap(); - fs::create_dir(root.join("packages/android")).unwrap(); - fs::create_dir(root.join("lib")).unwrap(); - fs::create_dir(root.join("templates")).unwrap(); - let package_path = root.join("packages/android/org.fdroid.fdroid.lua"); - fs::write( - &package_path, - r#"return { id = "android/org.fdroid.fdroid", name = "F-Droid" }"#, - ) - .unwrap(); - let layout = RepositoryLayout::load(root).unwrap(); - let first = package_cache_key(&layout, &layout.packages[0]).unwrap(); - - fs::write( - &package_path, - r#"return { id = "android/org.fdroid.fdroid", name = "F-Droid Nightly" }"#, - ) - .unwrap(); - let layout = RepositoryLayout::load(root).unwrap(); - let second = package_cache_key(&layout, &layout.packages[0]).unwrap(); - - assert_eq!(first.repository_id.as_str(), "official"); - assert_eq!(first.package_id.to_string(), "android/org.fdroid.fdroid"); - assert_eq!(first.api_version, REPO_API_VERSION_V1); - assert_ne!(first.package_file_hash, second.package_file_hash); - } - #[test] fn package_directory_cache_key_changes_when_local_files_change() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs index 0b597f4..770d3bf 100644 --- a/crates/getter-operations/src/autogen.rs +++ b/crates/getter-operations/src/autogen.rs @@ -13,7 +13,7 @@ use getter_core::autogen::{ DEFAULT_AUTOGEN_REPOSITORY_ID, DEFAULT_AUTOGEN_REPOSITORY_NAME, INSTALLED_AUTOGEN_GENERATOR, }; use getter_core::repository::{ - generated_repository_target, GeneratedRepositoryTarget, GetterDataDirLayout, RepositoryLayout, + generated_repository_target, GeneratedRepositoryTarget, GetterDataDirLayout, RepositoryLoadError, RepositoryMetadata, RepositoryPackageDirectoryLayout, RepositoryRootConfig, REPO_API_VERSION_V1, }; @@ -358,23 +358,13 @@ pub(crate) fn higher_priority_package_coverage( } fn load_repository_package_ids(path: &Path) -> AutogenOperationResult> { - if path.join("repo.toml").is_file() { - let layout = RepositoryLayout::load(path) - .map_err(|source| AutogenOperationError::Repository(source.to_string()))?; - Ok(layout - .packages - .into_iter() - .map(|package| package.id) - .collect()) - } else { - let layout = RepositoryPackageDirectoryLayout::load(path) - .map_err(|source| AutogenOperationError::Repository(source.to_string()))?; - Ok(layout - .packages - .into_iter() - .map(|package| package.id) - .collect()) - } + let layout = RepositoryPackageDirectoryLayout::load(path) + .map_err(|source| AutogenOperationError::Repository(source.to_string()))?; + Ok(layout + .packages + .into_iter() + .map(|package| package.id) + .collect()) } fn autogen_candidate_json(candidate: &AutogenCandidate) -> AutogenOperationResult { diff --git a/crates/getter-operations/src/read_model.rs b/crates/getter-operations/src/read_model.rs index 79f5cad..2c75b90 100644 --- a/crates/getter-operations/src/read_model.rs +++ b/crates/getter-operations/src/read_model.rs @@ -6,11 +6,9 @@ //! to parse and render. #[cfg(feature = "lua")] -use getter_core::lua::{evaluate_package_directory_script, evaluate_package_file}; +use getter_core::lua::evaluate_package_directory_script; #[cfg(feature = "lua")] -use getter_core::repository::{ - RepositoryLayout, RepositoryLoadError, RepositoryPackageDirectoryLayout, -}; +use getter_core::repository::{RepositoryLoadError, RepositoryPackageDirectoryLayout}; #[cfg(feature = "lua")] use getter_core::{PackageId, RepositoryId}; use getter_storage::{MainDb, StorageError, StoredRepository, StoredTrackedPackage}; @@ -171,16 +169,6 @@ fn evaluate_package_in_repository( package_id: &PackageId, ) -> Result, ReadModelOperationError> { let path = repo_path(repo)?; - if path.join("repo.toml").is_file() { - let layout = RepositoryLayout::load(&path)?; - let Some(package_file) = layout.package_file(package_id) else { - return Ok(None); - }; - return evaluate_package_file(&layout, &package_file.path) - .map(Some) - .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())); - } - let layout = RepositoryPackageDirectoryLayout::load(&path)?; let Some(package) = layout.package(package_id) else { return Ok(None); @@ -291,36 +279,6 @@ mod tests { ); } - #[cfg(feature = "lua")] - #[test] - fn package_eval_reads_registered_lua_repository() { - let temp = tempdir().unwrap(); - let repo_path = temp.path().join("repo"); - write_legacy_lua_repo(&repo_path); - - let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); - db.upsert_repository( - &RepositoryMetadata { - id: "official".parse().unwrap(), - name: "Official".to_owned(), - priority: RepositoryPriority::new(10), - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_path), - None, - ) - .unwrap(); - - let result = package_eval_json( - temp.path(), - r#"{"package_id":"android/org.fdroid.fdroid","repository_id":"official"}"#, - ) - .unwrap(); - assert_eq!(result["package"]["id"], "android/org.fdroid.fdroid"); - assert_eq!(result["package"]["name"], "F-Droid"); - assert_eq!(result["package"]["permissions"]["free_network"], true); - } - #[cfg(feature = "lua")] #[test] fn package_eval_reads_registered_package_directory_repository() { @@ -356,35 +314,6 @@ mod tests { ); } - #[cfg(feature = "lua")] - fn write_legacy_lua_repo(repo_path: &Path) { - fs::create_dir_all(repo_path.join("packages/android")).unwrap(); - fs::create_dir_all(repo_path.join("lib")).unwrap(); - fs::create_dir_all(repo_path.join("templates")).unwrap(); - fs::write( - repo_path.join("repo.toml"), - r#"id = "official" -name = "Official" -priority = 10 -api_version = "getter.repo.v1" -"#, - ) - .unwrap(); - fs::write( - repo_path.join("packages/android/org.fdroid.fdroid.lua"), - r#"return package_def { - id = "android/org.fdroid.fdroid", - name = "F-Droid", - installed = { - { kind = "android_package", package_name = "org.fdroid.fdroid" }, - }, - permissions = { free_network = true }, -} -"#, - ) - .unwrap(); - } - #[cfg(feature = "lua")] fn write_package_directory_repo(repo_path: &Path) { let package_dir = repo_path.join("android/app/com.example.autogen"); diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 3721eed..5a3b94e 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -8,10 +8,9 @@ #[cfg(feature = "lua")] use getter_core::{ - lua::{evaluate_package_directory_script, evaluate_package_file}, + lua::evaluate_package_directory_script, repository::{ - package_cache_key, package_directory_cache_key, RepositoryLayout, RepositoryLoadError, - RepositoryPackageDirectoryLayout, + package_directory_cache_key, RepositoryLoadError, RepositoryPackageDirectoryLayout, }, }; use getter_core::{ @@ -386,21 +385,6 @@ fn evaluate_registered_package( continue; }; let root = PathBuf::from(root); - if root.join("repo.toml").is_file() { - let layout = RepositoryLayout::load(&root)?; - let Some(package_file) = layout.package_file(&request.package_id) else { - continue; - }; - let package = evaluate_package_file(&layout, &package_file.path) - .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; - let cache_key = package_cache_key(&layout, package_file)?; - let dependency_digest = format!( - "repo:{}:package:{}:hash:{}", - cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash - ); - return Ok((package, dependency_digest)); - } - let layout = RepositoryPackageDirectoryLayout::load(&root)?; let Some(package_directory) = layout.package(&request.package_id) else { continue; @@ -465,46 +449,6 @@ mod tests { #[cfg(feature = "lua")] use std::fs; - #[cfg(feature = "lua")] - #[test] - fn registered_package_update_check_issues_action_from_lua_static_updates() { - let temp = tempfile::tempdir().unwrap(); - let repo_root = temp.path().join("repo"); - write_static_update_repo(&repo_root); - let db = MainDb::open_in_memory().unwrap(); - db.upsert_repository( - &RepositoryMetadata { - id: "official".parse().unwrap(), - name: "Official".to_owned(), - priority: RepositoryPriority::new(0), - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_root), - None, - ) - .unwrap(); - let mut runtime = GetterRuntime::new(); - - let issued = issue_action_from_registered_package_json( - &mut runtime, - &db, - &json!({ - "package_id": "android/org.fdroid.fdroid", - "installed_version": "1.0.0" - }) - .to_string(), - ) - .unwrap(); - - assert_eq!(issued["package"]["repository"], "official"); - assert_eq!(issued["update"]["status"], "update_available"); - let action_id = issued["action"]["action_id"].as_str().unwrap(); - let submitted = - submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) - .unwrap(); - assert_eq!(submitted["package_id"], "android/org.fdroid.fdroid"); - } - #[cfg(feature = "lua")] #[test] fn registered_package_update_check_issues_action_from_package_directory_static_updates() { @@ -551,13 +495,13 @@ mod tests { fn registered_package_update_check_without_update_does_not_issue_action() { let temp = tempfile::tempdir().unwrap(); let repo_root = temp.path().join("repo"); - write_static_update_repo(&repo_root); + write_package_directory_static_update_repo(&repo_root); let db = MainDb::open_in_memory().unwrap(); db.upsert_repository( &RepositoryMetadata { - id: "official".parse().unwrap(), - name: "Official".to_owned(), - priority: RepositoryPriority::new(0), + id: "autogen".parse().unwrap(), + name: "Autogen".to_owned(), + priority: RepositoryPriority::new(-1), api_version: REPO_API_VERSION_V1.to_owned(), }, Some(&repo_root), @@ -570,7 +514,7 @@ mod tests { &mut runtime, &db, &json!({ - "package_id": "android/org.fdroid.fdroid", + "package_id": "android/app/com.example.autogen", "installed_version": "1.2.0" }) .to_string(), @@ -706,44 +650,6 @@ mod tests { runtime.submit_action(&action.action_id).unwrap().task_id } - #[cfg(feature = "lua")] - fn write_static_update_repo(root: &std::path::Path) { - fs::create_dir_all(root.join("packages/android")).unwrap(); - fs::create_dir(root.join("lib")).unwrap(); - fs::create_dir(root.join("templates")).unwrap(); - fs::write( - root.join("repo.toml"), - r#"id = "official" -name = "Official" -priority = 0 -api_version = "getter.repo.v1" -"#, - ) - .unwrap(); - fs::write( - root.join("packages/android/org.fdroid.fdroid.lua"), - r#" -return package_def { - id = "android/org.fdroid.fdroid", - name = "F-Droid", - updates = { - { - version = "1.2.0", - artifacts = { - { - name = "app.apk", - url = "https://example.invalid/app.apk", - file_name = "app.apk", - }, - }, - }, - }, -} -"#, - ) - .unwrap(); - } - #[cfg(feature = "lua")] fn write_package_directory_static_update_repo(root: &std::path::Path) { let package_dir = root.join("android/app/com.example.autogen"); From a67e8227ffefae4b6665ec1a4a2e29e4839fe3e1 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 16:46:46 +0800 Subject: [PATCH 42/72] feat(providers): add GitHub latest commit fixtures --- crates/getter-cli/src/lib.rs | 226 ++++++++- crates/getter-cli/tests/bdd_cli.rs | 74 +++ .../cli/provider_github_latest_commit.feature | 10 + .../src/github_latest_commit.rs | 477 ++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + crates/getter-providers/src/lib.rs | 164 +++++- 6 files changed, 950 insertions(+), 2 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/provider_github_latest_commit.feature create mode 100644 crates/getter-operations/src/github_latest_commit.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 7c7efbe..0d32f2f 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -23,6 +23,7 @@ use getter_downloader::{ }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; use getter_operations::fdroid_autogen; +use getter_operations::github_latest_commit::{self, GithubLatestCommitOperationError}; use getter_operations::github_releases::{self, GithubReleaseOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; use getter_operations::runtime as runtime_operations; @@ -146,6 +147,13 @@ pub enum CliCommand { include_prereleases: bool, refresh: bool, }, + ProviderGithubLatestCommit { + owner: String, + repo: String, + reference: Option, + commit: PathBuf, + refresh: bool, + }, LegacyImportRoomBundle { bundle: PathBuf, }, @@ -329,6 +337,16 @@ impl From for CliError { } } +impl From for CliError { + fn from(value: GithubLatestCommitOperationError) -> Self { + match value { + GithubLatestCommitOperationError::Storage(source) => Self::Storage(source.to_string()), + GithubLatestCommitOperationError::InvalidRequest(detail) => Self::Usage(detail), + other => Self::Provider(other.to_string()), + } + } +} + impl From for CliError { fn from(value: GithubReleaseOperationError) -> Self { match value { @@ -599,6 +617,18 @@ where refresh: args.refresh, } } + [domain, provider, action, rest @ ..] + if domain == "provider" && provider == "github" && action == "latest-commit" => + { + let args = parse_provider_github_latest_commit_args(rest)?; + CliCommand::ProviderGithubLatestCommit { + owner: args.owner, + repo: args.repo, + reference: args.reference, + commit: args.commit, + refresh: args.refresh, + } + } [domain, subject, action, flag, preview, rest @ ..] if domain == "autogen" && subject == "cleanup" @@ -863,6 +893,25 @@ fn execute(invocation: CliInvocation) -> Result { }); github_releases::github_releases_json(&db, &request.to_string()).map_err(CliError::from) } + CliCommand::ProviderGithubLatestCommit { + owner, + repo, + reference, + commit, + refresh, + } => { + let db = open_cache_db(&invocation.data_dir)?; + let commit_json = read_github_commit_fixture(&commit)?; + let request = json!({ + "owner": owner, + "repo": repo, + "ref": reference, + "mode": if refresh { "force_refresh" } else { "use_cached" }, + "commit_json": commit_json, + }); + github_latest_commit::github_latest_commit_json(&db, &request.to_string()) + .map_err(CliError::from) + } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { @@ -1099,6 +1148,15 @@ struct ProviderGithubReleasesArgs { refresh: bool, } +#[derive(Debug, Default, PartialEq, Eq)] +struct ProviderGithubLatestCommitArgs { + owner: String, + repo: String, + reference: Option, + commit: PathBuf, + refresh: bool, +} + fn parse_provider_github_releases_args( args: &[String], ) -> Result { @@ -1198,6 +1256,87 @@ fn parse_provider_github_releases_args( Ok(parsed) } +fn parse_provider_github_latest_commit_args( + args: &[String], +) -> Result { + let mut parsed = ProviderGithubLatestCommitArgs::default(); + let mut position = 0; + while position < args.len() { + match args[position].as_str() { + "--owner" => { + parsed.owner = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github latest-commit --owner requires an owner".to_owned(), + ) + })? + .clone(); + position += 2; + } + "--repo" => { + parsed.repo = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github latest-commit --repo requires a repo".to_owned(), + ) + })? + .clone(); + position += 2; + } + "--commit" => { + let path = args.get(position + 1).ok_or_else(|| { + CliError::Usage( + "provider github latest-commit --commit requires a fixture path".to_owned(), + ) + })?; + parsed.commit = PathBuf::from(path); + position += 2; + } + "--ref" => { + parsed.reference = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github latest-commit --ref requires a ref".to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--refresh" => { + parsed.refresh = true; + position += 1; + } + other => { + return Err(CliError::Usage(format!( + "unsupported provider github latest-commit argument '{other}'" + ))) + } + } + } + + if parsed.owner.trim().is_empty() { + return Err(CliError::Usage( + "provider github latest-commit requires --owner ".to_owned(), + )); + } + if parsed.repo.trim().is_empty() { + return Err(CliError::Usage( + "provider github latest-commit requires --repo ".to_owned(), + )); + } + if parsed.commit.as_os_str().is_empty() { + return Err(CliError::Usage( + "provider github latest-commit requires --commit ".to_owned(), + )); + } + + Ok(parsed) +} + fn parse_autogen_acceptance(args: &[String]) -> Result { match args { [flag] if flag == "--accept-all" => Ok(AutogenAcceptance::AcceptAll), @@ -1340,6 +1479,12 @@ fn read_github_releases_fixture(path: &Path) -> Result { }) } +fn read_github_commit_fixture(path: &Path) -> Result { + fs::read_to_string(path).map_err(|source| { + CliError::Provider(format!("failed to read GitHub commit fixture: {source}")) + }) +} + fn read_installed_inventory(path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; @@ -1869,7 +2014,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1939,6 +2084,7 @@ impl CliCommand { Self::AutogenFdroidPreview { .. } => "autogen fdroid preview", Self::AutogenFdroidApply { .. } => "autogen fdroid apply", Self::ProviderGithubReleases { .. } => "provider github releases", + Self::ProviderGithubLatestCommit { .. } => "provider github latest-commit", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", Self::LegacyImportRoomDb { .. } => "legacy import-room-db", Self::LegacyReportList => "legacy report-list", @@ -1996,6 +2142,84 @@ mod tests { ); } + #[test] + fn parses_provider_github_latest_commit_command() { + let parsed = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "provider", + "github", + "latest-commit", + "--owner", + "DUpdateSystem", + "--repo", + "UpgradeAll", + "--commit", + "/tmp/commit.json", + "--ref", + "main", + "--refresh", + ]) + .unwrap(); + + assert_eq!( + parsed.command, + CliCommand::ProviderGithubLatestCommit { + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + reference: Some("main".to_owned()), + commit: PathBuf::from("/tmp/commit.json"), + refresh: true, + } + ); + } + + #[test] + fn provider_github_latest_commit_missing_cli_flags_is_usage_error() { + let output = run([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "provider", + "github", + "latest-commit", + "--owner", + "DUpdateSystem", + ]); + + assert_eq!(output.exit_code, ExitCode::Usage); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["error"]["code"], "cli.usage"); + } + + #[test] + fn provider_github_latest_commit_malformed_fixture_is_provider_error() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + let commit = temp.path().join("commit.json"); + fs::write(&commit, "not json").unwrap(); + + let output = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "provider".to_owned(), + "github".to_owned(), + "latest-commit".to_owned(), + "--owner".to_owned(), + "DUpdateSystem".to_owned(), + "--repo".to_owned(), + "UpgradeAll".to_owned(), + "--commit".to_owned(), + commit.to_string_lossy().to_string(), + ]); + + assert_eq!(output.exit_code, ExitCode::Provider); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["error"]["code"], "provider.error"); + } + #[test] fn old_task_namespace_is_not_public_cli_surface() { let output = run(["getter", "--data-dir", "/tmp/ua-getter", "task", "list"]); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index f5f0887..b0a05e2 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -17,6 +17,7 @@ struct CliWorld { update_fixture: Option, fdroid_index: Option, github_releases: Option, + github_commit: Option, task_request: Option, runtime_script: Option, remembered_task_id: Option, @@ -240,6 +241,31 @@ fn fixture_github_releases_response(world: &mut CliWorld, project: String) { world.github_releases = Some(releases); } +#[given(expr = "a fixture GitHub commit response for {string}")] +fn fixture_github_commit_response(world: &mut CliWorld, project: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let commit = temp.path().join("github-commit.json"); + fs::write( + &commit, + serde_json::to_vec_pretty(&serde_json::json!({ + "sha": "0123456789abcdef0123456789abcdef01234567", + "html_url": "https://github.com/DUpdateSystem/UpgradeAll/commit/0123456789abcdef0123456789abcdef01234567", + "commit": { + "message": format!("{project} live commit"), + "author": { + "date": "2026-06-02T03:04:05Z" + }, + "committer": { + "date": "2026-06-02T04:05:06Z" + } + } + })) + .expect("GitHub commit serializes"), + ) + .expect("write GitHub commit fixture"); + world.github_commit = Some(commit); +} + #[given("an empty installed inventory")] fn empty_installed_inventory(world: &mut CliWorld) { let temp = world.temp.as_ref().expect("tempdir exists"); @@ -985,6 +1011,30 @@ fn run_getter_provider_github_releases(world: &mut CliWorld, owner: String, repo world.json = None; } +#[when(expr = "I run getter provider github latest-commit for owner {string} repo {string}")] +fn run_getter_provider_github_latest_commit(world: &mut CliWorld, owner: String, repo: String) { + let commit = world + .github_commit + .as_ref() + .expect("GitHub commit fixture exists"); + let output = run_getter( + world, + [ + "provider".to_owned(), + "github".to_owned(), + "latest-commit".to_owned(), + "--owner".to_owned(), + owner, + "--repo".to_owned(), + repo, + "--commit".to_owned(), + commit.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen fdroid apply for that preview with accept-all")] fn run_getter_autogen_fdroid_apply_accept_all(world: &mut CliWorld) { let preview = world @@ -1605,6 +1655,30 @@ fn github_release_provider_returns_candidate( assert!(json["data"]["diagnostics"].as_array().unwrap().is_empty()); } +#[then(expr = "the GitHub latest-commit provider returns live revision {string}")] +fn github_latest_commit_provider_returns_live_revision(world: &mut CliWorld, revision: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "provider github latest-commit"); + assert_eq!(json["data"]["operation"], "github.latest_commit"); + assert_eq!(json["data"]["provider"], "github"); + assert_eq!(json["data"]["source"], "refreshed"); + assert_eq!(json["data"]["live"], true); + assert_eq!(json["data"]["version"], revision); + assert_eq!(json["data"]["revision"], revision); + assert_eq!(json["data"]["latest_commit"]["revision"], revision); + assert_eq!( + json["data"]["latest_commit"]["published_at"], + "2026-06-02T03:04:05Z" + ); + assert!(json["data"].get("candidates").is_none()); + assert!(json["data"].get("selected_update").is_none()); + assert!(json["data"].get("artifacts").is_none()); + assert!(json["data"].get("actions").is_none()); + assert!(json["data"].get("action_id").is_none()); + assert!(json["data"]["diagnostics"].as_array().unwrap().is_empty()); +} + #[then(expr = "the autogen repository contains generated F-Droid package {string}")] fn autogen_repository_contains_generated_fdroid_package(world: &mut CliWorld, package_id: String) { autogen_repository_contains_generated_package(world, package_id.clone()); diff --git a/crates/getter-cli/tests/features/cli/provider_github_latest_commit.feature b/crates/getter-cli/tests/features/cli/provider_github_latest_commit.feature new file mode 100644 index 0000000..4cdfbbd --- /dev/null +++ b/crates/getter-cli/tests/features/cli/provider_github_latest_commit.feature @@ -0,0 +1,10 @@ +@getter-cli @provider @github +Feature: GitHub latest commit provider fixtures + Scenario: User queries fixture-backed GitHub latest commit as a live revision + Given an initialized getter data directory + And a fixture GitHub commit response for "DUpdateSystem/UpgradeAll" + When I run getter provider github latest-commit for owner "DUpdateSystem" repo "UpgradeAll" + Then the command succeeds + And the output is valid JSON + And the GitHub latest-commit provider returns live revision "0123456789abcdef0123456789abcdef01234567" + And the autogen repository has not been written diff --git a/crates/getter-operations/src/github_latest_commit.rs b/crates/getter-operations/src/github_latest_commit.rs new file mode 100644 index 0000000..15e8b7e --- /dev/null +++ b/crates/getter-operations/src/github_latest_commit.rs @@ -0,0 +1,477 @@ +//! Fixture-backed GitHub latest-commit cache/query operations. +//! +//! This ADR-0012 provider slice models latest-commit checks as live/floating +//! provider facts. It parses controlled GitHub commit fixtures through the +//! shared provider cache without producing ordinary release candidates, runtime +//! actions, downloads, installer handoffs, or Flutter/Kotlin provider parsing. + +use crate::provider_cache::{ + read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, + ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, +}; +use getter_providers::{ + github_latest_commit_live_revision, parse_github_commit_json, GithubCommit, GithubLiveRevision, + GithubProviderError, +}; +use getter_storage::{CacheDb, StorageError}; +use serde::Deserialize; +use serde_json::{json, Value}; +use sha2::{Digest, Sha512}; + +pub const GITHUB_PROVIDER_ID: &str = "github"; +pub const GITHUB_LATEST_COMMIT_CACHE_VERSION: &str = "github-latest-commit-v1"; +pub const DEFAULT_GITHUB_API_BASE_URL: &str = "https://api.github.com"; +pub const DEFAULT_GITHUB_REF: &str = "HEAD"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GithubLatestCommitConfig { + pub api_base_url: String, + pub owner: String, + pub repo: String, + pub reference: String, +} + +impl GithubLatestCommitConfig { + pub fn cache_key(&self) -> String { + format!( + "{GITHUB_PROVIDER_ID}:{GITHUB_LATEST_COMMIT_CACHE_VERSION}:latest_commit:{}:{}/{}/{}", + digest_hex(&self.api_base_url), + self.owner, + self.repo, + self.reference, + ) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GithubLatestCommitResult { + pub config: GithubLatestCommitConfig, + pub cache_key: String, + pub commit: GithubCommit, + pub live_revision: GithubLiveRevision, + pub source: ProviderCacheSource, + pub diagnostics: Vec, +} + +#[derive(Debug, thiserror::Error)] +pub enum GithubLatestCommitOperationError { + #[error("provider cache operation failed: {0}")] + Cache(#[from] ProviderCacheOperationError), + #[error("storage error: {0}")] + Storage(#[from] StorageError), + #[error("GitHub provider parse failed: {0}")] + Provider(#[from] GithubProviderError), + #[error("GitHub provider serialization failed: {0}")] + Serialization(#[from] serde_json::Error), + #[error("invalid GitHub latest-commit request: {0}")] + InvalidRequest(String), +} + +pub fn read_or_refresh_github_latest_commit( + db: &CacheDb, + config: GithubLatestCommitConfig, + mode: ProviderCacheMode, + refresh_json: F, +) -> Result +where + F: FnOnce() -> Result, +{ + let cache_key = config.cache_key(); + let cache_result = read_or_refresh_provider_response( + db, + ProviderCacheRequest { + cache_key: &cache_key, + provider: GITHUB_PROVIDER_ID, + mode, + }, + || { + let json = refresh_json()?; + let commit = parse_github_commit_json(&json).map_err(|source| source.to_string())?; + github_latest_commit_live_revision(&commit).map_err(|source| source.to_string())?; + serde_json::to_value(commit).map_err(|source| source.to_string()) + }, + )?; + let commit = serde_json::from_value(cache_result.response.response_json)?; + let live_revision = github_latest_commit_live_revision(&commit)?; + + Ok(GithubLatestCommitResult { + config, + cache_key, + commit, + live_revision, + source: cache_result.source, + diagnostics: cache_result.diagnostics, + }) +} + +pub fn github_latest_commit_json( + db: &CacheDb, + request_json: &str, +) -> Result { + let request: GithubLatestCommitJsonRequest = serde_json::from_str(request_json)?; + let owner = required_non_empty(request.owner, "owner")?; + let repo = required_non_empty(request.repo, "repo")?; + let reference = request + .reference + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_REF.to_owned()); + let config = GithubLatestCommitConfig { + api_base_url: request + .api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()), + owner, + repo, + reference, + }; + let mode = match request.mode.as_deref() { + Some("force_refresh") => ProviderCacheMode::ForceRefresh, + Some("use_cached") | None => ProviderCacheMode::UseCached, + Some(other) => { + return Err(GithubLatestCommitOperationError::InvalidRequest(format!( + "unknown mode '{other}'" + ))) + } + }; + let result = read_or_refresh_github_latest_commit(db, config, mode, || { + request.commit_json.ok_or_else(|| { + "fixture-backed GitHub latest-commit refresh requires commit_json".to_owned() + }) + })?; + + Ok(json!({ + "operation": "github.latest_commit", + "provider": GITHUB_PROVIDER_ID, + "api_base_url": result.config.api_base_url, + "owner": result.config.owner, + "repo": result.config.repo, + "ref": result.config.reference, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "live": true, + "version": result.live_revision.version, + "revision": result.live_revision.revision, + "latest_commit": result.live_revision, + "commit": result.commit, + "diagnostics": result.diagnostics.iter().map(diagnostic_json).collect::>(), + })) +} + +#[derive(Debug, Deserialize)] +struct GithubLatestCommitJsonRequest { + #[serde(default)] + api_base_url: Option, + #[serde(default)] + owner: Option, + #[serde(default)] + repo: Option, + #[serde(default, rename = "ref")] + reference: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + commit_json: Option, +} + +fn required_non_empty( + value: Option, + field: &'static str, +) -> Result { + value + .filter(|value| !value.trim().is_empty()) + .ok_or_else(|| GithubLatestCommitOperationError::InvalidRequest(format!("missing {field}"))) +} + +fn provider_source_json(source: ProviderCacheSource) -> &'static str { + match source { + ProviderCacheSource::Cache => "cache", + ProviderCacheSource::Refreshed => "refreshed", + ProviderCacheSource::Stale => "stale", + } +} + +fn diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) +} + +fn digest_hex(value: &str) -> String { + let mut hasher = Sha512::new(); + hasher.update(value.as_bytes()); + format!("{:x}", hasher.finalize()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::provider_cache::{CACHE_REFRESH_FAILED, USED_STALE_CACHE}; + use std::cell::Cell; + + const GITHUB_COMMIT_FIXTURE: &str = r#"{ + "sha": "0123456789abcdef0123456789abcdef01234567", + "html_url": "https://github.com/DUpdateSystem/UpgradeAll/commit/0123456789abcdef0123456789abcdef01234567", + "commit": { + "message": "Update app metadata", + "author": { + "date": "2026-06-02T03:04:05Z" + }, + "committer": { + "date": "2026-06-02T04:05:06Z" + } + } +}"#; + + const UPDATED_COMMIT_FIXTURE: &str = r#"{ + "sha": "fedcba9876543210fedcba9876543210fedcba98", + "html_url": "https://github.com/DUpdateSystem/UpgradeAll/commit/fedcba9876543210fedcba9876543210fedcba98", + "commit": { + "message": "Refresh generated packages", + "committer": { + "date": "2026-06-03T03:04:05Z" + } + } +}"#; + + const EMPTY_SHA_COMMIT_FIXTURE: &str = r#"{ + "sha": " ", + "commit": { + "message": "Broken fixture" + } +}"#; + + fn config() -> GithubLatestCommitConfig { + GithubLatestCommitConfig { + api_base_url: DEFAULT_GITHUB_API_BASE_URL.to_owned(), + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + reference: DEFAULT_GITHUB_REF.to_owned(), + } + } + + #[test] + fn cache_key_identifies_latest_commit_request_and_ref() { + let main = config(); + let mut branch = config(); + branch.reference = "main".to_owned(); + let releases = crate::github_releases::GithubReleaseConfig { + api_base_url: crate::github_releases::DEFAULT_GITHUB_API_BASE_URL.to_owned(), + owner: main.owner.clone(), + repo: main.repo.clone(), + }; + + assert!(main + .cache_key() + .contains(GITHUB_LATEST_COMMIT_CACHE_VERSION)); + assert!(main.cache_key().contains(":latest_commit:")); + assert_ne!(main.cache_key(), branch.cache_key()); + assert_ne!(main.cache_key(), releases.cache_key()); + } + + #[test] + fn cache_miss_parses_and_stores_fixture_commit_as_live_revision() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + let cache_key = config.cache_key(); + + let result = + read_or_refresh_github_latest_commit(&db, config, ProviderCacheMode::UseCached, || { + Ok(GITHUB_COMMIT_FIXTURE.to_owned()) + }) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert!(result.live_revision.live); + assert_eq!( + result.live_revision.revision, + "0123456789abcdef0123456789abcdef01234567" + ); + assert_eq!( + result.live_revision.published_at.as_deref(), + Some("2026-06-02T03:04:05Z") + ); + let cached = db.provider_response(&cache_key).unwrap().unwrap(); + assert_eq!(cached.provider, GITHUB_PROVIDER_ID); + assert_eq!( + cached.response_json["sha"], + "0123456789abcdef0123456789abcdef01234567" + ); + } + + #[test] + fn cache_hit_avoids_refreshing_fixture_commit() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_latest_commit(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_COMMIT_FIXTURE.to_owned()) + }) + .unwrap(); + let refreshed = Cell::new(false); + + let result = read_or_refresh_github_latest_commit( + &db, + config(), + ProviderCacheMode::UseCached, + || { + refreshed.set(true); + Ok(UPDATED_COMMIT_FIXTURE.to_owned()) + }, + ) + .unwrap(); + + assert!(!refreshed.get()); + assert_eq!(result.source, ProviderCacheSource::Cache); + assert_eq!( + result.live_revision.revision, + "0123456789abcdef0123456789abcdef01234567" + ); + } + + #[test] + fn forced_refresh_replaces_cached_fixture_commit() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_latest_commit(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_COMMIT_FIXTURE.to_owned()) + }) + .unwrap(); + + let result = read_or_refresh_github_latest_commit( + &db, + config(), + ProviderCacheMode::ForceRefresh, + || Ok(UPDATED_COMMIT_FIXTURE.to_owned()), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!( + result.live_revision.revision, + "fedcba9876543210fedcba9876543210fedcba98" + ); + } + + #[test] + fn forced_refresh_failure_returns_stale_commit_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_latest_commit(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_COMMIT_FIXTURE.to_owned()) + }) + .unwrap(); + + let result = read_or_refresh_github_latest_commit( + &db, + config(), + ProviderCacheMode::ForceRefresh, + || Err("HTTP 503".to_owned()), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!( + result.live_revision.revision, + "0123456789abcdef0123456789abcdef01234567" + ); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + } + + #[test] + fn cache_miss_with_empty_sha_is_an_error_and_does_not_store_cache() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + let cache_key = config.cache_key(); + + let error = + read_or_refresh_github_latest_commit(&db, config, ProviderCacheMode::UseCached, || { + Ok(EMPTY_SHA_COMMIT_FIXTURE.to_owned()) + }) + .unwrap_err(); + + assert!(matches!( + error, + GithubLatestCommitOperationError::Cache(ProviderCacheOperationError::RefreshFailed(_)) + )); + assert!(db.provider_response(&cache_key).unwrap().is_none()); + } + + #[test] + fn forced_refresh_with_empty_sha_returns_stale_commit_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_latest_commit(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_COMMIT_FIXTURE.to_owned()) + }) + .unwrap(); + + let result = read_or_refresh_github_latest_commit( + &db, + config(), + ProviderCacheMode::ForceRefresh, + || Ok(EMPTY_SHA_COMMIT_FIXTURE.to_owned()), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!( + result.live_revision.revision, + "0123456789abcdef0123456789abcdef01234567" + ); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + } + + #[test] + fn json_operation_returns_live_revision_from_cached_commit() { + let db = CacheDb::open_in_memory().unwrap(); + let first = github_latest_commit_json( + &db, + &json!({ + "owner": "DUpdateSystem", + "repo": "UpgradeAll", + "commit_json": GITHUB_COMMIT_FIXTURE + }) + .to_string(), + ) + .unwrap(); + assert_eq!(first["source"], "refreshed"); + + let second = github_latest_commit_json( + &db, + &json!({ + "owner": "DUpdateSystem", + "repo": "UpgradeAll" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(second["operation"], "github.latest_commit"); + assert_eq!(second["ref"], DEFAULT_GITHUB_REF); + assert_eq!(second["source"], "cache"); + assert_eq!(second["live"], true); + assert!(second.get("candidates").is_none()); + assert!(second.get("selected_update").is_none()); + assert!(second.get("artifacts").is_none()); + assert!(second.get("actions").is_none()); + assert!(second.get("action_id").is_none()); + assert_eq!( + second["revision"], + "0123456789abcdef0123456789abcdef01234567" + ); + assert_eq!( + second["latest_commit"]["published_at"], + "2026-06-02T03:04:05Z" + ); + assert!(second["latest_commit"]["live"].as_bool().unwrap()); + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 46511ea..30097ff 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -8,6 +8,7 @@ pub mod autogen; pub mod fdroid_autogen; pub mod fdroid_catalog; +pub mod github_latest_commit; pub mod github_releases; pub mod legacy_room; pub mod provider_cache; diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index 6a9ebfb..a71cd49 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -262,6 +262,42 @@ pub struct GithubReleaseAsset { pub digest: Option, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubCommit { + pub sha: String, + #[serde(default)] + pub html_url: Option, + #[serde(default)] + pub commit: GithubCommitDetails, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubCommitDetails { + #[serde(default)] + pub message: Option, + #[serde(default)] + pub author: Option, + #[serde(default)] + pub committer: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubCommitActor { + #[serde(default)] + pub date: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubLiveRevision { + pub version: String, + pub revision: String, + pub live: bool, + pub source: String, + pub published_at: Option, + pub html_url: Option, + pub message: Option, +} + #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct GithubReleaseCandidateOptions { pub include_prereleases: bool, @@ -276,8 +312,13 @@ pub struct GithubAssetFilter { #[derive(Debug, thiserror::Error)] pub enum GithubProviderError { - #[error("failed to parse GitHub releases JSON: {0}")] + #[error("failed to parse GitHub provider JSON: {0}")] Json(#[from] serde_json::Error), + #[error("invalid GitHub commit field {field}: {message}")] + InvalidCommit { + field: &'static str, + message: String, + }, #[error("invalid GitHub asset {filter_kind} regex '{pattern}': {source}")] AssetFilterRegex { filter_kind: &'static str, @@ -291,6 +332,43 @@ pub fn parse_github_releases_json(json: &str) -> Result, Gith Ok(serde_json::from_str(json)?) } +pub fn parse_github_commit_json(json: &str) -> Result { + Ok(serde_json::from_str(json)?) +} + +pub fn github_latest_commit_live_revision( + commit: &GithubCommit, +) -> Result { + let revision = commit.sha.trim(); + if revision.is_empty() { + return Err(GithubProviderError::InvalidCommit { + field: "sha", + message: "commit sha must not be empty".to_owned(), + }); + } + + Ok(GithubLiveRevision { + version: revision.to_owned(), + revision: revision.to_owned(), + live: true, + source: "github".to_owned(), + published_at: commit + .commit + .author + .as_ref() + .and_then(|author| author.date.clone()) + .or_else(|| { + commit + .commit + .committer + .as_ref() + .and_then(|committer| committer.date.clone()) + }), + html_url: commit.html_url.clone(), + message: commit.commit.message.clone(), + }) +} + pub fn github_release_update_candidates( releases: &[GithubRelease], options: &GithubReleaseCandidateOptions, @@ -580,6 +658,76 @@ mod tests { assert_eq!(candidates[0].artifacts[0].sha256, None); } + #[test] + fn parses_github_commit_json_into_live_revision() { + let commit = parse_github_commit_json(GITHUB_COMMIT_FIXTURE).unwrap(); + + assert_eq!(commit.sha, "0123456789abcdef0123456789abcdef01234567"); + let revision = github_latest_commit_live_revision(&commit).unwrap(); + + assert!(revision.live); + assert_eq!(revision.source, "github"); + assert_eq!(revision.version, "0123456789abcdef0123456789abcdef01234567"); + assert_eq!( + revision.revision, + "0123456789abcdef0123456789abcdef01234567" + ); + assert_eq!( + revision.published_at.as_deref(), + Some("2026-06-02T03:04:05Z") + ); + assert_eq!(revision.message.as_deref(), Some("Update app metadata")); + assert_eq!( + revision.html_url.as_deref(), + Some("https://github.com/DUpdateSystem/UpgradeAll/commit/0123456789abcdef0123456789abcdef01234567") + ); + } + + #[test] + fn falls_back_to_committer_date_for_live_revision() { + let commit = parse_github_commit_json( + r#"{ + "sha": "fedcba9876543210fedcba9876543210fedcba98", + "commit": { "committer": { "date": "2026-06-03T03:04:05Z" } } +}"#, + ) + .unwrap(); + + let revision = github_latest_commit_live_revision(&commit).unwrap(); + + assert_eq!( + revision.published_at.as_deref(), + Some("2026-06-03T03:04:05Z") + ); + } + + #[test] + fn omits_live_revision_date_when_commit_dates_are_missing() { + let commit = parse_github_commit_json( + r#"{ + "sha": "fedcba9876543210fedcba9876543210fedcba98", + "commit": { "message": "No dates" } +}"#, + ) + .unwrap(); + + let revision = github_latest_commit_live_revision(&commit).unwrap(); + + assert_eq!(revision.published_at, None); + } + + #[test] + fn rejects_empty_github_commit_sha() { + let commit = parse_github_commit_json(r#"{ "sha": "", "commit": {} }"#).unwrap(); + + let error = github_latest_commit_live_revision(&commit).unwrap_err(); + + assert!(matches!( + error, + GithubProviderError::InvalidCommit { field: "sha", .. } + )); + } + const GITHUB_RELEASES_FIXTURE: &str = r#"[ { "tag_name": "v1.2.0", @@ -651,4 +799,18 @@ mod tests { ] } ]"#; + + const GITHUB_COMMIT_FIXTURE: &str = r#"{ + "sha": "0123456789abcdef0123456789abcdef01234567", + "html_url": "https://github.com/DUpdateSystem/UpgradeAll/commit/0123456789abcdef0123456789abcdef01234567", + "commit": { + "message": "Update app metadata", + "author": { + "date": "2026-06-02T03:04:05Z" + }, + "committer": { + "date": "2026-06-02T04:05:06Z" + } + } +}"#; } From 496bc08b44b20fd02af00490194ec526647e7c7d Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 17:59:57 +0800 Subject: [PATCH 43/72] test(lua): cover F-Droid luaclass shape --- crates/getter-core/src/lua.rs | 80 +++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 2507fd7..186aa65 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -1008,6 +1008,86 @@ return android.package_version { assert_eq!(package.installed.len(), 1); } + #[test] + fn package_directory_can_use_repository_local_fdroid_luaclass_shape() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::create_dir_all(temp.path().join("luaclass")).unwrap(); + fs::write( + temp.path().join("luaclass/fdroid_android.lua"), + r#" +local fdroid = {} + +function fdroid.package(spec) + if spec.package_name ~= "org.fdroid.fdroid" then + error("F-Droid package_name fixture mismatch") + end + return package_version { + updates = { + { + version = "1.20.0", + version_code = 1020000, + source = "fdroid", + artifacts = { + { + name = "org.fdroid.fdroid_1020000.apk", + url = "https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk", + file_name = "org.fdroid.fdroid_1020000.apk", + }, + }, + }, + }, + } +end + +return fdroid +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "org.fdroid.fdroid", +} +"#, + ) + .unwrap(); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + + assert_eq!( + package.id.to_string(), + "android/f-droid/app/org.fdroid.fdroid" + ); + assert_eq!(package.repository.as_str(), "official"); + assert_eq!(package.name, "android/f-droid/app/org.fdroid.fdroid"); + assert_eq!( + package.installed, + vec![InstalledTarget::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned() + }] + ); + assert_eq!(package.updates.len(), 1); + assert_eq!(package.updates[0].version, "1.20.0"); + assert_eq!(package.updates[0].version_code, Some(1020000)); + assert_eq!(package.updates[0].source.as_deref(), Some("fdroid")); + assert_eq!( + package.updates[0].artifacts[0].file_name.as_deref(), + Some("org.fdroid.fdroid_1020000.apk") + ); + } + #[test] fn package_directory_can_read_package_local_files() { let temp = tempfile::tempdir().unwrap(); From d8cd8c97501c6ff20f49a7b0819eaeb372fde559 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 18:09:39 +0800 Subject: [PATCH 44/72] test(lua): cover GitHub luaclass shape --- crates/getter-core/src/lua.rs | 99 +++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 186aa65..e45801c 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -1088,6 +1088,105 @@ return fdroid.package { ); } + #[test] + fn package_directory_can_use_repository_local_github_android_luaclass_shape() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::create_dir_all(temp.path().join("luaclass")).unwrap(); + fs::write( + temp.path().join("luaclass/github_android_apk.lua"), + r#" +local github_android = {} + +function github_android.package(spec) + if spec.owner ~= "f-droid" or spec.repo ~= "fdroidclient" then + error("GitHub project fixture mismatch") + end + local asset_name = "F-Droid.apk" + if spec.asset.include and not string.match(asset_name, spec.asset.include) then + error("GitHub asset include fixture mismatch") + end + if spec.asset.exclude and string.match(asset_name, spec.asset.exclude) then + error("GitHub asset exclude fixture mismatch") + end + return package_version { + name = spec.name, + installed = { + { kind = "android_package", package_name = spec.android_package }, + }, + source_priority = { "github" }, + updates = { + { + version = "v1.20.0", + source = "github", + artifacts = { + { + name = asset_name, + url = "https://github.com/f-droid/fdroidclient/releases/download/v1.20.0/F-Droid.apk", + file_name = asset_name, + }, + }, + }, + }, + } +end + +return github_android +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + android_package = "org.fdroid.fdroid", + owner = "f-droid", + repo = "fdroidclient", + asset = { + include = "%.apk$", + exclude = "debug", + }, +} +"#, + ) + .unwrap(); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + + assert_eq!(package.id.to_string(), "android/app/org.fdroid.fdroid"); + assert_eq!(package.repository.as_str(), "official"); + assert_eq!(package.name, "F-Droid"); + assert_eq!( + package.installed, + vec![InstalledTarget::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned() + }] + ); + assert_eq!(package.source_priority, vec!["github"]); + assert_eq!(package.updates.len(), 1); + assert_eq!(package.updates[0].version, "v1.20.0"); + assert_eq!(package.updates[0].source.as_deref(), Some("github")); + assert_eq!( + package.updates[0].artifacts[0].url, + "https://github.com/f-droid/fdroidclient/releases/download/v1.20.0/F-Droid.apk" + ); + assert_eq!( + package.updates[0].artifacts[0].file_name.as_deref(), + Some("F-Droid.apk") + ); + } + #[test] fn package_directory_can_read_package_local_files() { let temp = tempfile::tempdir().unwrap(); From 7eb6ab2d4b4cfe2515c0fcabf469b91c3ed04f12 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 18:39:57 +0800 Subject: [PATCH 45/72] feat(lua): add host binding injection seam --- crates/getter-core/src/lua.rs | 148 +++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 2 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index e45801c..cc1b084 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -53,6 +53,30 @@ pub fn evaluate_package_directory_script( metadata: &PackageDirectoryMetadata, script: &PackageVersionScript, ) -> Result { + evaluate_package_directory_script_with_host_bindings( + repository_id, + package, + metadata, + script, + |_| Ok(()), + ) +} + +/// Evaluates a package version script with caller-installed Lua host bindings. +/// +/// This is an internal seam for higher-level getter operations to provide +/// getter-owned host functions while keeping `getter-core` independent from +/// provider/cache crates. It does not define a stable public Lua provider API. +pub fn evaluate_package_directory_script_with_host_bindings( + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + install_host_bindings: F, +) -> Result +where + F: FnOnce(&Lua) -> mlua::Result<()>, +{ let source = fs::read_to_string(&script.path).map_err(|source| LuaPackageError::ReadFile { path: script.path.clone(), source, @@ -70,15 +94,20 @@ pub fn evaluate_package_directory_script( &LuaRepositoryEnvironment::PackageDirectory { package }, &script.path, &source, + install_host_bindings, )?; validate_package_directory_version_json(repository_id, package, metadata, script, json) } -fn evaluate_package_source_to_json( +fn evaluate_package_source_to_json( environment: &LuaRepositoryEnvironment<'_>, path: &Path, source: &str, -) -> Result { + install_host_bindings: F, +) -> Result +where + F: FnOnce(&Lua) -> mlua::Result<()>, +{ let path = path.to_path_buf(); let lua = Lua::new(); configure_package_path(&lua, environment).map_err(|source| LuaPackageError::Runtime { @@ -93,6 +122,10 @@ fn evaluate_package_source_to_json( path: path.clone(), source: Box::new(source), })?; + install_host_bindings(&lua).map_err(|source| LuaPackageError::Runtime { + path: path.clone(), + source: Box::new(source), + })?; let value = lua .load(source) @@ -1187,6 +1220,117 @@ return github_android.package { ); } + #[test] + fn package_directory_luaclass_can_call_injected_provider_host() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::create_dir_all(temp.path().join("luaclass")).unwrap(); + fs::write( + temp.path().join("luaclass/github_android_apk.lua"), + r#" +local github_android = {} + +function github_android.package(spec) + local releases = getter_test_provider.github_releases(spec.owner, spec.repo, spec.asset) + return package_version { + name = spec.name, + source_priority = { "github" }, + updates = releases, + } +end + +return github_android +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "%.apk$" }, +} +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("official").unwrap(), + package_dir, + &metadata, + script, + |lua| { + let provider = lua.create_table()?; + provider.set( + "github_releases", + lua.create_function( + |lua, (owner, repo, asset): (String, String, Table)| { + if owner != "f-droid" || repo != "fdroidclient" { + return Err(mlua::Error::external("GitHub fixture mismatch")); + } + let include: String = asset.get("include")?; + if include != "%.apk$" { + return Err(mlua::Error::external("GitHub asset filter mismatch")); + } + let artifact = lua.create_table()?; + artifact.set("name", "F-Droid.apk")?; + artifact.set( + "url", + "https://github.com/f-droid/fdroidclient/releases/download/v1.20.0/F-Droid.apk", + )?; + artifact.set("file_name", "F-Droid.apk")?; + let artifacts = lua.create_table()?; + artifacts.raw_set(1, artifact)?; + let candidate = lua.create_table()?; + candidate.set("version", "v1.20.0")?; + candidate.set("source", "github")?; + candidate.set("artifacts", artifacts)?; + let candidates = lua.create_table()?; + candidates.raw_set(1, candidate)?; + Ok(candidates) + }, + )?, + )?; + lua.globals().set("getter_test_provider", provider) + }, + ) + .unwrap(); + + assert_eq!(package.id.to_string(), "android/app/org.fdroid.fdroid"); + assert_eq!(package.repository.as_str(), "official"); + assert_eq!(package.name, "F-Droid"); + assert_eq!( + package.installed, + vec![InstalledTarget::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned() + }] + ); + assert_eq!(package.source_priority, vec!["github"]); + assert_eq!(package.updates.len(), 1); + assert_eq!(package.updates[0].version, "v1.20.0"); + assert_eq!(package.updates[0].source.as_deref(), Some("github")); + assert_eq!( + package.updates[0].artifacts[0].file_name.as_deref(), + Some("F-Droid.apk") + ); + } + #[test] fn package_directory_can_read_package_local_files() { let temp = tempfile::tempdir().unwrap(); From 236cfaedf9d21833045337962cfcc375a9525c2d Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 19:52:46 +0800 Subject: [PATCH 46/72] feat(lua): add dev provider host package eval --- Cargo.lock | 1 + crates/getter-operations/Cargo.toml | 4 +- crates/getter-operations/src/lib.rs | 3 + .../src/lua_provider_host.rs | 530 ++++++++++++++++++ 4 files changed, 537 insertions(+), 1 deletion(-) create mode 100644 crates/getter-operations/src/lua_provider_host.rs diff --git a/Cargo.lock b/Cargo.lock index c072fcc..a65d2b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -618,6 +618,7 @@ dependencies = [ "getter-providers", "getter-storage", "json_comments", + "mlua", "serde", "serde_json", "sha2", diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index ed85e8c..81c2e82 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -5,13 +5,15 @@ edition.workspace = true [features] default = [] -lua = ["getter-core/lua"] +lua = ["getter-core/lua", "dep:mlua"] +lua-provider-host-dev = ["lua"] [dependencies] json_comments = "0.2" getter-core = { path = "../getter-core", default-features = false } getter-providers = { path = "../getter-providers" } getter-storage = { path = "../getter-storage" } +mlua = { version = "0.10", features = ["luajit", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" sha2 = "0.10" diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 30097ff..ae19c2d 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -11,6 +11,9 @@ pub mod fdroid_catalog; pub mod github_latest_commit; pub mod github_releases; pub mod legacy_room; +#[cfg(feature = "lua-provider-host-dev")] +#[doc(hidden)] +pub mod lua_provider_host; pub mod provider_cache; pub mod read_model; pub mod runtime; diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs new file mode 100644 index 0000000..0d3b2b4 --- /dev/null +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -0,0 +1,530 @@ +//! Operation-installed Lua provider host bindings. +//! +//! This module is an implementation bridge toward ADR-0012 provider-backed Lua +//! modules. It exercises higher-level getter operations installing host +//! functions for package evaluation without making `getter-core` depend on +//! provider cache/storage crates and without defining the final stable Lua +//! provider API. + +use crate::github_releases::{ + read_or_refresh_github_releases, GithubReleaseConfig, GithubReleaseOperationError, + DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, +}; +use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource}; +use getter_core::lua::{evaluate_package_directory_script_with_host_bindings, LuaPackageError}; +use getter_core::repository::{RepositoryLoadError, RepositoryPackageDirectoryLayout}; +use getter_core::{PackageId, RepositoryId, UpdateArtifact, UpdateCandidate}; +use getter_providers::{ + github_release_update_candidates, GithubAssetFilter, GithubProviderError, GithubRelease, + GithubReleaseCandidateOptions, +}; +use getter_storage::{CacheDb, MainDb, StorageError, StoredRepository}; +use mlua::{Lua, Table, Value as LuaValue}; +use serde::Deserialize; +use serde_json::{json, Value}; +use std::cell::RefCell; +use std::path::{Path, PathBuf}; +use std::rc::Rc; + +#[derive(Debug, thiserror::Error)] +pub enum LuaProviderHostOperationError { + #[error("invalid Lua provider-host request: {0}")] + InvalidRequest(String), + #[error("storage operation failed: {0}")] + Storage(#[from] StorageError), + #[error("repository operation failed: {0}")] + Repository(#[from] RepositoryLoadError), + #[error("package evaluation failed: {0}")] + PackageEval(#[from] LuaPackageError), + #[error("GitHub provider operation failed: {0}")] + Github(#[from] GithubReleaseOperationError), + #[error("GitHub provider normalization failed: {0}")] + GithubProvider(#[from] GithubProviderError), + #[error("Lua provider-host response serialization failed: {0}")] + Serialization(#[from] serde_json::Error), +} + +/// Evaluates a package with a fixture-backed GitHub release host binding. +/// +/// This is a development/provider-host tracer, not the stable package-eval or +/// product update-check API. Repository packages can exercise repository-local +/// `luaclass/` modules that call `getter_dev.github_release_candidates { ... }`, +/// while release parsing/cache behavior stays in `getter-operations`. +pub fn github_package_eval_json( + data_dir: &Path, + request_json: &str, +) -> Result { + let request: GithubPackageEvalRequest = serde_json::from_str(request_json) + .map_err(|source| LuaProviderHostOperationError::InvalidRequest(source.to_string()))?; + let mode = provider_cache_mode(request.mode.as_deref())?; + let api_base_url = request + .api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()); + let main_db = MainDb::open(data_dir.join("main.db"))?; + let repository = find_repository(&main_db, &request.repository_id)?; + let repository_path = repo_path(&repository)?; + let layout = RepositoryPackageDirectoryLayout::load(&repository_path)?; + let package_directory = layout.package(&request.package_id).ok_or_else(|| { + LuaProviderHostOperationError::InvalidRequest(format!( + "package '{}' was not found in repository '{}'", + request.package_id, request.repository_id + )) + })?; + let metadata = layout.package_metadata(package_directory)?; + let script = layout.unambiguous_version_script(package_directory)?; + let provider_calls = Rc::new(RefCell::new(Vec::new())); + let cache_db_path = data_dir.join("cache.db"); + let default_include_prereleases = request.include_prereleases; + let releases_json = request.releases_json; + let package = evaluate_package_directory_script_with_host_bindings( + &repository.id, + package_directory, + &metadata, + script, + { + let provider_calls = Rc::clone(&provider_calls); + move |lua| { + install_github_dev_host( + lua, + GithubDevHostConfig { + cache_db_path, + api_base_url, + mode, + releases_json, + default_include_prereleases, + provider_calls, + }, + ) + } + }, + )?; + let package = serde_json::to_value(package)?; + + Ok(json!({ + "operation": "github.package_eval.fixture", + "package": package, + "provider_calls": provider_calls.borrow().clone(), + })) +} + +#[derive(Debug, Deserialize)] +struct GithubPackageEvalRequest { + repository_id: RepositoryId, + package_id: PackageId, + #[serde(default)] + api_base_url: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + releases_json: Option, + #[serde(default)] + include_prereleases: bool, +} + +struct GithubDevHostConfig { + cache_db_path: PathBuf, + api_base_url: String, + mode: ProviderCacheMode, + releases_json: Option, + default_include_prereleases: bool, + provider_calls: Rc>>, +} + +fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { + let host = lua.create_table()?; + host.set( + "github_release_candidates", + lua.create_function(move |lua, spec: Table| { + let request = GithubReleaseHostRequest::from_lua_table(&spec)?; + let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; + let provider_config = GithubReleaseConfig { + api_base_url: config.api_base_url.clone(), + owner: request.owner, + repo: request.repo, + }; + let refresh_fixture = config.releases_json.clone(); + let result = read_or_refresh_github_releases(&db, provider_config, config.mode, || { + refresh_fixture.ok_or_else(|| { + "fixture-backed GitHub release host requires releases_json".to_owned() + }) + }) + .map_err(mlua::Error::external)?; + let options = GithubReleaseCandidateOptions { + include_prereleases: request + .include_prereleases + .unwrap_or(config.default_include_prereleases), + asset_filter: request.asset_filter, + }; + let candidates = github_release_update_candidates(&result.releases, &options) + .map_err(mlua::Error::external)?; + let mut diagnostics = result + .diagnostics + .iter() + .map(provider_diagnostic_json) + .collect::>(); + if candidates.is_empty() + && github_eligible_release_count(&result.releases, &options) > 0 + { + diagnostics.push(json!({ + "code": GITHUB_ASSET_NOT_FOUND, + "message": "no GitHub release assets matched the requested filters", + "provider": GITHUB_PROVIDER_ID, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + })); + } + config.provider_calls.borrow_mut().push(json!({ + "provider": GITHUB_PROVIDER_ID, + "request": "releases", + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "diagnostics": diagnostics, + })); + update_candidates_to_lua(lua, &candidates) + })?, + )?; + lua.globals().set("getter_dev", host) +} + +struct GithubReleaseHostRequest { + owner: String, + repo: String, + asset_filter: GithubAssetFilter, + include_prereleases: Option, +} + +impl GithubReleaseHostRequest { + fn from_lua_table(table: &Table) -> mlua::Result { + Ok(Self { + owner: required_lua_string(table, "owner")?, + repo: required_lua_string(table, "repo")?, + asset_filter: asset_filter_from_lua(table.get("asset")?)?, + include_prereleases: optional_lua_bool(table, "include_prereleases")?, + }) + } +} + +fn provider_cache_mode( + mode: Option<&str>, +) -> Result { + match mode { + Some("force_refresh") => Ok(ProviderCacheMode::ForceRefresh), + Some("use_cached") | None => Ok(ProviderCacheMode::UseCached), + Some(other) => Err(LuaProviderHostOperationError::InvalidRequest(format!( + "unknown mode '{other}'" + ))), + } +} + +fn find_repository( + db: &MainDb, + repository_id: &RepositoryId, +) -> Result { + db.repositories()? + .into_iter() + .find(|repository| &repository.id == repository_id) + .ok_or_else(|| { + LuaProviderHostOperationError::InvalidRequest(format!( + "repository '{repository_id}' is not registered" + )) + }) +} + +fn repo_path(repository: &StoredRepository) -> Result { + repository.path.as_ref().map(PathBuf::from).ok_or_else(|| { + LuaProviderHostOperationError::InvalidRequest(format!( + "repository '{}' has no path", + repository.id + )) + }) +} + +fn required_lua_string(table: &Table, field: &'static str) -> mlua::Result { + let value: Option = table.get(field)?; + value + .filter(|value| !value.trim().is_empty()) + .ok_or_else(|| { + mlua::Error::external(format!( + "getter_dev.github_release_candidates missing {field}" + )) + }) +} + +fn optional_lua_bool(table: &Table, field: &'static str) -> mlua::Result> { + match table.get::(field)? { + LuaValue::Nil => Ok(None), + LuaValue::Boolean(value) => Ok(Some(value)), + _ => Err(mlua::Error::external(format!( + "getter_dev.github_release_candidates {field} must be a boolean" + ))), + } +} + +fn asset_filter_from_lua(value: LuaValue) -> mlua::Result { + match value { + LuaValue::Nil => Ok(GithubAssetFilter::default()), + LuaValue::Table(table) => Ok(GithubAssetFilter { + include: optional_lua_string(&table, "include")?, + exclude: optional_lua_string(&table, "exclude")?, + }), + _ => Err(mlua::Error::external( + "getter_dev.github_release_candidates asset must be a table", + )), + } +} + +fn optional_lua_string(table: &Table, field: &'static str) -> mlua::Result> { + match table.get::(field)? { + LuaValue::Nil => Ok(None), + LuaValue::String(value) => Ok(Some(value.to_str()?.to_owned())), + _ => Err(mlua::Error::external(format!( + "getter_dev.github_release_candidates asset.{field} must be a string" + ))), + } +} + +fn github_eligible_release_count( + releases: &[GithubRelease], + options: &GithubReleaseCandidateOptions, +) -> usize { + releases + .iter() + .filter(|release| !release.draft) + .filter(|release| options.include_prereleases || !release.prerelease) + .count() +} + +fn update_candidates_to_lua(lua: &Lua, candidates: &[UpdateCandidate]) -> mlua::Result { + if candidates.is_empty() { + return Ok(LuaValue::Nil); + } + + let table = lua.create_table()?; + for (index, candidate) in candidates.iter().enumerate() { + table.raw_set(index + 1, update_candidate_to_lua(lua, candidate)?)?; + } + Ok(LuaValue::Table(table)) +} + +fn update_candidate_to_lua(lua: &Lua, candidate: &UpdateCandidate) -> mlua::Result
{ + let table = lua.create_table()?; + table.set("version", candidate.version.as_str())?; + if let Some(version_code) = candidate.version_code { + table.set("version_code", version_code)?; + } + if let Some(channel) = candidate.channel.as_ref() { + table.set("channel", channel.as_str())?; + } + if let Some(source) = candidate.source.as_ref() { + table.set("source", source.as_str())?; + } + let artifacts = lua.create_table()?; + for (index, artifact) in candidate.artifacts.iter().enumerate() { + artifacts.raw_set(index + 1, update_artifact_to_lua(lua, artifact)?)?; + } + table.set("artifacts", artifacts)?; + Ok(table) +} + +fn update_artifact_to_lua(lua: &Lua, artifact: &UpdateArtifact) -> mlua::Result
{ + let table = lua.create_table()?; + table.set("name", artifact.name.as_str())?; + table.set("url", artifact.url.as_str())?; + if let Some(file_name) = artifact.file_name.as_ref() { + table.set("file_name", file_name.as_str())?; + } + if let Some(sha256) = artifact.sha256.as_ref() { + table.set("sha256", sha256.as_str())?; + } + if let Some(size) = artifact.size { + table.set("size", size)?; + } + Ok(table) +} + +fn provider_source_json(source: ProviderCacheSource) -> &'static str { + match source { + ProviderCacheSource::Cache => "cache", + ProviderCacheSource::Refreshed => "refreshed", + ProviderCacheSource::Stale => "stale", + } +} + +fn provider_diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; + use getter_core::{RepositoryId, RepositoryPriority}; + use getter_storage::CacheDb; + use serde_json::json; + use std::fs; + + const GITHUB_RELEASES_FIXTURE: &str = r#"[ + { + "tag_name": "v1.20.0", + "name": "F-Droid 1.20.0", + "draft": false, + "prerelease": false, + "published_at": "2026-06-01T00:00:00Z", + "assets": [ + { + "name": "F-Droid.apk", + "browser_download_url": "https://github.com/f-droid/fdroidclient/releases/download/v1.20.0/F-Droid.apk", + "content_type": "application/vnd.android.package-archive", + "size": 12345, + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } +]"#; + + #[test] + fn github_package_eval_uses_repository_luaclass_and_provider_cache() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_github_package_fixture(data_dir, "[.]apk$"); + + let first = github_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(first["operation"], "github.package_eval.fixture"); + assert_eq!(first["provider_calls"][0]["source"], "refreshed"); + assert_eq!(first["package"]["name"], "F-Droid"); + assert_eq!(first["package"]["source_priority"], json!(["github"])); + assert_eq!(first["package"]["updates"][0]["version"], "v1.20.0"); + assert_eq!(first["package"]["updates"][0]["source"], "github"); + assert_eq!( + first["package"]["updates"][0]["artifacts"][0]["sha256"], + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ); + let cache_key = first["provider_calls"][0]["cache_key"].as_str().unwrap(); + assert!(CacheDb::open(data_dir.join("cache.db")) + .unwrap() + .provider_response(cache_key) + .unwrap() + .is_some()); + + let second = github_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(second["provider_calls"][0]["source"], "cache"); + assert_eq!(second["package"]["updates"][0]["version"], "v1.20.0"); + } + + #[test] + fn github_package_eval_reports_asset_filter_diagnostics() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_github_package_fixture(data_dir, "[.]zip$"); + + let result = github_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["package"]["updates"], json!([])); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["code"], + GITHUB_ASSET_NOT_FOUND + ); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["provider"], + GITHUB_PROVIDER_ID + ); + } + + fn write_github_package_fixture(data_dir: &std::path::Path, asset_include: &str) { + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(repo_root.join("luaclass")).unwrap(); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + repo_root.join("luaclass/github_android_apk.lua"), + r#" +local github_android = {} + +function github_android.package(spec) + return package_version { + name = spec.name, + source_priority = { "github" }, + updates = getter_dev.github_release_candidates { + owner = spec.owner, + repo = spec.repo, + asset = spec.asset, + }, + } +end + +return github_android +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + let version_script = r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "__ASSET_INCLUDE__" }, +} +"# + .replace("__ASSET_INCLUDE__", asset_include); + fs::write(package_dir.join("9999.lua"), version_script).unwrap(); + let main_db = MainDb::open(data_dir.join("main.db")).unwrap(); + main_db + .upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::DEFAULT, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_root), + None, + ) + .unwrap(); + } +} From 0f74313b52e3cc6083223125376dfa2a3b122a80 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 20:40:11 +0800 Subject: [PATCH 47/72] feat(lua): add F-Droid dev provider host eval --- .../src/lua_provider_host.rs | 334 +++++++++++++++++- 1 file changed, 331 insertions(+), 3 deletions(-) diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 0d3b2b4..ee16e1a 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -6,6 +6,10 @@ //! provider cache/storage crates and without defining the final stable Lua //! provider API. +use crate::fdroid_catalog::{ + read_or_refresh_fdroid_catalog, FdroidCatalogOperationError, FdroidEndpointConfig, + DEFAULT_FDROID_ENDPOINT_ID, DEFAULT_FDROID_ENDPOINT_URL, FDROID_PROVIDER_ID, +}; use crate::github_releases::{ read_or_refresh_github_releases, GithubReleaseConfig, GithubReleaseOperationError, DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, @@ -15,8 +19,8 @@ use getter_core::lua::{evaluate_package_directory_script_with_host_bindings, Lua use getter_core::repository::{RepositoryLoadError, RepositoryPackageDirectoryLayout}; use getter_core::{PackageId, RepositoryId, UpdateArtifact, UpdateCandidate}; use getter_providers::{ - github_release_update_candidates, GithubAssetFilter, GithubProviderError, GithubRelease, - GithubReleaseCandidateOptions, + github_release_update_candidates, FdroidEndpoint, GithubAssetFilter, GithubProviderError, + GithubRelease, GithubReleaseCandidateOptions, }; use getter_storage::{CacheDb, MainDb, StorageError, StoredRepository}; use mlua::{Lua, Table, Value as LuaValue}; @@ -36,6 +40,8 @@ pub enum LuaProviderHostOperationError { Repository(#[from] RepositoryLoadError), #[error("package evaluation failed: {0}")] PackageEval(#[from] LuaPackageError), + #[error("F-Droid provider operation failed: {0}")] + Fdroid(#[from] FdroidCatalogOperationError), #[error("GitHub provider operation failed: {0}")] Github(#[from] GithubReleaseOperationError), #[error("GitHub provider normalization failed: {0}")] @@ -44,6 +50,98 @@ pub enum LuaProviderHostOperationError { Serialization(#[from] serde_json::Error), } +const FDROID_PACKAGE_NOT_FOUND: &str = "provider.fdroid.package_not_found"; + +/// Evaluates a package with a fixture-backed F-Droid catalog host binding. +/// +/// This is a development/provider-host tracer, not the stable package-eval or +/// product update-check API. Repository packages can exercise repository-local +/// `luaclass/` modules that call `getter_dev.fdroid_update_candidates { ... }`, +/// while catalog parsing/cache behavior stays in `getter-operations`. +pub fn fdroid_package_eval_json( + data_dir: &Path, + request_json: &str, +) -> Result { + let request: FdroidPackageEvalRequest = serde_json::from_str(request_json) + .map_err(|source| LuaProviderHostOperationError::InvalidRequest(source.to_string()))?; + let mode = provider_cache_mode(request.mode.as_deref())?; + let endpoint = FdroidEndpointConfig { + endpoint_id: request + .endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), + endpoint_url: request + .endpoint_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), + }; + let main_db = MainDb::open(data_dir.join("main.db"))?; + let repository = find_repository(&main_db, &request.repository_id)?; + let repository_path = repo_path(&repository)?; + let layout = RepositoryPackageDirectoryLayout::load(&repository_path)?; + let package_directory = layout.package(&request.package_id).ok_or_else(|| { + LuaProviderHostOperationError::InvalidRequest(format!( + "package '{}' was not found in repository '{}'", + request.package_id, request.repository_id + )) + })?; + let metadata = layout.package_metadata(package_directory)?; + let script = layout.unambiguous_version_script(package_directory)?; + let provider_calls = Rc::new(RefCell::new(Vec::new())); + let cache_db_path = data_dir.join("cache.db"); + let index_xml = request.index_xml; + let package = evaluate_package_directory_script_with_host_bindings( + &repository.id, + package_directory, + &metadata, + script, + { + let provider_calls = Rc::clone(&provider_calls); + move |lua| { + install_fdroid_dev_host( + lua, + FdroidDevHostConfig { + cache_db_path, + endpoint, + mode, + index_xml, + provider_calls, + }, + ) + } + }, + )?; + let package = serde_json::to_value(package)?; + + Ok(json!({ + "operation": "fdroid.package_eval.fixture", + "package": package, + "provider_calls": provider_calls.borrow().clone(), + })) +} + +#[derive(Debug, Deserialize)] +struct FdroidPackageEvalRequest { + repository_id: RepositoryId, + package_id: PackageId, + #[serde(default)] + endpoint_id: Option, + #[serde(default)] + endpoint_url: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + index_xml: Option, +} + +struct FdroidDevHostConfig { + cache_db_path: PathBuf, + endpoint: FdroidEndpointConfig, + mode: ProviderCacheMode, + index_xml: Option, + provider_calls: Rc>>, +} + /// Evaluates a package with a fixture-backed GitHub release host binding. /// /// This is a development/provider-host tracer, not the stable package-eval or @@ -131,6 +229,82 @@ struct GithubDevHostConfig { provider_calls: Rc>>, } +fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Result<()> { + let host = lua.create_table()?; + host.set( + "fdroid_update_candidates", + lua.create_function(move |lua, spec: Table| { + let request = FdroidUpdateHostRequest::from_lua_table(&spec)?; + let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; + let endpoint = config.endpoint.clone(); + let refresh_fixture = config.index_xml.clone(); + let result = read_or_refresh_fdroid_catalog(&db, endpoint, config.mode, || { + refresh_fixture.ok_or_else(|| { + "fixture-backed F-Droid catalog host requires index_xml".to_owned() + }) + }) + .map_err(mlua::Error::external)?; + let mut diagnostics = result + .diagnostics + .iter() + .map(provider_diagnostic_json) + .collect::>(); + let candidates = result + .app(&request.package_name) + .map(|app| { + let endpoint = FdroidEndpoint { + name: result.catalog.endpoint.name.clone(), + url: Some(result.endpoint.endpoint_url.clone()), + timestamp: result.catalog.endpoint.timestamp.clone(), + }; + app.update_candidates(&endpoint) + }) + .unwrap_or_else(|| { + diagnostics.push(json!({ + "code": FDROID_PACKAGE_NOT_FOUND, + "message": format!("F-Droid package '{}' was not found in endpoint '{}'", request.package_name, result.endpoint.endpoint_id), + "provider": FDROID_PROVIDER_ID, + "endpoint_id": result.endpoint.endpoint_id, + "package_name": request.package_name, + "cache_key": result.cache_key, + })); + Vec::new() + }); + config.provider_calls.borrow_mut().push(json!({ + "provider": FDROID_PROVIDER_ID, + "request": "catalog", + "endpoint_id": result.endpoint.endpoint_id, + "endpoint_url": result.endpoint.endpoint_url, + "package_name": request.package_name, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "diagnostics": diagnostics, + })); + update_candidates_to_lua(lua, &candidates) + })?, + )?; + lua.globals().set("getter_dev", host) +} + +struct FdroidUpdateHostRequest { + package_name: String, +} + +impl FdroidUpdateHostRequest { + fn from_lua_table(table: &Table) -> mlua::Result { + let package_name: Option = table.get("package_name")?; + Ok(Self { + package_name: package_name + .filter(|value| !value.trim().is_empty()) + .ok_or_else(|| { + mlua::Error::external( + "getter_dev.fdroid_update_candidates missing package_name", + ) + })?, + }) + } +} + fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { let host = lua.create_table()?; host.set( @@ -373,6 +547,23 @@ mod tests { use serde_json::json; use std::fs; + const FDROID_INDEX_FIXTURE: &str = r#" + + + + F-Droid + App repository client + + 1.20.0 + 1020000 + org.fdroid.fdroid_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + +"#; + const GITHUB_RELEASES_FIXTURE: &str = r#"[ { "tag_name": "v1.20.0", @@ -392,6 +583,96 @@ mod tests { } ]"#; + #[test] + fn fdroid_package_eval_uses_repository_luaclass_and_provider_cache() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_fdroid_package_fixture(data_dir, "org.fdroid.fdroid"); + + let first = fdroid_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(first["operation"], "fdroid.package_eval.fixture"); + assert_eq!(first["provider_calls"][0]["source"], "refreshed"); + assert_eq!(first["provider_calls"][0]["endpoint_id"], "official"); + assert_eq!( + first["provider_calls"][0]["package_name"], + "org.fdroid.fdroid" + ); + assert_eq!(first["package"]["name"], "F-Droid"); + assert_eq!(first["package"]["source_priority"], json!(["fdroid"])); + assert_eq!(first["package"]["updates"][0]["version"], "1.20.0"); + assert_eq!(first["package"]["updates"][0]["version_code"], 1020000); + assert_eq!(first["package"]["updates"][0]["source"], "fdroid"); + assert_eq!( + first["package"]["updates"][0]["artifacts"][0]["url"], + "https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk" + ); + assert_eq!( + first["package"]["updates"][0]["artifacts"][0]["sha256"], + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ); + let cache_key = first["provider_calls"][0]["cache_key"].as_str().unwrap(); + assert!(CacheDb::open(data_dir.join("cache.db")) + .unwrap() + .provider_response(cache_key) + .unwrap() + .is_some()); + + let second = fdroid_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(second["provider_calls"][0]["source"], "cache"); + assert_eq!(second["package"]["updates"][0]["version"], "1.20.0"); + } + + #[test] + fn fdroid_package_eval_reports_package_not_found_diagnostics() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_fdroid_package_fixture(data_dir, "missing.package"); + + let result = fdroid_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["package"]["updates"], json!([])); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["code"], + FDROID_PACKAGE_NOT_FOUND + ); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["provider"], + FDROID_PROVIDER_ID + ); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["package_name"], + "missing.package" + ); + } + #[test] fn github_package_eval_uses_repository_luaclass_and_provider_cache() { let temp = tempfile::tempdir().unwrap(); @@ -468,6 +749,49 @@ mod tests { ); } + fn write_fdroid_package_fixture(data_dir: &std::path::Path, package_name: &str) { + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(repo_root.join("luaclass")).unwrap(); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + repo_root.join("luaclass/fdroid_android.lua"), + r#" +local fdroid = {} + +function fdroid.package(spec) + return package_version { + source_priority = { "fdroid" }, + updates = getter_dev.fdroid_update_candidates { + package_name = spec.package_name, + }, + } +end + +return fdroid +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "display_name": "F-Droid", + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + let version_script = r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "__PACKAGE_NAME__", +} +"# + .replace("__PACKAGE_NAME__", package_name); + fs::write(package_dir.join("9999.lua"), version_script).unwrap(); + register_official_repository(data_dir, &repo_root); + } + fn write_github_package_fixture(data_dir: &std::path::Path, asset_include: &str) { let repo_root = data_dir.join("repo/official"); let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); @@ -513,6 +837,10 @@ return github_android.package { "# .replace("__ASSET_INCLUDE__", asset_include); fs::write(package_dir.join("9999.lua"), version_script).unwrap(); + register_official_repository(data_dir, &repo_root); + } + + fn register_official_repository(data_dir: &std::path::Path, repo_root: &std::path::Path) { let main_db = MainDb::open(data_dir.join("main.db")).unwrap(); main_db .upsert_repository( @@ -522,7 +850,7 @@ return github_android.package { priority: RepositoryPriority::DEFAULT, api_version: REPO_API_VERSION_V1.to_owned(), }, - Some(&repo_root), + Some(repo_root), None, ) .unwrap(); From 517bbcf43d4244b93bf8b0f49077905aaf3b630f Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 21:56:44 +0800 Subject: [PATCH 48/72] feat(lua): add builtin luaclass fallback --- crates/getter-core/src/lua.rs | 160 ++++++++++++++------ crates/getter-core/src/luaclass/android.lua | 12 ++ 2 files changed, 123 insertions(+), 49 deletions(-) create mode 100644 crates/getter-core/src/luaclass/android.lua diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index cc1b084..baad1ce 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -8,11 +8,14 @@ use crate::{ InstalledTarget, PackageId, PackagePermissions, RepositoryId, ResolvedPackage, UpdateArtifact, UpdateCandidate, }; -use mlua::{Lua, Table, Value}; +use mlua::{Function, Lua, Table, Value}; use serde_json::{Map, Number, Value as JsonValue}; use std::fs; use std::path::{Path, PathBuf}; +const BUILTIN_LUACLASS_MODULES: &[(&str, &str)] = + &[("android", include_str!("luaclass/android.lua"))]; + #[derive(Debug, thiserror::Error)] pub enum LuaPackageError { #[error("failed to read Lua package file {path}: {source}")] @@ -158,7 +161,8 @@ fn configure_package_path( package: package_dir, } => { package.set("path", "")?; - install_luaclass_prefix_searcher( + install_builtin_luaclass_searcher(lua, &package)?; + install_repository_luaclass_searcher( lua, &package, package_directory_repository_root(package_dir).join(REPOSITORY_LUACLASS_DIR), @@ -198,62 +202,85 @@ fn remove_unsafe_globals(lua: &Lua) -> mlua::Result<()> { Ok(()) } -fn install_luaclass_prefix_searcher( +fn install_repository_luaclass_searcher( lua: &Lua, package: &Table, luaclass_dir: PathBuf, ) -> mlua::Result<()> { - install_prefixed_file_searcher( + install_searcher( lua, package, - "luaclass.", - luaclass_dir, - "constrained repository luaclass searcher only handles luaclass.* modules", + lua.create_function(move |lua, module: String| { + let Some(module) = module.strip_prefix("luaclass.") else { + return lua + .create_string("\n\tconstrained repository luaclass searcher only handles luaclass.* modules") + .map(Value::String); + }; + + let Some(relative_module) = module_to_relative_path(module) else { + return lua + .create_string(format!("\n\tinvalid repository module name 'luaclass.{module}'")) + .map(Value::String); + }; + + let module_path = luaclass_dir.join(&relative_module).with_extension("lua"); + let init_path = luaclass_dir.join(&relative_module).join("init.lua"); + for candidate in [&module_path, &init_path] { + if candidate.is_file() { + let source = fs::read_to_string(candidate).map_err(mlua::Error::external)?; + let chunk = lua + .load(&source) + .set_name(candidate.to_string_lossy().as_ref()); + return chunk.into_function().map(Value::Function); + } + } + + lua.create_string(format!( + "\n\tno repository module 'luaclass.{module}' in {}", + luaclass_dir.display() + )) + .map(Value::String) + })?, ) } -fn install_prefixed_file_searcher( - lua: &Lua, - package: &Table, - prefix: &'static str, - module_dir: PathBuf, - wrong_prefix_message: &'static str, -) -> mlua::Result<()> { - let searchers = package_searchers(package)?; - let searcher = lua.create_function(move |lua, module: String| { - let Some(module) = module.strip_prefix(prefix) else { - return lua - .create_string(format!("\n\t{wrong_prefix_message}")) - .map(Value::String); - }; - - let Some(relative_module) = module_to_relative_path(module) else { - return lua - .create_string(format!( - "\n\tinvalid repository module name '{prefix}{module}'" - )) - .map(Value::String); - }; - - let module_path = module_dir.join(&relative_module).with_extension("lua"); - let init_path = module_dir.join(&relative_module).join("init.lua"); - for candidate in [&module_path, &init_path] { - if candidate.is_file() { - let source = fs::read_to_string(candidate).map_err(mlua::Error::external)?; - let chunk = lua - .load(&source) - .set_name(candidate.to_string_lossy().as_ref()); - return chunk.into_function().map(Value::Function); +fn install_builtin_luaclass_searcher(lua: &Lua, package: &Table) -> mlua::Result<()> { + install_searcher( + lua, + package, + lua.create_function(move |lua, module: String| { + let Some(module) = module.strip_prefix("luaclass.") else { + return lua + .create_string("\n\tbuiltin luaclass searcher only handles luaclass.* modules") + .map(Value::String); + }; + if module_to_relative_path(module).is_none() { + return lua + .create_string(format!( + "\n\tinvalid builtin luaclass module name 'luaclass.{module}'" + )) + .map(Value::String); } - } - - lua.create_string(format!( - "\n\tno repository module '{prefix}{module}' in {}", - module_dir.display() - )) - .map(Value::String) - })?; + let Some((_, source)) = BUILTIN_LUACLASS_MODULES + .iter() + .find(|(name, _)| *name == module) + else { + return lua + .create_string(format!( + "\n\tno builtin luaclass module 'luaclass.{module}'" + )) + .map(Value::String); + }; + lua.load(*source) + .set_name(format!("@builtin/luaclass/{module}.lua")) + .into_function() + .map(Value::Function) + })?, + ) +} +fn install_searcher(_lua: &Lua, package: &Table, searcher: Function) -> mlua::Result<()> { + let searchers = package_searchers(package)?; let len = searchers.raw_len(); for index in (2..=len).rev() { let value: Value = searchers.raw_get(index)?; @@ -984,7 +1011,7 @@ return package_version { id = "android/app/com.example.autogen" } } #[test] - fn package_directory_can_load_luaclass_modules() { + fn package_directory_can_load_repository_luaclass_modules() { let temp = tempfile::tempdir().unwrap(); let package_dir = temp.path().join("android/app/com.example.autogen"); fs::create_dir_all(&package_dir).unwrap(); @@ -995,6 +1022,7 @@ return package_version { id = "android/app/com.example.autogen" } return { package_version = function(input) return { + name = "repository module", installed = input.installed, updates = input.updates, } @@ -1037,7 +1065,41 @@ return android.package_version { ) .unwrap(); - assert_eq!(package.name, "Example Autogen"); + assert_eq!(package.name, "repository module"); + assert_eq!(package.installed.len(), 1); + } + + #[test] + fn package_directory_can_load_builtin_luaclass_modules() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local android = require("luaclass.android") +return android.package_version { + name = "builtin module", + installed = { + { kind = "android_package", package_name = "com.example.autogen" }, + }, +} +"#, + ) + .unwrap(); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + + assert_eq!(package.name, "builtin module"); assert_eq!(package.installed.len(), 1); } diff --git a/crates/getter-core/src/luaclass/android.lua b/crates/getter-core/src/luaclass/android.lua new file mode 100644 index 0000000..05ee382 --- /dev/null +++ b/crates/getter-core/src/luaclass/android.lua @@ -0,0 +1,12 @@ +local android = {} + +function android.package_version(input) + return package_version { + name = input.name, + installed = input.installed, + source_priority = input.source_priority, + updates = input.updates, + } +end + +return android From 759e7b8e1269b851383d57d2fea93916f2d161f7 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 22:39:47 +0800 Subject: [PATCH 49/72] feat(lua): add HTTP host seam --- crates/getter-core/src/lua.rs | 302 +++++++++++++++++++++++++++++++++- 1 file changed, 296 insertions(+), 6 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index baad1ce..28857c9 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -10,6 +10,7 @@ use crate::{ }; use mlua::{Function, Lua, Table, Value}; use serde_json::{Map, Number, Value as JsonValue}; +use std::collections::BTreeMap; use std::fs; use std::path::{Path, PathBuf}; @@ -345,17 +346,137 @@ fn install_package_file_helpers(lua: &Lua, package: &PackageDirectory) -> mlua:: })?; let globals = lua.globals(); - let getter_builtin = match globals.get::("getter_builtin")? { - Value::Table(table) => table, + let getter_builtin = getter_builtin_table(lua)?; + getter_builtin.set("read_package_file", read_package_file.clone())?; + globals.set("read_package_file", read_package_file) +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LuaHttpGetRequest { + pub url: String, + pub headers: BTreeMap, + pub cache: bool, +} + +/// Installs the Lua `http_get` host seam for a caller-provided transport. +/// +/// Plain package evaluation does not install this function. Higher-level +/// getter operations/runtime code must deliberately provide a handler and own +/// provider policy, permission checks, Manifest validation, and cache behavior. +pub fn install_http_get_host(lua: &Lua, handler: F) -> mlua::Result<()> +where + F: Fn(LuaHttpGetRequest) -> mlua::Result> + 'static, +{ + let http_get = lua.create_function(move |lua, args: mlua::Variadic| { + let request = lua_http_get_request_from_args(args)?; + let bytes = handler(request)?; + lua.create_string(&bytes) + })?; + let globals = lua.globals(); + let getter_builtin = getter_builtin_table(lua)?; + getter_builtin.set("http_get", http_get.clone())?; + globals.set("http_get", http_get) +} + +fn getter_builtin_table(lua: &Lua) -> mlua::Result
{ + let globals = lua.globals(); + match globals.get::("getter_builtin")? { + Value::Table(table) => Ok(table), Value::Nil => { let table = lua.create_table()?; globals.set("getter_builtin", table.clone())?; - table + Ok(table) } - _ => return Err(mlua::Error::external("getter_builtin must be a table")), + _ => Err(mlua::Error::external("getter_builtin must be a table")), + } +} + +fn lua_http_get_request_from_args(args: mlua::Variadic) -> mlua::Result { + if args.is_empty() || args.len() > 2 { + return Err(mlua::Error::external( + "http_get expects url and optional options table", + )); + } + let url = match &args[0] { + Value::String(value) => value.to_str()?.to_owned(), + _ => return Err(mlua::Error::external("http_get url must be a string")), }; - getter_builtin.set("read_package_file", read_package_file.clone())?; - globals.set("read_package_file", read_package_file) + if url.trim().is_empty() { + return Err(mlua::Error::external("http_get url must not be empty")); + } + let mut request = LuaHttpGetRequest { + url, + headers: BTreeMap::new(), + cache: false, + }; + match args.get(1) { + None | Some(Value::Nil) => {} + Some(Value::Table(options)) => apply_http_get_options(&mut request, options)?, + Some(_) => return Err(mlua::Error::external("http_get options must be a table")), + } + Ok(request) +} + +fn apply_http_get_options(request: &mut LuaHttpGetRequest, options: &Table) -> mlua::Result<()> { + for pair in options.clone().pairs::() { + let (key, _) = pair?; + match key { + Value::String(value) if matches!(value.to_str()?.as_ref(), "cache" | "headers") => {} + Value::String(value) => { + return Err(mlua::Error::external(format!( + "http_get unsupported option '{}'", + value.to_str()? + ))) + } + _ => { + return Err(mlua::Error::external( + "http_get option keys must be strings", + )) + } + } + } + + match options.get::("cache")? { + Value::Nil => {} + Value::Boolean(value) => request.cache = value, + _ => { + return Err(mlua::Error::external( + "http_get options.cache must be a boolean", + )) + } + } + + match options.get::("headers")? { + Value::Nil => {} + Value::Table(headers) => { + for pair in headers.pairs::() { + let (key, value) = pair?; + let key = match key { + Value::String(value) => value.to_str()?.to_owned(), + _ => { + return Err(mlua::Error::external( + "http_get headers keys must be strings", + )) + } + }; + let value = match value { + Value::String(value) => value.to_str()?.to_owned(), + _ => { + return Err(mlua::Error::external( + "http_get headers values must be strings", + )) + } + }; + request.headers.insert(key, value); + } + } + _ => { + return Err(mlua::Error::external( + "http_get options.headers must be a table", + )) + } + } + Ok(()) } fn package_file_relative_path(path: &str) -> mlua::Result { @@ -802,6 +923,21 @@ mod tests { ) } + fn write_simple_android_package(root: &Path, script_source: &str) { + let package_dir = root.join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write(package_dir.join("9999.lua"), script_source).unwrap(); + } + #[test] fn evaluates_json_like_lua_package_table() { let temp = tempfile::tempdir().unwrap(); @@ -1393,6 +1529,160 @@ return github_android.package { ); } + #[test] + fn package_directory_http_get_is_not_installed_by_plain_eval() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local builtin_http_get = getter_builtin and getter_builtin.http_get +return package_version { name = (http_get == nil and builtin_http_get == nil) and "not installed" or "installed" } +"#, + ); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + + assert_eq!(package.name, "not installed"); + } + + #[test] + fn package_directory_http_get_host_parses_request_and_defaults_cache_false() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local first = http_get("https://example.invalid/a") +local second = http_get("https://example.invalid/b", { + headers = { Accept = "application/json", ["X-Test"] = "yes" }, + cache = true, +}) +return package_version { name = first .. "|" .. second } +"#, + ); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + let requests = std::rc::Rc::new(std::cell::RefCell::new(Vec::new())); + + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("autogen").unwrap(), + package_dir, + &metadata, + script, + { + let requests = std::rc::Rc::clone(&requests); + move |lua| { + install_http_get_host(lua, move |request| { + let body = if request.url.ends_with("/a") { + b"first".to_vec() + } else { + b"second".to_vec() + }; + requests.borrow_mut().push(request); + Ok(body) + }) + } + }, + ) + .unwrap(); + + assert_eq!(package.name, "first|second"); + let requests = requests.borrow(); + assert_eq!(requests.len(), 2); + assert_eq!(requests[0].url, "https://example.invalid/a"); + assert!(!requests[0].cache); + assert!(requests[0].headers.is_empty()); + assert_eq!(requests[1].url, "https://example.invalid/b"); + assert!(requests[1].cache); + assert_eq!( + requests[1].headers.get("Accept").map(String::as_str), + Some("application/json") + ); + assert_eq!( + requests[1].headers.get("X-Test").map(String::as_str), + Some("yes") + ); + } + + #[test] + fn package_directory_http_get_rejects_unsupported_options() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local extra_ok = pcall(http_get, "https://example.invalid/a", { method = "POST" }) +local header_ok = pcall(http_get, "https://example.invalid/b", { headers = { Accept = 1 } }) +return package_version { name = (not extra_ok and not header_ok) and "rejected" or "accepted" } +"#, + ); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("autogen").unwrap(), + package_dir, + &metadata, + script, + |lua| install_http_get_host(lua, |_| Ok(Vec::new())), + ) + .unwrap(); + + assert_eq!(package.name, "rejected"); + } + + #[test] + fn getter_builtin_exposes_http_get_for_lua_wrappers() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local upstream_http_get = getter_builtin.http_get +function http_get(url, opts) + opts = opts or {} + opts.headers = opts.headers or {} + opts.headers["X-Rewritten"] = "yes" + return upstream_http_get(url .. "?mirror=1", opts) +end +return package_version { name = http_get("https://example.invalid/file", { cache = true }) } +"#, + ); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + let requests = std::rc::Rc::new(std::cell::RefCell::new(Vec::new())); + + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("autogen").unwrap(), + package_dir, + &metadata, + script, + { + let requests = std::rc::Rc::clone(&requests); + move |lua| { + install_http_get_host(lua, move |request| { + requests.borrow_mut().push(request); + Ok(b"wrapped".to_vec()) + }) + } + }, + ) + .unwrap(); + + assert_eq!(package.name, "wrapped"); + let requests = requests.borrow(); + assert_eq!(requests.len(), 1); + assert_eq!(requests[0].url, "https://example.invalid/file?mirror=1"); + assert!(requests[0].cache); + assert_eq!( + requests[0].headers.get("X-Rewritten").map(String::as_str), + Some("yes") + ); + } + #[test] fn package_directory_can_read_package_local_files() { let temp = tempfile::tempdir().unwrap(); From 939c6de0be97b8f0ace143e53e6d3ff9dc76e30e Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 23:25:31 +0800 Subject: [PATCH 50/72] feat(lua): add dev provider luaclass modules --- crates/getter-core/Cargo.toml | 1 + crates/getter-core/src/lua.rs | 111 +++++++++++++++++- .../src/luaclass/fdroid_android.lua | 32 +++++ .../src/luaclass/github_android_apk.lua | 65 ++++++++++ crates/getter-operations/Cargo.toml | 2 +- .../src/lua_provider_host.rs | 84 +++++++------ 6 files changed, 259 insertions(+), 36 deletions(-) create mode 100644 crates/getter-core/src/luaclass/fdroid_android.lua create mode 100644 crates/getter-core/src/luaclass/github_android_apk.lua diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 4b1f9c5..5cb34ef 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -6,6 +6,7 @@ edition.workspace = true [features] default = ["lua"] lua = ["dep:mlua"] +provider-luaclass-dev = ["lua"] [dependencies] json_comments = "0.2" diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 28857c9..f00f014 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -14,8 +14,19 @@ use std::collections::BTreeMap; use std::fs; use std::path::{Path, PathBuf}; -const BUILTIN_LUACLASS_MODULES: &[(&str, &str)] = - &[("android", include_str!("luaclass/android.lua"))]; +const BUILTIN_LUACLASS_MODULES: &[(&str, &str)] = &[ + ("android", include_str!("luaclass/android.lua")), + #[cfg(feature = "provider-luaclass-dev")] + ( + "fdroid_android", + include_str!("luaclass/fdroid_android.lua"), + ), + #[cfg(feature = "provider-luaclass-dev")] + ( + "github_android_apk", + include_str!("luaclass/github_android_apk.lua"), + ), +]; #[derive(Debug, thiserror::Error)] pub enum LuaPackageError { @@ -1239,6 +1250,102 @@ return android.package_version { assert_eq!(package.installed.len(), 1); } + #[cfg(not(feature = "provider-luaclass-dev"))] + #[test] + fn provider_luaclass_dev_modules_are_not_default_builtins() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { package_name = "org.fdroid.fdroid" } +"#, + ) + .unwrap(); + + let err = evaluate_single_package_directory(temp.path(), "official").unwrap_err(); + + let message = err.to_string(); + assert!(matches!(err, LuaPackageError::Runtime { .. })); + assert!(message.contains("no builtin luaclass module 'luaclass.fdroid_android'")); + } + + #[cfg(feature = "provider-luaclass-dev")] + #[test] + fn provider_luaclass_dev_builtin_can_call_injected_host() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { package_name = "org.fdroid.fdroid" } +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("official").unwrap(), + package_dir, + &metadata, + script, + |lua| { + let getter_dev = lua.create_table()?; + getter_dev.set( + "fdroid_update_candidates", + lua.create_function(|lua, spec: Table| { + let package_name: String = spec.get("package_name")?; + if package_name != "org.fdroid.fdroid" { + return Err(mlua::Error::external("F-Droid package mismatch")); + } + let artifact = lua.create_table()?; + artifact.set("name", "org.fdroid.fdroid.apk")?; + artifact.set("url", "https://f-droid.org/repo/org.fdroid.fdroid.apk")?; + let artifacts = lua.create_table()?; + artifacts.raw_set(1, artifact)?; + let candidate = lua.create_table()?; + candidate.set("version", "1.20.0")?; + candidate.set("source", "fdroid")?; + candidate.set("artifacts", artifacts)?; + let candidates = lua.create_table()?; + candidates.raw_set(1, candidate)?; + Ok(candidates) + })?, + )?; + lua.globals().set("getter_dev", getter_dev) + }, + ) + .unwrap(); + + assert_eq!(package.name, "android/f-droid/app/org.fdroid.fdroid"); + assert_eq!(package.source_priority, vec!["fdroid"]); + assert_eq!(package.updates[0].version, "1.20.0"); + assert_eq!(package.updates[0].source.as_deref(), Some("fdroid")); + } + #[test] fn package_directory_can_use_repository_local_fdroid_luaclass_shape() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/luaclass/fdroid_android.lua b/crates/getter-core/src/luaclass/fdroid_android.lua new file mode 100644 index 0000000..7a1237b --- /dev/null +++ b/crates/getter-core/src/luaclass/fdroid_android.lua @@ -0,0 +1,32 @@ +local fdroid = {} + +local function require_string(table, field) + local value = table[field] + if type(value) ~= "string" or value == "" then + error("fdroid.package requires string field '" .. field .. "'") + end + return value +end + +local function fdroid_update_candidates() + if type(getter_dev) ~= "table" or type(getter_dev.fdroid_update_candidates) ~= "function" then + error("luaclass.fdroid_android requires operation-installed getter_dev.fdroid_update_candidates") + end + return getter_dev.fdroid_update_candidates +end + +function fdroid.package(spec) + if type(spec) ~= "table" then + error("fdroid.package expects a table") + end + local package_name = require_string(spec, "package_name") + local update_candidates = fdroid_update_candidates() + return package_version { + source_priority = { "fdroid" }, + updates = update_candidates { + package_name = package_name, + }, + } +end + +return fdroid diff --git a/crates/getter-core/src/luaclass/github_android_apk.lua b/crates/getter-core/src/luaclass/github_android_apk.lua new file mode 100644 index 0000000..33d8791 --- /dev/null +++ b/crates/getter-core/src/luaclass/github_android_apk.lua @@ -0,0 +1,65 @@ +local github_android = {} + +local function require_string(table, field) + local value = table[field] + if type(value) ~= "string" or value == "" then + error("github_android.package requires string field '" .. field .. "'") + end + return value +end + +local function optional_table(table, field) + local value = table[field] + if value == nil then + return nil + end + if type(value) ~= "table" then + error("github_android.package field '" .. field .. "' must be a table") + end + return value +end + +local function optional_boolean(table, field) + local value = table[field] + if value == nil then + return nil + end + if type(value) ~= "boolean" then + error("github_android.package field '" .. field .. "' must be a boolean") + end + return value +end + +local function github_release_candidates() + if type(getter_dev) ~= "table" or type(getter_dev.github_release_candidates) ~= "function" then + error("luaclass.github_android_apk requires operation-installed getter_dev.github_release_candidates") + end + return getter_dev.github_release_candidates +end + +function github_android.package(spec) + if type(spec) ~= "table" then + error("github_android.package expects a table") + end + local owner = require_string(spec, "owner") + local repo = require_string(spec, "repo") + local release_candidates = github_release_candidates() + local result = { + name = spec.name, + source_priority = { "github" }, + updates = release_candidates { + owner = owner, + repo = repo, + asset = optional_table(spec, "asset"), + include_prereleases = optional_boolean(spec, "include_prereleases"), + }, + } + if spec.android_package ~= nil then + result.installed = { + { kind = "android_package", package_name = require_string(spec, "android_package") }, + } + end + return package_version(result) +end + +return github_android diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 81c2e82..e81dab9 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true [features] default = [] lua = ["getter-core/lua", "dep:mlua"] -lua-provider-host-dev = ["lua"] +lua-provider-host-dev = ["lua", "getter-core/provider-luaclass-dev"] [dependencies] json_comments = "0.2" diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index ee16e1a..8e697af 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -584,7 +584,7 @@ mod tests { ]"#; #[test] - fn fdroid_package_eval_uses_repository_luaclass_and_provider_cache() { + fn fdroid_package_eval_uses_dev_builtin_luaclass_and_provider_cache() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_fdroid_package_fixture(data_dir, "org.fdroid.fdroid"); @@ -674,7 +674,7 @@ mod tests { } #[test] - fn github_package_eval_uses_repository_luaclass_and_provider_cache() { + fn github_package_eval_uses_dev_builtin_luaclass_and_provider_cache() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_github_package_fixture(data_dir, "[.]apk$"); @@ -749,21 +749,33 @@ mod tests { ); } - fn write_fdroid_package_fixture(data_dir: &std::path::Path, package_name: &str) { - let repo_root = data_dir.join("repo/official"); - let package_dir = repo_root.join("android/f-droid/app/org.fdroid.fdroid"); - fs::create_dir_all(repo_root.join("luaclass")).unwrap(); - fs::create_dir_all(&package_dir).unwrap(); + #[test] + fn provider_package_eval_prefers_repository_luaclass_over_dev_builtin() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_fdroid_package_fixture(data_dir, "org.fdroid.fdroid"); + let repo_luaclass = data_dir.join("repo/official/luaclass"); + fs::create_dir_all(&repo_luaclass).unwrap(); fs::write( - repo_root.join("luaclass/fdroid_android.lua"), + repo_luaclass.join("fdroid_android.lua"), r#" local fdroid = {} function fdroid.package(spec) + if spec.package_name ~= "org.fdroid.fdroid" then + error("repository override package_name mismatch") + end return package_version { - source_priority = { "fdroid" }, - updates = getter_dev.fdroid_update_candidates { - package_name = spec.package_name, + name = "repository override", + source_priority = { "repository-override" }, + updates = { + { + version = "9.9.9", + source = "repository-override", + artifacts = { + { name = "override.apk", url = "https://example.invalid/override.apk" }, + }, + }, }, } end @@ -772,6 +784,34 @@ return fdroid "#, ) .unwrap(); + + let result = fdroid_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"], json!([])); + assert_eq!(result["package"]["name"], "repository override"); + assert_eq!( + result["package"]["source_priority"], + json!(["repository-override"]) + ); + assert_eq!(result["package"]["updates"][0]["version"], "9.9.9"); + assert_eq!( + result["package"]["updates"][0]["source"], + "repository-override" + ); + } + + fn write_fdroid_package_fixture(data_dir: &std::path::Path, package_name: &str) { + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( package_dir.join("metadata.jsonc"), r#"{ @@ -795,29 +835,7 @@ return fdroid.package { fn write_github_package_fixture(data_dir: &std::path::Path, asset_include: &str) { let repo_root = data_dir.join("repo/official"); let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); - fs::create_dir_all(repo_root.join("luaclass")).unwrap(); fs::create_dir_all(&package_dir).unwrap(); - fs::write( - repo_root.join("luaclass/github_android_apk.lua"), - r#" -local github_android = {} - -function github_android.package(spec) - return package_version { - name = spec.name, - source_priority = { "github" }, - updates = getter_dev.github_release_candidates { - owner = spec.owner, - repo = spec.repo, - asset = spec.asset, - }, - } -end - -return github_android -"#, - ) - .unwrap(); fs::write( package_dir.join("metadata.jsonc"), r#"{ From 03e81cc7b413b424c486f349f86358c2e19d3ba8 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 00:25:32 +0800 Subject: [PATCH 51/72] feat(lua): add HTTP policy harness --- crates/getter-operations/src/lib.rs | 3 + .../getter-operations/src/lua_http_policy.rs | 399 ++++++++++++++++++ 2 files changed, 402 insertions(+) create mode 100644 crates/getter-operations/src/lua_http_policy.rs diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index ae19c2d..50684dc 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -11,6 +11,9 @@ pub mod fdroid_catalog; pub mod github_latest_commit; pub mod github_releases; pub mod legacy_room; +#[cfg(feature = "lua")] +#[doc(hidden)] +pub mod lua_http_policy; #[cfg(feature = "lua-provider-host-dev")] #[doc(hidden)] pub mod lua_provider_host; diff --git a/crates/getter-operations/src/lua_http_policy.rs b/crates/getter-operations/src/lua_http_policy.rs new file mode 100644 index 0000000..cb7116a --- /dev/null +++ b/crates/getter-operations/src/lua_http_policy.rs @@ -0,0 +1,399 @@ +//! Operation-owned Lua HTTP policy harness. +//! +//! This module installs getter-core's `http_get` seam for package evaluation +//! using fixture/in-memory responses. It proves the operation/runtime side owns +//! permission and package Manifest policy while `getter-core` remains transport +//! and storage agnostic. It intentionally does not perform live HTTP, cache +//! persistence/revalidation, auth, hook loading, or public CLI/native exposure. + +use getter_core::lua::{ + evaluate_package_directory_script_with_host_bindings, install_http_get_host, LuaHttpGetRequest, + LuaPackageError, +}; +use getter_core::repository::{ + PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageVersionScript, + PACKAGE_MANIFEST_FILE, +}; +use getter_core::{RepositoryId, ResolvedPackage}; +use sha2::{Digest, Sha512}; +use std::cell::RefCell; +use std::collections::{BTreeMap, BTreeSet}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::rc::Rc; + +pub const HTTP_RESPONSE_NOT_IN_MANIFEST: &str = "package.http.response_not_in_manifest"; +pub const HTTP_FIXTURE_NOT_FOUND: &str = "package.http.fixture_not_found"; + +#[derive(Debug, thiserror::Error)] +pub enum LuaHttpPolicyError { + #[error("failed to read package Manifest at {path}: {source}")] + ReadManifest { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("invalid package Manifest at {path}: {reason}")] + InvalidManifest { path: PathBuf, reason: String }, + #[error("package evaluation failed: {0}")] + PackageEval(#[from] LuaPackageError), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct LuaHttpPolicyEvaluation { + pub package: ResolvedPackage, + pub http_requests: Vec, +} + +/// Evaluates a package version script with fixture-backed `http_get`. +/// +/// This is an internal harness for operation/runtime policy tests. The caller +/// supplies URL-to-body fixtures instead of a live transport. For scripts that +/// do not declare `allow_free_network`, every returned response body must have +/// a SHA-512 digest listed in the package `Manifest`. Missing `Manifest` is an +/// empty allow-list. Scripts that declare `allow_free_network` bypass Manifest +/// membership, but requests are still captured for diagnostics/validation. +pub fn evaluate_package_directory_script_with_fixture_http( + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + responses: BTreeMap>, +) -> Result { + let allow_free_network = metadata + .permissions_for(&script.file_name) + .contains(&PackageLuaPermission::AllowFreeNetwork); + let manifest = if allow_free_network { + None + } else { + Some(PackageManifest::load( + package.path.join(PACKAGE_MANIFEST_FILE), + )?) + }; + let responses = Rc::new(responses); + let http_requests = Rc::new(RefCell::new(Vec::new())); + + let resolved = evaluate_package_directory_script_with_host_bindings( + repository_id, + package, + metadata, + script, + { + let http_requests = Rc::clone(&http_requests); + move |lua| { + install_http_get_host(lua, { + let responses = Rc::clone(&responses); + let http_requests = Rc::clone(&http_requests); + let manifest = manifest.clone(); + move |request| { + let body = responses.get(&request.url).cloned().ok_or_else(|| { + mlua::Error::external(format!( + "{HTTP_FIXTURE_NOT_FOUND}: no fixture response for {}", + request.url + )) + })?; + if let Some(manifest) = manifest.as_ref() { + let digest = sha512_hex(&body); + if !manifest.contains(&digest) { + return Err(mlua::Error::external(format!( + "{HTTP_RESPONSE_NOT_IN_MANIFEST}: response body SHA-512 {digest} for {} is not listed in package Manifest", + request.url + ))); + } + } + http_requests.borrow_mut().push(request); + Ok(body) + } + }) + } + }, + )?; + + let http_requests = http_requests.borrow().clone(); + Ok(LuaHttpPolicyEvaluation { + package: resolved, + http_requests, + }) +} + +#[derive(Debug, Clone)] +struct PackageManifest { + sha512: BTreeSet, +} + +impl PackageManifest { + fn load(path: impl AsRef) -> Result { + let path = path.as_ref(); + let source = match fs::read_to_string(path) { + Ok(source) => source, + Err(source) if source.kind() == std::io::ErrorKind::NotFound => { + return Ok(Self { + sha512: BTreeSet::new(), + }) + } + Err(source) => { + return Err(LuaHttpPolicyError::ReadManifest { + path: path.to_path_buf(), + source, + }) + } + }; + let mut sha512 = BTreeSet::new(); + for (line_index, raw_line) in source.lines().enumerate() { + let line = raw_line.trim(); + if line.is_empty() { + continue; + } + let hash = line + .split_whitespace() + .next() + .expect("non-empty line has token"); + if !is_sha512_hex(hash) { + return Err(LuaHttpPolicyError::InvalidManifest { + path: path.to_path_buf(), + reason: format!( + "line {} must start with a 128-character SHA-512 hex digest", + line_index + 1 + ), + }); + } + sha512.insert(hash.to_ascii_lowercase()); + } + Ok(Self { sha512 }) + } + + fn contains(&self, digest: &str) -> bool { + self.sha512.contains(digest) + } +} + +fn is_sha512_hex(value: &str) -> bool { + value.len() == 128 && value.bytes().all(|byte| byte.is_ascii_hexdigit()) +} + +fn sha512_hex(body: &[u8]) -> String { + let mut hasher = Sha512::new(); + hasher.update(body); + format!("{:x}", hasher.finalize()) +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::repository::{RepositoryPackageDirectoryLayout, LUA_API_SHEBANG_V1}; + use serde_json::json; + use std::fs; + + const URL: &str = "https://example.invalid/body"; + + #[test] + fn non_free_network_script_accepts_manifest_listed_response_body() { + let fixture = package_fixture(PackageFixture { + body: b"manifest body", + manifest_body: Some(b"manifest body"), + allow_free_network: false, + cache: false, + }); + + let result = fixture.evaluate().unwrap(); + + assert_eq!(result.package.name, "manifest body"); + assert_eq!(result.http_requests.len(), 1); + assert!(!result.http_requests[0].cache); + } + + #[test] + fn non_free_network_script_rejects_missing_manifest_response_body() { + let fixture = package_fixture(PackageFixture { + body: b"unlisted body", + manifest_body: None, + allow_free_network: false, + cache: false, + }); + + let err = fixture.evaluate().unwrap_err(); + + let message = err.to_string(); + assert!(matches!(err, LuaHttpPolicyError::PackageEval(_))); + assert!(message.contains(HTTP_RESPONSE_NOT_IN_MANIFEST)); + } + + #[test] + fn non_free_network_script_rejects_empty_manifest_response_body() { + let fixture = package_fixture(PackageFixture { + body: b"unlisted body", + manifest_body: Some(b"unused"), + allow_free_network: false, + cache: false, + }); + fs::write(fixture.package_dir.join(PACKAGE_MANIFEST_FILE), "").unwrap(); + + let err = fixture.evaluate().unwrap_err(); + + let message = err.to_string(); + assert!(matches!(err, LuaHttpPolicyError::PackageEval(_))); + assert!(message.contains(HTTP_RESPONSE_NOT_IN_MANIFEST)); + } + + #[test] + fn non_free_network_script_rejects_mismatched_manifest_response_body() { + let fixture = package_fixture(PackageFixture { + body: b"actual body", + manifest_body: Some(b"different body"), + allow_free_network: false, + cache: false, + }); + + let err = fixture.evaluate().unwrap_err(); + + let message = err.to_string(); + assert!(message.contains(HTTP_RESPONSE_NOT_IN_MANIFEST)); + assert!(message.contains(URL)); + } + + #[test] + fn allow_free_network_script_bypasses_manifest_membership() { + let fixture = package_fixture(PackageFixture { + body: b"free network body", + manifest_body: None, + allow_free_network: true, + cache: false, + }); + + let result = fixture.evaluate().unwrap(); + + assert_eq!(result.package.name, "free network body"); + assert!(result.package.permissions.free_network); + } + + #[test] + fn cache_true_is_forwarded_without_cache_persistence_behavior() { + let fixture = package_fixture(PackageFixture { + body: b"cache body", + manifest_body: Some(b"cache body"), + allow_free_network: false, + cache: true, + }); + + let result = fixture.evaluate().unwrap(); + + assert_eq!(result.package.name, "cache body"); + assert_eq!(result.http_requests.len(), 1); + assert!(result.http_requests[0].cache); + assert_eq!( + result.http_requests[0] + .headers + .get("Accept") + .map(String::as_str), + Some("text/plain") + ); + } + + #[test] + fn invalid_manifest_fails_before_lua_evaluation() { + let fixture = package_fixture(PackageFixture { + body: b"unused", + manifest_body: Some(b"unused"), + allow_free_network: false, + cache: false, + }); + fs::write( + fixture.package_dir.join(PACKAGE_MANIFEST_FILE), + "not-a-sha512 body", + ) + .unwrap(); + + let err = fixture.evaluate().unwrap_err(); + + assert!(matches!(err, LuaHttpPolicyError::InvalidManifest { .. })); + assert!(err.to_string().contains("line 1")); + } + + struct PackageFixture { + body: &'static [u8], + manifest_body: Option<&'static [u8]>, + allow_free_network: bool, + cache: bool, + } + + struct WrittenPackageFixture { + temp: tempfile::TempDir, + package_dir: PathBuf, + responses: BTreeMap>, + } + + impl WrittenPackageFixture { + fn evaluate(&self) -> Result { + let layout = RepositoryPackageDirectoryLayout::load(self.temp.path()).unwrap(); + let package = layout + .package(&"android/app/org.example".parse().unwrap()) + .unwrap(); + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + evaluate_package_directory_script_with_fixture_http( + &RepositoryId::new("official").unwrap(), + package, + &metadata, + script, + self.responses.clone(), + ) + } + } + + fn package_fixture(fixture: PackageFixture) -> WrittenPackageFixture { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.example"); + fs::create_dir_all(&package_dir).unwrap(); + let mut metadata = json!({ + "type": "android:app", + "android": { "package_name": "org.example" } + }); + if fixture.allow_free_network { + metadata["lua"] = json!({ + "9999.lua": { "permission": ["allow_free_network"] } + }); + } + fs::write( + package_dir.join("metadata.jsonc"), + serde_json::to_string_pretty(&metadata).unwrap(), + ) + .unwrap(); + if let Some(manifest_body) = fixture.manifest_body { + fs::write( + package_dir.join(PACKAGE_MANIFEST_FILE), + format!("{} fixture-body\n", sha512_hex(manifest_body)), + ) + .unwrap(); + } + fs::write(package_dir.join("9999.lua"), version_script(fixture.cache)).unwrap(); + let mut responses = BTreeMap::new(); + responses.insert(URL.to_owned(), fixture.body.to_vec()); + WrittenPackageFixture { + temp, + package_dir, + responses, + } + } + + fn version_script(cache: bool) -> String { + format!( + r#"{LUA_API_SHEBANG_V1} +local body = http_get("{URL}", {{ + headers = {{ Accept = "text/plain" }}, + cache = {cache}, +}}) +return package_version {{ + name = body, + updates = {{ + {{ + version = "1.0.0", + artifacts = {{ + {{ name = "fixture", url = "https://example.invalid/artifact" }}, + }}, + }}, + }}, +}} +"# + ) + } +} From 25c510acdeea043095bb44951b63bdce88bd76e5 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 01:01:03 +0800 Subject: [PATCH 52/72] feat(lua): add runtime hook harness --- crates/getter-operations/src/lib.rs | 3 + .../getter-operations/src/lua_http_policy.rs | 206 +++++++++++++++++- .../src/lua_runtime_hooks.rs | 81 +++++++ 3 files changed, 286 insertions(+), 4 deletions(-) create mode 100644 crates/getter-operations/src/lua_runtime_hooks.rs diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 50684dc..f3ddcb5 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -17,6 +17,9 @@ pub mod lua_http_policy; #[cfg(feature = "lua-provider-host-dev")] #[doc(hidden)] pub mod lua_provider_host; +#[cfg(feature = "lua")] +#[doc(hidden)] +pub mod lua_runtime_hooks; pub mod provider_cache; pub mod read_model; pub mod runtime; diff --git a/crates/getter-operations/src/lua_http_policy.rs b/crates/getter-operations/src/lua_http_policy.rs index cb7116a..da254ce 100644 --- a/crates/getter-operations/src/lua_http_policy.rs +++ b/crates/getter-operations/src/lua_http_policy.rs @@ -4,8 +4,9 @@ //! using fixture/in-memory responses. It proves the operation/runtime side owns //! permission and package Manifest policy while `getter-core` remains transport //! and storage agnostic. It intentionally does not perform live HTTP, cache -//! persistence/revalidation, auth, hook loading, or public CLI/native exposure. +//! persistence/revalidation, auth, or public CLI/native exposure. +use crate::lua_runtime_hooks::load_runtime_hooks; use getter_core::lua::{ evaluate_package_directory_script_with_host_bindings, install_http_get_host, LuaHttpGetRequest, LuaPackageError, @@ -43,6 +44,7 @@ pub enum LuaHttpPolicyError { pub struct LuaHttpPolicyEvaluation { pub package: ResolvedPackage, pub http_requests: Vec, + pub runtime_hooks: Vec, } /// Evaluates a package version script with fixture-backed `http_get`. @@ -59,6 +61,26 @@ pub fn evaluate_package_directory_script_with_fixture_http( metadata: &PackageDirectoryMetadata, script: &PackageVersionScript, responses: BTreeMap>, +) -> Result { + evaluate_package_directory_script_with_fixture_http_and_runtime_hooks( + repository_id, + package, + metadata, + script, + responses, + None, + ) +} + +/// Evaluates a package version script with fixture-backed `http_get` and +/// optional runtime hooks from `/rc/hook/*.lua`. +pub fn evaluate_package_directory_script_with_fixture_http_and_runtime_hooks( + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + responses: BTreeMap>, + data_dir: Option<&Path>, ) -> Result { let allow_free_network = metadata .permissions_for(&script.file_name) @@ -72,6 +94,8 @@ pub fn evaluate_package_directory_script_with_fixture_http( }; let responses = Rc::new(responses); let http_requests = Rc::new(RefCell::new(Vec::new())); + let runtime_hooks = Rc::new(RefCell::new(Vec::new())); + let data_dir = data_dir.map(Path::to_path_buf); let resolved = evaluate_package_directory_script_with_host_bindings( repository_id, @@ -80,6 +104,7 @@ pub fn evaluate_package_directory_script_with_fixture_http( script, { let http_requests = Rc::clone(&http_requests); + let runtime_hooks = Rc::clone(&runtime_hooks); move |lua| { install_http_get_host(lua, { let responses = Rc::clone(&responses); @@ -104,15 +129,21 @@ pub fn evaluate_package_directory_script_with_fixture_http( http_requests.borrow_mut().push(request); Ok(body) } - }) + })?; + if let Some(data_dir) = data_dir.as_deref() { + *runtime_hooks.borrow_mut() = load_runtime_hooks(lua, data_dir)?; + } + Ok(()) } }, )?; let http_requests = http_requests.borrow().clone(); + let runtime_hooks = runtime_hooks.borrow().clone(); Ok(LuaHttpPolicyEvaluation { package: resolved, http_requests, + runtime_hooks, }) } @@ -309,6 +340,148 @@ mod tests { assert!(err.to_string().contains("line 1")); } + #[test] + fn missing_runtime_hook_directory_is_noop() { + let fixture = package_fixture(PackageFixture { + body: b"manifest body", + manifest_body: Some(b"manifest body"), + allow_free_network: false, + cache: false, + }); + + let result = fixture.evaluate_with_hooks().unwrap(); + + assert_eq!(result.package.name, "manifest body"); + assert!(result.runtime_hooks.is_empty()); + } + + #[test] + fn dot_prefixed_runtime_hook_is_ignored() { + let fixture = package_fixture(PackageFixture { + body: b"manifest body", + manifest_body: Some(b"manifest body"), + allow_free_network: false, + cache: false, + }); + fixture.write_hook(".10-fail.lua", r#"error("ignored hook should not run")"#); + + let result = fixture.evaluate_with_hooks().unwrap(); + + assert_eq!(result.package.name, "manifest body"); + assert!(result.runtime_hooks.is_empty()); + } + + #[test] + fn runtime_hooks_load_in_deterministic_filename_order() { + let fixture = package_fixture(PackageFixture { + body: b"manifest body", + manifest_body: Some(b"manifest body"), + allow_free_network: false, + cache: false, + }); + fixture.write_hook("20-second.lua", r#"hook_order = (hook_order or "") .. "b""#); + fixture.write_hook("10-first.lua", r#"hook_order = (hook_order or "") .. "a""#); + + let result = fixture.evaluate_with_hooks().unwrap(); + + assert_eq!(result.package.name, "manifest body"); + let loaded = result + .runtime_hooks + .iter() + .map(|path| path.file_name().unwrap().to_str().unwrap().to_owned()) + .collect::>(); + assert_eq!(loaded, vec!["10-first.lua", "20-second.lua"]); + assert_eq!( + result.http_requests[0] + .headers + .get("X-Hook-Order") + .map(String::as_str), + Some("ab") + ); + } + + #[test] + fn runtime_hook_can_wrap_http_get_and_rewrite_request() { + let mut fixture = package_fixture(PackageFixture { + body: b"unused original body", + manifest_body: Some(b"mirror body"), + allow_free_network: false, + cache: false, + }); + fixture.add_response("https://mirror.invalid/body", b"mirror body"); + fixture.write_hook( + "10-http-rewrite.lua", + r#" +local original_http_get = getter_builtin.http_get +function http_get(url, options) + options = options or {} + options.headers = options.headers or {} + options.headers["X-Original-Url"] = url + options.cache = true + return original_http_get("https://mirror.invalid/body", options) +end +"#, + ); + + let result = fixture.evaluate_with_hooks().unwrap(); + + assert_eq!(result.package.name, "mirror body"); + assert_eq!(result.http_requests.len(), 1); + assert_eq!(result.http_requests[0].url, "https://mirror.invalid/body"); + assert!(result.http_requests[0].cache); + assert_eq!( + result.http_requests[0] + .headers + .get("X-Original-Url") + .map(String::as_str), + Some(URL) + ); + } + + #[test] + fn runtime_hook_rewrite_still_enforces_manifest_on_returned_body() { + let mut fixture = package_fixture(PackageFixture { + body: b"unused original body", + manifest_body: Some(b"unused original body"), + allow_free_network: false, + cache: false, + }); + fixture.add_response("https://mirror.invalid/body", b"unlisted mirror body"); + fixture.write_hook( + "10-http-rewrite.lua", + r#" +local original_http_get = getter_builtin.http_get +function http_get(url, options) + return original_http_get("https://mirror.invalid/body", options) +end +"#, + ); + + let err = fixture.evaluate_with_hooks().unwrap_err(); + + let message = err.to_string(); + assert!(message.contains(HTTP_RESPONSE_NOT_IN_MANIFEST)); + assert!(message.contains("https://mirror.invalid/body")); + } + + #[test] + fn runtime_hook_failure_fails_closed() { + let fixture = package_fixture(PackageFixture { + body: b"manifest body", + manifest_body: Some(b"manifest body"), + allow_free_network: false, + cache: false, + }); + fixture.write_hook("10-fail.lua", r#"error("hook failed closed")"#); + + let err = fixture.evaluate_with_hooks().unwrap_err(); + + let message = err.to_string(); + assert!(matches!(err, LuaHttpPolicyError::PackageEval(_))); + assert!(message.contains("failed to execute runtime hook")); + assert!(message.contains("hook failed closed")); + } + struct PackageFixture { body: &'static [u8], manifest_body: Option<&'static [u8]>, @@ -324,20 +497,42 @@ mod tests { impl WrittenPackageFixture { fn evaluate(&self) -> Result { + self.evaluate_with_data_dir(None) + } + + fn evaluate_with_hooks(&self) -> Result { + self.evaluate_with_data_dir(Some(self.temp.path())) + } + + fn evaluate_with_data_dir( + &self, + data_dir: Option<&Path>, + ) -> Result { let layout = RepositoryPackageDirectoryLayout::load(self.temp.path()).unwrap(); let package = layout .package(&"android/app/org.example".parse().unwrap()) .unwrap(); let metadata = layout.package_metadata(package).unwrap(); let script = layout.unambiguous_version_script(package).unwrap(); - evaluate_package_directory_script_with_fixture_http( + evaluate_package_directory_script_with_fixture_http_and_runtime_hooks( &RepositoryId::new("official").unwrap(), package, &metadata, script, self.responses.clone(), + data_dir, ) } + + fn write_hook(&self, file_name: &str, source: &str) { + let hook_dir = self.temp.path().join("rc/hook"); + fs::create_dir_all(&hook_dir).unwrap(); + fs::write(hook_dir.join(file_name), source).unwrap(); + } + + fn add_response(&mut self, url: &str, body: &'static [u8]) { + self.responses.insert(url.to_owned(), body.to_vec()); + } } fn package_fixture(fixture: PackageFixture) -> WrittenPackageFixture { @@ -379,7 +574,10 @@ mod tests { format!( r#"{LUA_API_SHEBANG_V1} local body = http_get("{URL}", {{ - headers = {{ Accept = "text/plain" }}, + headers = {{ + Accept = "text/plain", + ["X-Hook-Order"] = hook_order or "", + }}, cache = {cache}, }}) return package_version {{ diff --git a/crates/getter-operations/src/lua_runtime_hooks.rs b/crates/getter-operations/src/lua_runtime_hooks.rs new file mode 100644 index 0000000..e73ed86 --- /dev/null +++ b/crates/getter-operations/src/lua_runtime_hooks.rs @@ -0,0 +1,81 @@ +//! Operation-owned runtime/local Lua hook loading. +//! +//! This module is an internal harness for ADR-0012 `rc/hook/*.lua` runtime +//! policy. It only loads local hook source into an already prepared Lua +//! environment; callers still decide which host functions exist and which +//! operation policy applies. It intentionally does not expose hooks through +//! CLI/native/Flutter surfaces or make hook policy part of `getter-core`. + +use mlua::Lua; +use std::fs; +use std::path::{Path, PathBuf}; + +const RUNTIME_HOOK_DIR: &str = "rc/hook"; + +/// Loads enabled runtime hooks from `/rc/hook/*.lua`. +/// +/// Hooks are discovered from the filesystem only, dot-prefixed Lua basenames +/// are ignored, and the remaining hooks are loaded in deterministic path order. +/// Any read or runtime error fails closed by returning an error to the caller. +pub fn load_runtime_hooks(lua: &Lua, data_dir: &Path) -> mlua::Result> { + let hook_dir = data_dir.join(RUNTIME_HOOK_DIR); + let entries = match fs::read_dir(&hook_dir) { + Ok(entries) => entries, + Err(source) if source.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()), + Err(source) => { + return Err(mlua::Error::external(format!( + "failed to read runtime hook directory {}: {source}", + hook_dir.display() + ))) + } + }; + + let mut hooks = Vec::new(); + for entry in entries { + let entry = entry.map_err(|source| { + mlua::Error::external(format!( + "failed to read runtime hook directory entry in {}: {source}", + hook_dir.display() + )) + })?; + let path = entry.path(); + if !is_enabled_lua_hook(&path) { + continue; + } + hooks.push(path); + } + hooks.sort(); + + for hook in &hooks { + let source = fs::read_to_string(hook).map_err(|source| { + mlua::Error::external(format!( + "failed to read runtime hook {}: {source}", + hook.display() + )) + })?; + lua.load(&source) + .set_name(hook.to_string_lossy().as_ref()) + .exec() + .map_err(|source| { + mlua::Error::external(format!( + "failed to execute runtime hook {}: {source}", + hook.display() + )) + })?; + } + + Ok(hooks) +} + +fn is_enabled_lua_hook(path: &Path) -> bool { + if !path.is_file() { + return false; + } + if path.extension().and_then(|extension| extension.to_str()) != Some("lua") { + return false; + } + let Some(file_name) = path.file_name().and_then(|file_name| file_name.to_str()) else { + return false; + }; + !file_name.starts_with('.') +} From bb5b7032554b76d01d164772e4490698ee315d44 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 10:47:34 +0800 Subject: [PATCH 53/72] feat(lua): add stable provider host harness --- .../src/lua_provider_host.rs | 1214 ++++++++++++++--- 1 file changed, 1056 insertions(+), 158 deletions(-) diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 8e697af..15cb3b1 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -3,8 +3,8 @@ //! This module is an implementation bridge toward ADR-0012 provider-backed Lua //! modules. It exercises higher-level getter operations installing host //! functions for package evaluation without making `getter-core` depend on -//! provider cache/storage crates and without defining the final stable Lua -//! provider API. +//! provider cache/storage crates. The stable `getter.provider.*` harness here +//! is still fixture-backed, dev-feature-gated, and hidden from product APIs. use crate::fdroid_catalog::{ read_or_refresh_fdroid_catalog, FdroidCatalogOperationError, FdroidEndpointConfig, @@ -14,10 +14,14 @@ use crate::github_releases::{ read_or_refresh_github_releases, GithubReleaseConfig, GithubReleaseOperationError, DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, }; +use crate::lua_runtime_hooks::load_runtime_hooks; use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource}; use getter_core::lua::{evaluate_package_directory_script_with_host_bindings, LuaPackageError}; -use getter_core::repository::{RepositoryLoadError, RepositoryPackageDirectoryLayout}; -use getter_core::{PackageId, RepositoryId, UpdateArtifact, UpdateCandidate}; +use getter_core::repository::{ + PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageVersionScript, + RepositoryLoadError, RepositoryPackageDirectoryLayout, PACKAGE_MANIFEST_FILE, +}; +use getter_core::{PackageId, RepositoryId, ResolvedPackage}; use getter_providers::{ github_release_update_candidates, FdroidEndpoint, GithubAssetFilter, GithubProviderError, GithubRelease, GithubReleaseCandidateOptions, @@ -25,8 +29,10 @@ use getter_providers::{ use getter_storage::{CacheDb, MainDb, StorageError, StoredRepository}; use mlua::{Lua, Table, Value as LuaValue}; use serde::Deserialize; -use serde_json::{json, Value}; +use serde_json::{json, Map, Value}; +use sha2::{Digest, Sha512}; use std::cell::RefCell; +use std::collections::BTreeSet; use std::path::{Path, PathBuf}; use std::rc::Rc; @@ -48,9 +54,19 @@ pub enum LuaProviderHostOperationError { GithubProvider(#[from] GithubProviderError), #[error("Lua provider-host response serialization failed: {0}")] Serialization(#[from] serde_json::Error), + #[error("failed to read package Manifest at {path}: {source}")] + ReadManifest { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("invalid package Manifest at {path}: {reason}")] + InvalidManifest { path: PathBuf, reason: String }, } const FDROID_PACKAGE_NOT_FOUND: &str = "provider.fdroid.package_not_found"; +const PROVIDER_RESPONSE_NOT_IN_MANIFEST: &str = "package.provider.response_not_in_manifest"; +const PROVIDER_CACHE_PROVENANCE_MISSING: &str = "package.provider.cache_provenance_missing"; /// Evaluates a package with a fixture-backed F-Droid catalog host binding. /// @@ -142,6 +158,15 @@ struct FdroidDevHostConfig { provider_calls: Rc>>, } +#[derive(Clone)] +struct FdroidProviderHostConfig { + cache_db_path: PathBuf, + endpoint: FdroidEndpointConfig, + mode: ProviderCacheMode, + index_xml: Option, + manifest: Option, +} + /// Evaluates a package with a fixture-backed GitHub release host binding. /// /// This is a development/provider-host tracer, not the stable package-eval or @@ -206,6 +231,153 @@ pub fn github_package_eval_json( })) } +/// Evaluates a package with the fixture-backed stable `getter.provider.*` host. +/// +/// This is an internal Slice 1 harness for ADR-0012 provider host API v1. It +/// installs the stable namespace and runtime hooks, but still uses fixture data +/// and remains hidden from CLI/native/Flutter product APIs. +pub fn stable_provider_package_eval_json( + data_dir: &Path, + request_json: &str, +) -> Result { + let request: StableProviderPackageEvalRequest = serde_json::from_str(request_json) + .map_err(|source| LuaProviderHostOperationError::InvalidRequest(source.to_string()))?; + let mode = provider_cache_mode(request.mode.as_deref())?; + let endpoint = FdroidEndpointConfig { + endpoint_id: request + .fdroid_endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), + endpoint_url: request + .fdroid_endpoint_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), + }; + let github_api_base_url = request + .github_api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()); + let main_db = MainDb::open(data_dir.join("main.db"))?; + let repository = find_repository(&main_db, &request.repository_id)?; + let repository_path = repo_path(&repository)?; + let layout = RepositoryPackageDirectoryLayout::load(&repository_path)?; + let package_directory = layout.package(&request.package_id).ok_or_else(|| { + LuaProviderHostOperationError::InvalidRequest(format!( + "package '{}' was not found in repository '{}'", + request.package_id, request.repository_id + )) + })?; + let metadata = layout.package_metadata(package_directory)?; + let script = layout.unambiguous_version_script(package_directory)?; + let provider_calls = Rc::new(RefCell::new(Vec::new())); + let runtime_hooks = Rc::new(RefCell::new(Vec::new())); + let fdroid_index_xml = request.fdroid_index_xml; + let github_releases_json = request.github_releases_json; + let default_include_prereleases = request.github_include_prereleases; + let manifest = if metadata + .permissions_for(&script.file_name) + .contains(&PackageLuaPermission::AllowFreeNetwork) + { + None + } else { + Some(PackageManifest::load( + package_directory.path.join(PACKAGE_MANIFEST_FILE), + )?) + }; + // Parsed provider cache currently has no source-body digest provenance. + // Non-free scripts therefore refetch/revalidate fixture bodies instead of + // accepting cache hits that cannot prove Manifest compatibility. + let provider_mode = if manifest.is_some() { + ProviderCacheMode::ForceRefresh + } else { + mode + }; + let package = evaluate_with_stable_provider_host( + data_dir, + &repository.id, + package_directory, + &metadata, + script, + StableProviderHostEvalConfig { + fdroid: FdroidProviderHostConfig { + cache_db_path: data_dir.join("cache.db"), + endpoint, + mode: provider_mode, + index_xml: fdroid_index_xml, + manifest: manifest.clone(), + }, + github: GithubProviderHostConfig { + cache_db_path: data_dir.join("cache.db"), + api_base_url: github_api_base_url, + mode: provider_mode, + releases_json: github_releases_json, + default_include_prereleases, + manifest, + }, + provider_calls: Rc::clone(&provider_calls), + runtime_hooks: Rc::clone(&runtime_hooks), + }, + )?; + let package = serde_json::to_value(package)?; + + Ok(json!({ + "operation": "provider.package_eval.fixture", + "package": package, + "provider_calls": provider_calls.borrow().clone(), + "runtime_hooks": runtime_hooks.borrow().clone(), + })) +} + +#[derive(Debug, Deserialize)] +struct StableProviderPackageEvalRequest { + repository_id: RepositoryId, + package_id: PackageId, + #[serde(default)] + mode: Option, + #[serde(default)] + fdroid_endpoint_id: Option, + #[serde(default)] + fdroid_endpoint_url: Option, + #[serde(default)] + fdroid_index_xml: Option, + #[serde(default)] + github_api_base_url: Option, + #[serde(default)] + github_releases_json: Option, + #[serde(default)] + github_include_prereleases: bool, +} + +struct StableProviderHostEvalConfig { + fdroid: FdroidProviderHostConfig, + github: GithubProviderHostConfig, + provider_calls: Rc>>, + runtime_hooks: Rc>>, +} + +fn evaluate_with_stable_provider_host( + data_dir: &Path, + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + config: StableProviderHostEvalConfig, +) -> Result { + evaluate_package_directory_script_with_host_bindings( + repository_id, + package, + metadata, + script, + move |lua| { + install_stable_provider_host(lua, config.fdroid, config.github)?; + *config.runtime_hooks.borrow_mut() = load_runtime_hooks(lua, data_dir)?; + install_provider_call_trace(lua, Rc::clone(&config.provider_calls))?; + Ok(()) + }, + ) + .map_err(Into::into) +} + #[derive(Debug, Deserialize)] struct GithubPackageEvalRequest { repository_id: RepositoryId, @@ -229,58 +401,37 @@ struct GithubDevHostConfig { provider_calls: Rc>>, } +#[derive(Clone)] +struct GithubProviderHostConfig { + cache_db_path: PathBuf, + api_base_url: String, + mode: ProviderCacheMode, + releases_json: Option, + default_include_prereleases: bool, + manifest: Option, +} + fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Result<()> { let host = lua.create_table()?; host.set( "fdroid_update_candidates", lua.create_function(move |lua, spec: Table| { - let request = FdroidUpdateHostRequest::from_lua_table(&spec)?; - let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; - let endpoint = config.endpoint.clone(); - let refresh_fixture = config.index_xml.clone(); - let result = read_or_refresh_fdroid_catalog(&db, endpoint, config.mode, || { - refresh_fixture.ok_or_else(|| { - "fixture-backed F-Droid catalog host requires index_xml".to_owned() - }) - }) - .map_err(mlua::Error::external)?; - let mut diagnostics = result - .diagnostics - .iter() - .map(provider_diagnostic_json) - .collect::>(); - let candidates = result - .app(&request.package_name) - .map(|app| { - let endpoint = FdroidEndpoint { - name: result.catalog.endpoint.name.clone(), - url: Some(result.endpoint.endpoint_url.clone()), - timestamp: result.catalog.endpoint.timestamp.clone(), - }; - app.update_candidates(&endpoint) - }) - .unwrap_or_else(|| { - diagnostics.push(json!({ - "code": FDROID_PACKAGE_NOT_FOUND, - "message": format!("F-Droid package '{}' was not found in endpoint '{}'", request.package_name, result.endpoint.endpoint_id), - "provider": FDROID_PROVIDER_ID, - "endpoint_id": result.endpoint.endpoint_id, - "package_name": request.package_name, - "cache_key": result.cache_key, - })); - Vec::new() - }); - config.provider_calls.borrow_mut().push(json!({ - "provider": FDROID_PROVIDER_ID, - "request": "catalog", - "endpoint_id": result.endpoint.endpoint_id, - "endpoint_url": result.endpoint.endpoint_url, - "package_name": request.package_name, - "cache_key": result.cache_key, - "source": provider_source_json(result.source), - "diagnostics": diagnostics, - })); - update_candidates_to_lua(lua, &candidates) + let result = fdroid_update_candidates_envelope( + lua, + spec, + FdroidProviderHostConfig { + cache_db_path: config.cache_db_path.clone(), + endpoint: config.endpoint.clone(), + mode: config.mode, + index_xml: config.index_xml.clone(), + manifest: None, + }, + )?; + config + .provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + envelope_candidates(result) })?, )?; lua.globals().set("getter_dev", host) @@ -297,9 +448,7 @@ impl FdroidUpdateHostRequest { package_name: package_name .filter(|value| !value.trim().is_empty()) .ok_or_else(|| { - mlua::Error::external( - "getter_dev.fdroid_update_candidates missing package_name", - ) + mlua::Error::external("F-Droid provider host missing package_name") })?, }) } @@ -310,60 +459,105 @@ fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Resu host.set( "github_release_candidates", lua.create_function(move |lua, spec: Table| { - let request = GithubReleaseHostRequest::from_lua_table(&spec)?; - let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; - let provider_config = GithubReleaseConfig { - api_base_url: config.api_base_url.clone(), - owner: request.owner, - repo: request.repo, - }; - let refresh_fixture = config.releases_json.clone(); - let result = read_or_refresh_github_releases(&db, provider_config, config.mode, || { - refresh_fixture.ok_or_else(|| { - "fixture-backed GitHub release host requires releases_json".to_owned() - }) - }) - .map_err(mlua::Error::external)?; - let options = GithubReleaseCandidateOptions { - include_prereleases: request - .include_prereleases - .unwrap_or(config.default_include_prereleases), - asset_filter: request.asset_filter, - }; - let candidates = github_release_update_candidates(&result.releases, &options) - .map_err(mlua::Error::external)?; - let mut diagnostics = result - .diagnostics - .iter() - .map(provider_diagnostic_json) - .collect::>(); - if candidates.is_empty() - && github_eligible_release_count(&result.releases, &options) > 0 - { - diagnostics.push(json!({ - "code": GITHUB_ASSET_NOT_FOUND, - "message": "no GitHub release assets matched the requested filters", - "provider": GITHUB_PROVIDER_ID, - "owner": result.config.owner, - "repo": result.config.repo, - "cache_key": result.cache_key, - })); - } - config.provider_calls.borrow_mut().push(json!({ - "provider": GITHUB_PROVIDER_ID, - "request": "releases", - "owner": result.config.owner, - "repo": result.config.repo, - "cache_key": result.cache_key, - "source": provider_source_json(result.source), - "diagnostics": diagnostics, - })); - update_candidates_to_lua(lua, &candidates) + let result = github_release_candidates_envelope( + lua, + spec, + GithubProviderHostConfig { + cache_db_path: config.cache_db_path.clone(), + api_base_url: config.api_base_url.clone(), + mode: config.mode, + releases_json: config.releases_json.clone(), + default_include_prereleases: config.default_include_prereleases, + manifest: None, + }, + )?; + config + .provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + envelope_candidates(result) })?, )?; lua.globals().set("getter_dev", host) } +fn install_stable_provider_host( + lua: &Lua, + fdroid: FdroidProviderHostConfig, + github: GithubProviderHostConfig, +) -> mlua::Result<()> { + let fdroid_update_candidates = lua.create_function(move |lua, spec: Table| { + fdroid_update_candidates_envelope(lua, spec, fdroid.clone()) + })?; + let github_release_candidates = lua.create_function(move |lua, spec: Table| { + github_release_candidates_envelope(lua, spec, github.clone()) + })?; + + let globals = lua.globals(); + let getter = nested_table(lua, globals.clone(), "getter")?; + let provider = nested_table(lua, getter, "provider")?; + let fdroid_table = nested_table(lua, provider.clone(), "fdroid")?; + fdroid_table.set("update_candidates", fdroid_update_candidates.clone())?; + let github_table = nested_table(lua, provider, "github")?; + github_table.set("release_candidates", github_release_candidates.clone())?; + + let getter_builtin = nested_table(lua, globals, "getter_builtin")?; + let builtin_provider = nested_table(lua, getter_builtin, "provider")?; + let builtin_fdroid_table = nested_table(lua, builtin_provider.clone(), "fdroid")?; + builtin_fdroid_table.set("update_candidates", fdroid_update_candidates)?; + let builtin_github_table = nested_table(lua, builtin_provider, "github")?; + builtin_github_table.set("release_candidates", github_release_candidates) +} + +fn nested_table(lua: &Lua, parent: Table, key: &str) -> mlua::Result
{ + match parent.get::(key)? { + LuaValue::Table(table) => Ok(table), + LuaValue::Nil => { + let table = lua.create_table()?; + parent.set(key, table.clone())?; + Ok(table) + } + _ => Err(mlua::Error::external(format!("{key} must be a table"))), + } +} + +fn install_provider_call_trace( + lua: &Lua, + provider_calls: Rc>>, +) -> mlua::Result<()> { + let globals = lua.globals(); + let getter: Table = globals.get("getter")?; + let provider: Table = getter.get("provider")?; + let fdroid_table: Table = provider.get("fdroid")?; + let original_fdroid: mlua::Function = fdroid_table.get("update_candidates")?; + fdroid_table.set( + "update_candidates", + lua.create_function({ + let provider_calls = Rc::clone(&provider_calls); + move |_, spec: Table| { + let result: Table = original_fdroid.call(spec)?; + provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + Ok(result) + } + })?, + )?; + + let github_table: Table = provider.get("github")?; + let original_github: mlua::Function = github_table.get("release_candidates")?; + github_table.set( + "release_candidates", + lua.create_function(move |_, spec: Table| { + let result: Table = original_github.call(spec)?; + provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + Ok(result) + })?, + ) +} + struct GithubReleaseHostRequest { owner: String, repo: String, @@ -382,6 +576,353 @@ impl GithubReleaseHostRequest { } } +fn fdroid_update_candidates_envelope( + lua: &Lua, + spec: Table, + config: FdroidProviderHostConfig, +) -> mlua::Result
{ + let request = FdroidUpdateHostRequest::from_lua_table(&spec)?; + let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; + let endpoint = config.endpoint; + let refresh_fixture = config.index_xml; + let manifest = config.manifest.clone(); + let result = read_or_refresh_fdroid_catalog(&db, endpoint, config.mode, || { + let xml = refresh_fixture + .ok_or_else(|| "fixture-backed F-Droid catalog host requires index_xml".to_owned())?; + require_manifest_body(&manifest, FDROID_PROVIDER_ID, &xml) + .map_err(|source| source.to_string())?; + Ok(xml) + }) + .map_err(mlua::Error::external)?; + reject_unproven_manifest_cache(&manifest, FDROID_PROVIDER_ID, result.source)?; + let mut diagnostics = result + .diagnostics + .iter() + .map(provider_diagnostic_json) + .collect::>(); + let candidates = result + .app(&request.package_name) + .map(|app| { + let endpoint = FdroidEndpoint { + name: result.catalog.endpoint.name.clone(), + url: Some(result.endpoint.endpoint_url.clone()), + timestamp: result.catalog.endpoint.timestamp.clone(), + }; + app.update_candidates(&endpoint) + }) + .unwrap_or_else(|| { + diagnostics.push(json!({ + "code": FDROID_PACKAGE_NOT_FOUND, + "message": format!("F-Droid package '{}' was not found in endpoint '{}'", request.package_name, result.endpoint.endpoint_id), + "provider": FDROID_PROVIDER_ID, + "endpoint_id": result.endpoint.endpoint_id, + "package_name": request.package_name, + "cache_key": result.cache_key, + })); + Vec::new() + }); + let envelope = provider_envelope_to_lua( + lua, + json!({ + "provider": FDROID_PROVIDER_ID, + "request": "catalog", + "endpoint_id": result.endpoint.endpoint_id, + "endpoint_url": result.endpoint.endpoint_url, + "package_name": request.package_name, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "candidates": candidates, + "diagnostics": diagnostics, + }), + )?; + Ok(envelope) +} + +fn github_release_candidates_envelope( + lua: &Lua, + spec: Table, + config: GithubProviderHostConfig, +) -> mlua::Result
{ + let request = GithubReleaseHostRequest::from_lua_table(&spec)?; + let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; + let provider_config = GithubReleaseConfig { + api_base_url: config.api_base_url, + owner: request.owner, + repo: request.repo, + }; + let refresh_fixture = config.releases_json; + let manifest = config.manifest.clone(); + let result = read_or_refresh_github_releases(&db, provider_config, config.mode, || { + let releases = refresh_fixture.ok_or_else(|| { + "fixture-backed GitHub release host requires releases_json".to_owned() + })?; + require_manifest_body(&manifest, GITHUB_PROVIDER_ID, &releases) + .map_err(|source| source.to_string())?; + Ok(releases) + }) + .map_err(mlua::Error::external)?; + reject_unproven_manifest_cache(&manifest, GITHUB_PROVIDER_ID, result.source)?; + let options = GithubReleaseCandidateOptions { + include_prereleases: request + .include_prereleases + .unwrap_or(config.default_include_prereleases), + asset_filter: request.asset_filter, + }; + let candidates = github_release_update_candidates(&result.releases, &options) + .map_err(mlua::Error::external)?; + let mut diagnostics = result + .diagnostics + .iter() + .map(provider_diagnostic_json) + .collect::>(); + if candidates.is_empty() && github_eligible_release_count(&result.releases, &options) > 0 { + diagnostics.push(json!({ + "code": GITHUB_ASSET_NOT_FOUND, + "message": "no GitHub release assets matched the requested filters", + "provider": GITHUB_PROVIDER_ID, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + })); + } + let envelope = provider_envelope_to_lua( + lua, + json!({ + "provider": GITHUB_PROVIDER_ID, + "request": "releases", + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "candidates": candidates, + "diagnostics": diagnostics, + }), + )?; + Ok(envelope) +} + +fn provider_envelope_to_lua(lua: &Lua, mut value: Value) -> mlua::Result
{ + if value + .get("candidates") + .and_then(Value::as_array) + .is_some_and(Vec::is_empty) + { + if let Some(object) = value.as_object_mut() { + object.remove("candidates"); + } + } + json_object_to_lua(lua, value) +} + +fn provider_call_from_envelope(envelope: &Table) -> mlua::Result { + let mut object = Map::new(); + for key in [ + "provider", + "request", + "endpoint_id", + "endpoint_url", + "package_name", + "owner", + "repo", + "cache_key", + "source", + "diagnostics", + ] { + let value: LuaValue = envelope.get(key)?; + if !matches!(value, LuaValue::Nil) { + object.insert(key.to_owned(), lua_value_to_json(value)?); + } + } + Ok(Value::Object(object)) +} + +fn envelope_candidates(envelope: Table) -> mlua::Result { + envelope.get("candidates") +} + +fn json_object_to_lua(lua: &Lua, value: Value) -> mlua::Result
{ + match json_value_to_lua(lua, value)? { + LuaValue::Table(table) => Ok(table), + _ => Err(mlua::Error::external( + "provider host envelope must be an object", + )), + } +} + +fn json_value_to_lua(lua: &Lua, value: Value) -> mlua::Result { + match value { + Value::Null => Ok(LuaValue::Nil), + Value::Bool(value) => Ok(LuaValue::Boolean(value)), + Value::Number(number) => { + if let Some(value) = number.as_i64() { + return Ok(LuaValue::Integer(value)); + } + if let Some(value) = number.as_u64().and_then(|value| i64::try_from(value).ok()) { + return Ok(LuaValue::Integer(value)); + } + Ok(LuaValue::Number(number.as_f64().ok_or_else(|| { + mlua::Error::external(format!( + "provider host number {number} cannot be represented" + )) + })?)) + } + Value::String(value) => lua.create_string(value).map(LuaValue::String), + Value::Array(values) => { + let table = lua.create_table()?; + for (index, value) in values.into_iter().enumerate() { + table.raw_set(index + 1, json_value_to_lua(lua, value)?)?; + } + Ok(LuaValue::Table(table)) + } + Value::Object(values) => { + let table = lua.create_table()?; + for (key, value) in values { + if !value.is_null() { + table.set(key, json_value_to_lua(lua, value)?)?; + } + } + Ok(LuaValue::Table(table)) + } + } +} + +fn lua_value_to_json(value: LuaValue) -> mlua::Result { + match value { + LuaValue::Nil => Ok(Value::Null), + LuaValue::Boolean(value) => Ok(Value::Bool(value)), + LuaValue::Integer(value) => Ok(json!(value)), + LuaValue::Number(value) => Ok(json!(value)), + LuaValue::String(value) => Ok(Value::String(value.to_str()?.to_owned())), + LuaValue::Table(table) => lua_table_to_json_value(table), + _ => Err(mlua::Error::external( + "provider host trace contains unsupported Lua value", + )), + } +} + +fn lua_table_to_json_value(table: Table) -> mlua::Result { + let mut len = 0; + for pair in table.clone().pairs::() { + let (key, _) = pair?; + if let LuaValue::Integer(index) = key { + if index > 0 { + len = len.max(index as usize); + continue; + } + } + let mut object = Map::new(); + for pair in table.pairs::() { + let (key, value) = pair?; + let key = match key { + LuaValue::String(value) => value.to_str()?.to_owned(), + LuaValue::Integer(value) => value.to_string(), + _ => { + return Err(mlua::Error::external( + "provider host trace table keys must be strings or positive integers", + )) + } + }; + object.insert(key, lua_value_to_json(value)?); + } + return Ok(Value::Object(object)); + } + let mut array = Vec::with_capacity(len); + for index in 1..=len { + array.push(lua_value_to_json(table.raw_get(index)?)?); + } + Ok(Value::Array(array)) +} + +#[derive(Debug, Clone)] +struct PackageManifest { + sha512: BTreeSet, +} + +impl PackageManifest { + fn load(path: impl AsRef) -> Result { + let path = path.as_ref(); + let source = match std::fs::read_to_string(path) { + Ok(source) => source, + Err(source) if source.kind() == std::io::ErrorKind::NotFound => { + return Ok(Self { + sha512: BTreeSet::new(), + }) + } + Err(source) => { + return Err(LuaProviderHostOperationError::ReadManifest { + path: path.to_path_buf(), + source, + }) + } + }; + let mut sha512 = BTreeSet::new(); + for (line_index, raw_line) in source.lines().enumerate() { + let line = raw_line.trim(); + if line.is_empty() { + continue; + } + let hash = line + .split_whitespace() + .next() + .expect("non-empty line has token"); + if !is_sha512_hex(hash) { + return Err(LuaProviderHostOperationError::InvalidManifest { + path: path.to_path_buf(), + reason: format!( + "line {} must start with a 128-character SHA-512 hex digest", + line_index + 1 + ), + }); + } + sha512.insert(hash.to_ascii_lowercase()); + } + Ok(Self { sha512 }) + } + + fn allows_body(&self, body: &str) -> bool { + self.sha512.contains(&sha512_hex(body.as_bytes())) + } +} + +fn is_sha512_hex(value: &str) -> bool { + value.len() == 128 && value.bytes().all(|byte| byte.is_ascii_hexdigit()) +} + +fn sha512_hex(body: &[u8]) -> String { + let mut hasher = Sha512::new(); + hasher.update(body); + format!("{:x}", hasher.finalize()) +} + +fn require_manifest_body( + manifest: &Option, + provider: &str, + body: &str, +) -> mlua::Result<()> { + let Some(manifest) = manifest else { + return Ok(()); + }; + if manifest.allows_body(body) { + return Ok(()); + } + Err(mlua::Error::external(format!( + "{PROVIDER_RESPONSE_NOT_IN_MANIFEST}: {provider} provider response body is not listed in package Manifest" + ))) +} + +fn reject_unproven_manifest_cache( + manifest: &Option, + provider: &str, + source: ProviderCacheSource, +) -> mlua::Result<()> { + if manifest.is_none() || source == ProviderCacheSource::Refreshed { + return Ok(()); + } + Err(mlua::Error::external(format!( + "{PROVIDER_CACHE_PROVENANCE_MISSING}: {provider} provider cache lacks Manifest-compatible source response provenance" + ))) +} + fn provider_cache_mode( mode: Option<&str>, ) -> Result { @@ -422,9 +963,7 @@ fn required_lua_string(table: &Table, field: &'static str) -> mlua::Result mlua::Result Ok(None), LuaValue::Boolean(value) => Ok(Some(value)), _ => Err(mlua::Error::external(format!( - "getter_dev.github_release_candidates {field} must be a boolean" + "GitHub release provider host {field} must be a boolean" ))), } } @@ -446,7 +985,7 @@ fn asset_filter_from_lua(value: LuaValue) -> mlua::Result { exclude: optional_lua_string(&table, "exclude")?, }), _ => Err(mlua::Error::external( - "getter_dev.github_release_candidates asset must be a table", + "GitHub release provider host asset must be a table", )), } } @@ -456,7 +995,7 @@ fn optional_lua_string(table: &Table, field: &'static str) -> mlua::Result Ok(None), LuaValue::String(value) => Ok(Some(value.to_str()?.to_owned())), _ => Err(mlua::Error::external(format!( - "getter_dev.github_release_candidates asset.{field} must be a string" + "GitHub release provider host asset.{field} must be a string" ))), } } @@ -472,54 +1011,6 @@ fn github_eligible_release_count( .count() } -fn update_candidates_to_lua(lua: &Lua, candidates: &[UpdateCandidate]) -> mlua::Result { - if candidates.is_empty() { - return Ok(LuaValue::Nil); - } - - let table = lua.create_table()?; - for (index, candidate) in candidates.iter().enumerate() { - table.raw_set(index + 1, update_candidate_to_lua(lua, candidate)?)?; - } - Ok(LuaValue::Table(table)) -} - -fn update_candidate_to_lua(lua: &Lua, candidate: &UpdateCandidate) -> mlua::Result
{ - let table = lua.create_table()?; - table.set("version", candidate.version.as_str())?; - if let Some(version_code) = candidate.version_code { - table.set("version_code", version_code)?; - } - if let Some(channel) = candidate.channel.as_ref() { - table.set("channel", channel.as_str())?; - } - if let Some(source) = candidate.source.as_ref() { - table.set("source", source.as_str())?; - } - let artifacts = lua.create_table()?; - for (index, artifact) in candidate.artifacts.iter().enumerate() { - artifacts.raw_set(index + 1, update_artifact_to_lua(lua, artifact)?)?; - } - table.set("artifacts", artifacts)?; - Ok(table) -} - -fn update_artifact_to_lua(lua: &Lua, artifact: &UpdateArtifact) -> mlua::Result
{ - let table = lua.create_table()?; - table.set("name", artifact.name.as_str())?; - table.set("url", artifact.url.as_str())?; - if let Some(file_name) = artifact.file_name.as_ref() { - table.set("file_name", file_name.as_str())?; - } - if let Some(sha256) = artifact.sha256.as_ref() { - table.set("sha256", sha256.as_str())?; - } - if let Some(size) = artifact.size { - table.set("size", size)?; - } - Ok(table) -} - fn provider_source_json(source: ProviderCacheSource) -> &'static str { match source { ProviderCacheSource::Cache => "cache", @@ -749,6 +1240,341 @@ mod tests { ); } + #[test] + fn stable_provider_host_exists_only_in_provider_backed_operation() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local provider_type = type(getter and getter.provider) +local builtin_provider_type = type(getter_builtin and getter_builtin.provider) +return package_version { + name = provider_type .. ":" .. builtin_provider_type, + updates = { + { + version = "1.0.0", + artifacts = { + { name = "placeholder", url = "https://example.invalid/placeholder.apk" }, + }, + }, + }, +} +"#, + ) + .unwrap(); + register_official_repository(data_dir, &repo_root); + let layout = RepositoryPackageDirectoryLayout::load(&repo_root).unwrap(); + let package = layout + .package(&"android/app/org.fdroid.fdroid".parse().unwrap()) + .unwrap(); + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + let plain = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("official").unwrap(), + package, + &metadata, + script, + |_| Ok(()), + ) + .unwrap(); + + assert_eq!(plain.name, "nil:nil"); + + let stable = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(stable["package"]["name"], "table:table"); + } + + #[test] + fn stable_provider_host_evaluates_fdroid_release_envelope() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["operation"], "provider.package_eval.fixture"); + assert_eq!(result["provider_calls"][0]["provider"], FDROID_PROVIDER_ID); + assert_eq!(result["provider_calls"][0]["request"], "catalog"); + assert_eq!(result["provider_calls"][0]["source"], "refreshed"); + assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); + assert_eq!(result["package"]["updates"][0]["version_code"], 1020000); + assert_eq!( + result["package"]["updates"][0]["artifacts"][0]["sha256"], + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ); + assert!(result["package"]["updates"].is_array()); + assert_eq!(result["runtime_hooks"], json!([])); + assert!(CacheDb::open(data_dir.join("cache.db")) + .unwrap() + .provider_response(result["provider_calls"][0]["cache_key"].as_str().unwrap()) + .unwrap() + .is_some()); + } + + #[test] + fn stable_provider_host_evaluates_github_release_envelope() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/app/org.fdroid.fdroid", + stable_github_script("[.]apk$"), + Some(GITHUB_RELEASES_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "github_releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["provider"], GITHUB_PROVIDER_ID); + assert_eq!(result["provider_calls"][0]["request"], "releases"); + assert_eq!(result["package"]["updates"][0]["version"], "v1.20.0"); + assert_eq!(result["package"]["updates"][0]["source"], "github"); + assert_eq!( + result["package"]["updates"][0]["artifacts"][0]["file_name"], + "F-Droid.apk" + ); + } + + #[test] + fn stable_provider_host_omits_no_candidate_updates() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("missing.package"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["package"]["updates"], json!([])); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["code"], + FDROID_PACKAGE_NOT_FOUND + ); + } + + #[test] + fn stable_provider_host_does_not_install_latest_commit_shape() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local latest_commit = getter.provider.github.latest_commit +return package_version { + name = type(latest_commit), + updates = { + { + version = "1.0.0", + artifacts = { + { name = "placeholder", url = "https://example.invalid/placeholder.apk" }, + }, + }, + }, +} +"#, + None, + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["package"]["name"], "nil"); + } + + #[test] + fn stable_provider_hook_wraps_provider_function_and_calls_builtin_original() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + write_hook( + data_dir, + "10-provider-tag.lua", + r#" +local original = getter_builtin.provider.fdroid.update_candidates +function getter.provider.fdroid.update_candidates(spec) + local result = original(spec) + result.source = "hooked-" .. result.source + return result +end +"#, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["source"], "hooked-refreshed"); + assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); + assert_eq!(result["runtime_hooks"].as_array().unwrap().len(), 1); + } + + #[test] + fn stable_provider_host_requires_manifest_listed_fixture_body() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + None, + false, + ); + + let err = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap_err(); + + assert!(err.to_string().contains(PROVIDER_RESPONSE_NOT_IN_MANIFEST)); + } + + #[test] + fn stable_provider_host_rejects_non_free_cache_hit_without_provenance() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + let err = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap_err(); + + assert!(err.to_string().contains(PROVIDER_CACHE_PROVENANCE_MISSING)); + } + + #[test] + fn stable_provider_host_manifest_check_does_not_apply_to_free_network_script() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + None, + true, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); + } + #[test] fn provider_package_eval_prefers_repository_luaclass_over_dev_builtin() { let temp = tempfile::tempdir().unwrap(); @@ -858,6 +1684,78 @@ return github_android.package { register_official_repository(data_dir, &repo_root); } + fn write_stable_provider_package_fixture( + data_dir: &std::path::Path, + package_id: &str, + version_script: impl AsRef, + manifest_body: Option<&str>, + allow_free_network: bool, + ) { + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join(package_id); + fs::create_dir_all(&package_dir).unwrap(); + let mut metadata = json!({ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } + }); + if allow_free_network { + metadata["lua"] = json!({ + "9999.lua": { "permission": ["allow_free_network"] } + }); + } + fs::write( + package_dir.join("metadata.jsonc"), + serde_json::to_string_pretty(&metadata).unwrap(), + ) + .unwrap(); + if let Some(manifest_body) = manifest_body { + fs::write( + package_dir.join(PACKAGE_MANIFEST_FILE), + format!("{} fixture-body\n", sha512_hex(manifest_body.as_bytes())), + ) + .unwrap(); + } + fs::write(package_dir.join("9999.lua"), version_script.as_ref()).unwrap(); + register_official_repository(data_dir, &repo_root); + } + + fn stable_fdroid_script(package_name: &str) -> String { + format!( + r#"#!/bin/upa-lua v1 +local result = getter.provider.fdroid.update_candidates {{ + package_name = "{package_name}", +}} +return package_version {{ + source_priority = {{ "fdroid" }}, + updates = result.candidates, +}} +"# + ) + } + + fn stable_github_script(asset_include: &str) -> String { + format!( + r#"#!/bin/upa-lua v1 +local result = getter.provider.github.release_candidates {{ + owner = "f-droid", + repo = "fdroidclient", + asset = {{ include = "{asset_include}" }}, +}} +return package_version {{ + name = "F-Droid", + source_priority = {{ "github" }}, + updates = result.candidates, +}} +"# + ) + } + + fn write_hook(data_dir: &std::path::Path, file_name: &str, source: &str) { + let hook_dir = data_dir.join("rc/hook"); + fs::create_dir_all(&hook_dir).unwrap(); + fs::write(hook_dir.join(file_name), source).unwrap(); + } + fn register_official_repository(data_dir: &std::path::Path, repo_root: &std::path::Path) { let main_db = MainDb::open(data_dir.join("main.db")).unwrap(); main_db From 6a83a2baa31efa96d7e7fd9d252b36ef29a9cf0f Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 12:04:07 +0800 Subject: [PATCH 54/72] feat(cache): store provider response provenance --- .../getter-operations/src/fdroid_catalog.rs | 23 ++- .../src/github_latest_commit.rs | 23 ++- .../getter-operations/src/github_releases.rs | 26 ++- .../src/lua_provider_host.rs | 142 ++++++++++++++-- .../getter-operations/src/provider_cache.rs | 82 +++++++-- crates/getter-storage/src/lib.rs | 156 +++++++++++++++++- 6 files changed, 403 insertions(+), 49 deletions(-) diff --git a/crates/getter-operations/src/fdroid_catalog.rs b/crates/getter-operations/src/fdroid_catalog.rs index 3a301f5..d65f29d 100644 --- a/crates/getter-operations/src/fdroid_catalog.rs +++ b/crates/getter-operations/src/fdroid_catalog.rs @@ -6,8 +6,9 @@ //! task state. use crate::provider_cache::{ - read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, - ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, + read_or_refresh_provider_response_with_provenance, source_response_sha512, + ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheOperationError, ProviderCacheRequest, + ProviderCacheSource, ProviderResponseRefresh, }; use getter_providers::{parse_fdroid_index_xml, FdroidApp, FdroidCatalog, FdroidCatalogError}; use getter_storage::{CacheDb, StorageError}; @@ -51,6 +52,8 @@ pub struct FdroidCatalogResult { pub cache_key: String, pub catalog: FdroidCatalog, pub source: ProviderCacheSource, + pub source_response_sha512: Vec, + pub provenance_schema_version: Option, pub diagnostics: Vec, } @@ -84,7 +87,7 @@ where F: FnOnce() -> Result, { let cache_key = endpoint.cache_key(); - let cache_result = read_or_refresh_provider_response( + let cache_result = read_or_refresh_provider_response_with_provenance( db, ProviderCacheRequest { cache_key: &cache_key, @@ -93,17 +96,27 @@ where }, || { let xml = refresh_xml()?; + let source_digest = source_response_sha512(xml.as_bytes()); let catalog = parse_fdroid_index_xml(&xml).map_err(|source| source.to_string())?; - serde_json::to_value(catalog).map_err(|source| source.to_string()) + let response_json = + serde_json::to_value(catalog).map_err(|source| source.to_string())?; + Ok(ProviderResponseRefresh { + response_json, + source_response_sha512: vec![source_digest], + freshness_json: json!({}), + }) }, )?; - let catalog = serde_json::from_value(cache_result.response.response_json)?; + let response = cache_result.response; + let catalog = serde_json::from_value(response.response_json)?; Ok(FdroidCatalogResult { endpoint, cache_key, catalog, source: cache_result.source, + source_response_sha512: response.source_response_sha512, + provenance_schema_version: response.provenance_schema_version, diagnostics: cache_result.diagnostics, }) } diff --git a/crates/getter-operations/src/github_latest_commit.rs b/crates/getter-operations/src/github_latest_commit.rs index 15e8b7e..114c7e1 100644 --- a/crates/getter-operations/src/github_latest_commit.rs +++ b/crates/getter-operations/src/github_latest_commit.rs @@ -6,8 +6,9 @@ //! actions, downloads, installer handoffs, or Flutter/Kotlin provider parsing. use crate::provider_cache::{ - read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, - ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, + read_or_refresh_provider_response_with_provenance, source_response_sha512, + ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheOperationError, ProviderCacheRequest, + ProviderCacheSource, ProviderResponseRefresh, }; use getter_providers::{ github_latest_commit_live_revision, parse_github_commit_json, GithubCommit, GithubLiveRevision, @@ -50,6 +51,8 @@ pub struct GithubLatestCommitResult { pub commit: GithubCommit, pub live_revision: GithubLiveRevision, pub source: ProviderCacheSource, + pub source_response_sha512: Vec, + pub provenance_schema_version: Option, pub diagnostics: Vec, } @@ -77,7 +80,7 @@ where F: FnOnce() -> Result, { let cache_key = config.cache_key(); - let cache_result = read_or_refresh_provider_response( + let cache_result = read_or_refresh_provider_response_with_provenance( db, ProviderCacheRequest { cache_key: &cache_key, @@ -86,12 +89,20 @@ where }, || { let json = refresh_json()?; + let source_digest = source_response_sha512(json.as_bytes()); let commit = parse_github_commit_json(&json).map_err(|source| source.to_string())?; github_latest_commit_live_revision(&commit).map_err(|source| source.to_string())?; - serde_json::to_value(commit).map_err(|source| source.to_string()) + let response_json = + serde_json::to_value(commit).map_err(|source| source.to_string())?; + Ok(ProviderResponseRefresh { + response_json, + source_response_sha512: vec![source_digest], + freshness_json: serde_json::json!({}), + }) }, )?; - let commit = serde_json::from_value(cache_result.response.response_json)?; + let response = cache_result.response; + let commit = serde_json::from_value(response.response_json)?; let live_revision = github_latest_commit_live_revision(&commit)?; Ok(GithubLatestCommitResult { @@ -100,6 +111,8 @@ where commit, live_revision, source: cache_result.source, + source_response_sha512: response.source_response_sha512, + provenance_schema_version: response.provenance_schema_version, diagnostics: cache_result.diagnostics, }) } diff --git a/crates/getter-operations/src/github_releases.rs b/crates/getter-operations/src/github_releases.rs index 9f151c2..cdac38b 100644 --- a/crates/getter-operations/src/github_releases.rs +++ b/crates/getter-operations/src/github_releases.rs @@ -6,8 +6,9 @@ //! live HTTP, Flutter/Kotlin provider parsing, downloader, or installer logic. use crate::provider_cache::{ - read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, - ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, + read_or_refresh_provider_response_with_provenance, source_response_sha512, + ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheOperationError, ProviderCacheRequest, + ProviderCacheSource, ProviderResponseRefresh, }; use getter_providers::{ github_release_update_candidates, parse_github_releases_json, GithubAssetFilter, @@ -47,6 +48,8 @@ pub struct GithubReleaseResult { pub cache_key: String, pub releases: Vec, pub source: ProviderCacheSource, + pub source_response_sha512: Vec, + pub provenance_schema_version: Option, pub diagnostics: Vec, } @@ -74,7 +77,7 @@ where F: FnOnce() -> Result, { let cache_key = config.cache_key(); - let cache_result = read_or_refresh_provider_response( + let cache_result = read_or_refresh_provider_response_with_provenance( db, ProviderCacheRequest { cache_key: &cache_key, @@ -83,18 +86,28 @@ where }, || { let json = refresh_json()?; + let source_digest = source_response_sha512(json.as_bytes()); let releases = parse_github_releases_json(&json).map_err(|source| source.to_string())?; - serde_json::to_value(releases).map_err(|source| source.to_string()) + let response_json = + serde_json::to_value(releases).map_err(|source| source.to_string())?; + Ok(ProviderResponseRefresh { + response_json, + source_response_sha512: vec![source_digest], + freshness_json: serde_json::json!({}), + }) }, )?; - let releases = serde_json::from_value(cache_result.response.response_json)?; + let response = cache_result.response; + let releases = serde_json::from_value(response.response_json)?; Ok(GithubReleaseResult { config, cache_key, releases, source: cache_result.source, + source_response_sha512: response.source_response_sha512, + provenance_schema_version: response.provenance_schema_version, diagnostics: cache_result.diagnostics, }) } @@ -361,6 +374,9 @@ mod tests { cache_key, provider: GITHUB_PROVIDER_ID.to_owned(), response_json: serde_json::from_str(GITHUB_RELEASES_FIXTURE).unwrap(), + source_response_sha512: Vec::new(), + provenance_schema_version: None, + freshness_json: serde_json::json!({}), }) .unwrap(); diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 15cb3b1..24101c7 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -15,7 +15,10 @@ use crate::github_releases::{ DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, }; use crate::lua_runtime_hooks::load_runtime_hooks; -use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource}; +use crate::provider_cache::{ + ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource, + PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1, +}; use getter_core::lua::{evaluate_package_directory_script_with_host_bindings, LuaPackageError}; use getter_core::repository::{ PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageVersionScript, @@ -284,14 +287,6 @@ pub fn stable_provider_package_eval_json( package_directory.path.join(PACKAGE_MANIFEST_FILE), )?) }; - // Parsed provider cache currently has no source-body digest provenance. - // Non-free scripts therefore refetch/revalidate fixture bodies instead of - // accepting cache hits that cannot prove Manifest compatibility. - let provider_mode = if manifest.is_some() { - ProviderCacheMode::ForceRefresh - } else { - mode - }; let package = evaluate_with_stable_provider_host( data_dir, &repository.id, @@ -302,14 +297,14 @@ pub fn stable_provider_package_eval_json( fdroid: FdroidProviderHostConfig { cache_db_path: data_dir.join("cache.db"), endpoint, - mode: provider_mode, + mode, index_xml: fdroid_index_xml, manifest: manifest.clone(), }, github: GithubProviderHostConfig { cache_db_path: data_dir.join("cache.db"), api_base_url: github_api_base_url, - mode: provider_mode, + mode, releases_json: github_releases_json, default_include_prereleases, manifest, @@ -594,7 +589,13 @@ fn fdroid_update_candidates_envelope( Ok(xml) }) .map_err(mlua::Error::external)?; - reject_unproven_manifest_cache(&manifest, FDROID_PROVIDER_ID, result.source)?; + reject_unproven_manifest_cache( + &manifest, + FDROID_PROVIDER_ID, + result.source, + &result.source_response_sha512, + result.provenance_schema_version.as_deref(), + )?; let mut diagnostics = result .diagnostics .iter() @@ -661,7 +662,13 @@ fn github_release_candidates_envelope( Ok(releases) }) .map_err(mlua::Error::external)?; - reject_unproven_manifest_cache(&manifest, GITHUB_PROVIDER_ID, result.source)?; + reject_unproven_manifest_cache( + &manifest, + GITHUB_PROVIDER_ID, + result.source, + &result.source_response_sha512, + result.provenance_schema_version.as_deref(), + )?; let options = GithubReleaseCandidateOptions { include_prereleases: request .include_prereleases @@ -880,7 +887,11 @@ impl PackageManifest { } fn allows_body(&self, body: &str) -> bool { - self.sha512.contains(&sha512_hex(body.as_bytes())) + self.allows_sha512(&sha512_hex(body.as_bytes())) + } + + fn allows_sha512(&self, digest: &str) -> bool { + is_sha512_hex(digest) && self.sha512.contains(&digest.to_ascii_lowercase()) } } @@ -914,12 +925,30 @@ fn reject_unproven_manifest_cache( manifest: &Option, provider: &str, source: ProviderCacheSource, + source_response_sha512: &[String], + provenance_schema_version: Option<&str>, ) -> mlua::Result<()> { - if manifest.is_none() || source == ProviderCacheSource::Refreshed { + let Some(manifest) = manifest else { + return Ok(()); + }; + if source == ProviderCacheSource::Refreshed { + return Ok(()); + } + if provenance_schema_version != Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1) + || source_response_sha512.is_empty() + { + return Err(mlua::Error::external(format!( + "{PROVIDER_CACHE_PROVENANCE_MISSING}: {provider} provider cache lacks Manifest-compatible source response provenance" + ))); + } + if source_response_sha512 + .iter() + .all(|digest| manifest.allows_sha512(digest)) + { return Ok(()); } Err(mlua::Error::external(format!( - "{PROVIDER_CACHE_PROVENANCE_MISSING}: {provider} provider cache lacks Manifest-compatible source response provenance" + "{PROVIDER_RESPONSE_NOT_IN_MANIFEST}: {provider} provider cache source response digest is not listed in package Manifest" ))) } @@ -1034,7 +1063,7 @@ mod tests { use super::*; use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; use getter_core::{RepositoryId, RepositoryPriority}; - use getter_storage::CacheDb; + use getter_storage::{CacheDb, ProviderResponseUpsert}; use serde_json::json; use std::fs; @@ -1515,7 +1544,7 @@ end } #[test] - fn stable_provider_host_rejects_non_free_cache_hit_without_provenance() { + fn stable_provider_host_uses_manifest_compatible_cache_provenance() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_stable_provider_package_fixture( @@ -1536,6 +1565,33 @@ end ) .unwrap(); + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["source"], "cache"); + assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); + } + + #[test] + fn stable_provider_host_rejects_non_free_cache_hit_without_provenance() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + write_fdroid_cache_response(data_dir, Vec::new(), None); + let err = stable_provider_package_eval_json( data_dir, &json!({ @@ -1549,6 +1605,36 @@ end assert!(err.to_string().contains(PROVIDER_CACHE_PROVENANCE_MISSING)); } + #[test] + fn stable_provider_host_rejects_cache_provenance_digest_not_in_manifest() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + write_fdroid_cache_response( + data_dir, + vec![sha512_hex(b"different provider body")], + Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1.to_owned()), + ); + + let err = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap_err(); + + assert!(err.to_string().contains(PROVIDER_RESPONSE_NOT_IN_MANIFEST)); + } + #[test] fn stable_provider_host_manifest_check_does_not_apply_to_free_network_script() { let temp = tempfile::tempdir().unwrap(); @@ -1750,6 +1836,26 @@ return package_version {{ ) } + fn write_fdroid_cache_response( + data_dir: &std::path::Path, + source_response_sha512: Vec, + provenance_schema_version: Option, + ) { + let endpoint = FdroidEndpointConfig::default(); + let catalog = getter_providers::parse_fdroid_index_xml(FDROID_INDEX_FIXTURE).unwrap(); + CacheDb::open(data_dir.join("cache.db")) + .unwrap() + .upsert_provider_response(&ProviderResponseUpsert { + cache_key: endpoint.cache_key(), + provider: FDROID_PROVIDER_ID.to_owned(), + response_json: serde_json::to_value(catalog).unwrap(), + source_response_sha512, + provenance_schema_version, + freshness_json: json!({}), + }) + .unwrap(); + } + fn write_hook(data_dir: &std::path::Path, file_name: &str, source: &str) { let hook_dir = data_dir.join("rc/hook"); fs::create_dir_all(&hook_dir).unwrap(); diff --git a/crates/getter-operations/src/provider_cache.rs b/crates/getter-operations/src/provider_cache.rs index 771cd81..89179e3 100644 --- a/crates/getter-operations/src/provider_cache.rs +++ b/crates/getter-operations/src/provider_cache.rs @@ -6,10 +6,12 @@ //! diagnostics. use getter_storage::{CacheDb, ProviderResponseUpsert, StorageError, StoredProviderResponse}; -use serde_json::Value; +use serde_json::{json, Value}; +use sha2::{Digest, Sha512}; pub const CACHE_REFRESH_FAILED: &str = "cache.refresh_failed"; pub const USED_STALE_CACHE: &str = "used_stale_cache"; +pub const PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1: &str = "provider-response-provenance-v1"; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ProviderCacheMode { @@ -31,6 +33,13 @@ pub struct ProviderCacheRequest<'a> { pub mode: ProviderCacheMode, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProviderResponseRefresh { + pub response_json: Value, + pub source_response_sha512: Vec, + pub freshness_json: Value, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProviderCacheDiagnostic { pub code: String, @@ -62,6 +71,23 @@ pub fn read_or_refresh_provider_response( ) -> Result where F: FnOnce() -> Result, +{ + read_or_refresh_provider_response_with_provenance(db, request, || { + refresh().map(|response_json| ProviderResponseRefresh { + response_json, + source_response_sha512: Vec::new(), + freshness_json: json!({}), + }) + }) +} + +pub fn read_or_refresh_provider_response_with_provenance( + db: &CacheDb, + request: ProviderCacheRequest<'_>, + refresh: F, +) -> Result +where + F: FnOnce() -> Result, { let cached = db.provider_response(request.cache_key)?; if request.mode == ProviderCacheMode::UseCached { @@ -75,11 +101,14 @@ where } match refresh() { - Ok(response_json) => { + Ok(refresh) => { let response = db.upsert_provider_response(&ProviderResponseUpsert { cache_key: request.cache_key.to_owned(), provider: request.provider.to_owned(), - response_json, + response_json: refresh.response_json, + source_response_sha512: refresh.source_response_sha512, + provenance_schema_version: Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1.to_owned()), + freshness_json: refresh.freshness_json, })?; Ok(ProviderCacheResult { response, @@ -116,9 +145,16 @@ where } } +pub fn source_response_sha512(body: impl AsRef<[u8]>) -> String { + let mut hasher = Sha512::new(); + hasher.update(body.as_ref()); + format!("{:x}", hasher.finalize()) +} + #[cfg(test)] mod tests { use super::*; + use getter_storage::ProviderResponseUpsert; use serde_json::json; use std::cell::Cell; @@ -129,6 +165,9 @@ mod tests { cache_key: "fdroid:official:index".to_owned(), provider: "fdroid".to_owned(), response_json: json!({ "revision": "cached" }), + source_response_sha512: Vec::new(), + provenance_schema_version: None, + freshness_json: json!({}), }) .unwrap(); let refreshed = Cell::new(false); @@ -157,25 +196,42 @@ mod tests { fn cache_miss_refreshes_and_stores_provider_response() { let db = CacheDb::open_in_memory().unwrap(); - let result = read_or_refresh_provider_response( + let result = read_or_refresh_provider_response_with_provenance( &db, ProviderCacheRequest { cache_key: "github:f-droid/fdroidclient:releases", provider: "github", mode: ProviderCacheMode::UseCached, }, - || Ok(json!({ "etag": "fresh" })), + || { + Ok(ProviderResponseRefresh { + response_json: json!({ "etag": "fresh" }), + source_response_sha512: vec![source_response_sha512("fresh body")], + freshness_json: json!({ "etag": "fresh" }), + }) + }, ) .unwrap(); assert_eq!(result.source, ProviderCacheSource::Refreshed); assert_eq!(result.response.response_json["etag"], "fresh"); assert_eq!( - db.provider_response("github:f-droid/fdroidclient:releases") - .unwrap() - .unwrap() - .response_json["etag"], - "fresh" + result.response.provenance_schema_version.as_deref(), + Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1) + ); + assert_eq!( + result.response.source_response_sha512, + vec![source_response_sha512("fresh body")] + ); + assert_eq!(result.response.freshness_json["etag"], "fresh"); + let cached = db + .provider_response("github:f-droid/fdroidclient:releases") + .unwrap() + .unwrap(); + assert_eq!(cached.response_json["etag"], "fresh"); + assert_eq!( + cached.source_response_sha512, + result.response.source_response_sha512 ); } @@ -186,6 +242,9 @@ mod tests { cache_key: "fdroid:official:index".to_owned(), provider: "fdroid".to_owned(), response_json: json!({ "revision": "old" }), + source_response_sha512: Vec::new(), + provenance_schema_version: None, + freshness_json: json!({}), }) .unwrap(); @@ -218,6 +277,9 @@ mod tests { cache_key: "github:f-droid/fdroidclient:releases".to_owned(), provider: "github".to_owned(), response_json: json!({ "etag": "old" }), + source_response_sha512: Vec::new(), + provenance_schema_version: None, + freshness_json: json!({}), }) .unwrap(); diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index 2af994e..e2b2c7e 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -875,32 +875,94 @@ CREATE TABLE IF NOT EXISTS provider_responses ( cache_key TEXT PRIMARY KEY, provider TEXT NOT NULL, response_json TEXT NOT NULL, + source_response_sha512_json TEXT NOT NULL DEFAULT '[]', + provenance_schema_version TEXT, + freshness_json TEXT NOT NULL DEFAULT '{}', fetched_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) ); "#, )?; + self.ensure_column( + "provider_responses", + "source_response_sha512_json", + "TEXT NOT NULL DEFAULT '[]'", + )?; + self.ensure_column("provider_responses", "provenance_schema_version", "TEXT")?; + self.ensure_column( + "provider_responses", + "freshness_json", + "TEXT NOT NULL DEFAULT '{}'", + )?; self.conn.execute( "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('cache-v1')", [], )?; + self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('cache-provider-provenance-v1')", + [], + )?; + Ok(()) + } + + fn ensure_column( + &self, + table: &'static str, + column: &'static str, + definition: &'static str, + ) -> Result<(), StorageError> { + let columns = self.table_columns(table)?; + if !columns.iter().any(|existing| existing == column) { + self.conn.execute_batch(&format!( + "ALTER TABLE {table} ADD COLUMN {column} {definition};" + ))?; + } Ok(()) } + fn table_columns(&self, table: &'static str) -> Result, StorageError> { + let mut stmt = self.conn.prepare(&format!("PRAGMA table_info({table})"))?; + let rows = stmt.query_map([], |row| row.get::<_, String>(1))?; + let mut columns = Vec::new(); + for row in rows { + columns.push(row?); + } + Ok(columns) + } + pub fn upsert_provider_response( &self, response: &ProviderResponseUpsert, ) -> Result { let response_json = serde_json::to_string(&response.response_json)?; + let source_response_sha512_json = serde_json::to_string(&response.source_response_sha512)?; + let freshness_json = serde_json::to_string(&response.freshness_json)?; self.conn.execute( r#" -INSERT INTO provider_responses(cache_key, provider, response_json) -VALUES (?1, ?2, ?3) +INSERT INTO provider_responses( + cache_key, + provider, + response_json, + source_response_sha512_json, + provenance_schema_version, + freshness_json +) +VALUES (?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT(cache_key) DO UPDATE SET provider = excluded.provider, response_json = excluded.response_json, + source_response_sha512_json = excluded.source_response_sha512_json, + provenance_schema_version = excluded.provenance_schema_version, + freshness_json = excluded.freshness_json, fetched_at_unix = unixepoch() "#, - params![response.cache_key, response.provider, response_json], + params![ + response.cache_key, + response.provider, + response_json, + source_response_sha512_json, + response.provenance_schema_version, + freshness_json, + ], )?; self.provider_response(&response.cache_key)?.ok_or_else(|| { StorageError::Invariant(format!( @@ -916,7 +978,14 @@ ON CONFLICT(cache_key) DO UPDATE SET ) -> Result, StorageError> { let mut stmt = self.conn.prepare( r#" -SELECT cache_key, provider, response_json, fetched_at_unix +SELECT + cache_key, + provider, + response_json, + source_response_sha512_json, + provenance_schema_version, + freshness_json, + fetched_at_unix FROM provider_responses WHERE cache_key = ?1 "#, @@ -927,16 +996,31 @@ WHERE cache_key = ?1 row.get::<_, String>(0)?, row.get::<_, String>(1)?, row.get::<_, String>(2)?, - row.get::<_, i64>(3)?, + row.get::<_, String>(3)?, + row.get::<_, Option>(4)?, + row.get::<_, String>(5)?, + row.get::<_, i64>(6)?, )) }) .optional()?; row.map( - |(cache_key, provider, response_json, fetched_at_unix)| -> Result<_, StorageError> { + |( + cache_key, + provider, + response_json, + source_response_sha512_json, + provenance_schema_version, + freshness_json, + fetched_at_unix, + )| + -> Result<_, StorageError> { Ok(StoredProviderResponse { cache_key, provider, response_json: serde_json::from_str(&response_json)?, + source_response_sha512: serde_json::from_str(&source_response_sha512_json)?, + provenance_schema_version, + freshness_json: serde_json::from_str(&freshness_json)?, fetched_at_unix, }) }, @@ -1057,6 +1141,9 @@ pub struct ProviderResponseUpsert { pub cache_key: String, pub provider: String, pub response_json: Value, + pub source_response_sha512: Vec, + pub provenance_schema_version: Option, + pub freshness_json: Value, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -1064,6 +1151,9 @@ pub struct StoredProviderResponse { pub cache_key: String, pub provider: String, pub response_json: Value, + pub source_response_sha512: Vec, + pub provenance_schema_version: Option, + pub freshness_json: Value, pub fetched_at_unix: i64, } @@ -1691,6 +1781,39 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); let _db = CacheDb::open_in_memory().unwrap(); } + #[test] + fn cache_db_migrates_legacy_provider_response_rows_without_provenance() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("cache.db"); + { + let conn = rusqlite::Connection::open(&db_path).unwrap(); + conn.execute_batch( + r#" +CREATE TABLE provider_responses ( + cache_key TEXT PRIMARY KEY, + provider TEXT NOT NULL, + response_json TEXT NOT NULL, + fetched_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); +INSERT INTO provider_responses(cache_key, provider, response_json) +VALUES ('fdroid:legacy:index', 'fdroid', '{"revision":"legacy"}'); +"#, + ) + .unwrap(); + } + + let db = CacheDb::open(&db_path).unwrap(); + let stored = db + .provider_response("fdroid:legacy:index") + .unwrap() + .unwrap(); + + assert_eq!(stored.response_json["revision"], "legacy"); + assert_eq!(stored.source_response_sha512, Vec::::new()); + assert_eq!(stored.provenance_schema_version, None); + assert_eq!(stored.freshness_json, serde_json::json!({})); + } + #[test] fn cache_db_round_trips_provider_response_cache_entries() { let db = CacheDb::open_in_memory().unwrap(); @@ -1703,12 +1826,21 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); "endpoint": "official", "packages": ["org.fdroid.fdroid"] }), + source_response_sha512: vec!["a".repeat(128)], + provenance_schema_version: Some("provider-response-provenance-v1".to_owned()), + freshness_json: serde_json::json!({ "etag": "fixture-etag" }), }) .unwrap(); assert_eq!(stored.cache_key, "fdroid:official:index:v1"); assert_eq!(stored.provider, "fdroid"); assert_eq!(stored.response_json["endpoint"], "official"); + assert_eq!(stored.source_response_sha512, vec!["a".repeat(128)]); + assert_eq!( + stored.provenance_schema_version.as_deref(), + Some("provider-response-provenance-v1") + ); + assert_eq!(stored.freshness_json["etag"], "fixture-etag"); assert_eq!( db.provider_response("fdroid:official:index:v1") .unwrap() @@ -1724,6 +1856,9 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); cache_key: "github:f-droid/fdroidclient:releases".to_owned(), provider: "github".to_owned(), response_json: serde_json::json!({ "etag": "old" }), + source_response_sha512: Vec::new(), + provenance_schema_version: None, + freshness_json: serde_json::json!({}), }) .unwrap(); @@ -1732,10 +1867,19 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); cache_key: "github:f-droid/fdroidclient:releases".to_owned(), provider: "github".to_owned(), response_json: serde_json::json!({ "etag": "new" }), + source_response_sha512: vec!["b".repeat(128)], + provenance_schema_version: Some("provider-response-provenance-v1".to_owned()), + freshness_json: serde_json::json!({ "etag": "new" }), }) .unwrap(); assert_eq!(stored.response_json["etag"], "new"); + assert_eq!(stored.source_response_sha512, vec!["b".repeat(128)]); + assert_eq!( + stored.provenance_schema_version.as_deref(), + Some("provider-response-provenance-v1") + ); + assert_eq!(stored.freshness_json["etag"], "new"); assert_eq!( db.provider_response("github:f-droid/fdroidclient:releases") .unwrap() From 20f13bfcb30b9f9cfccb57a88220d9d8dadd0cdb Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 14:07:12 +0800 Subject: [PATCH 55/72] feat(lua): promote standard provider luaclasses --- crates/getter-core/Cargo.toml | 1 - crates/getter-core/src/autogen.rs | 4 +- crates/getter-core/src/lua.rs | 53 ++--- .../src/luaclass/fdroid_android.lua | 34 +++- .../src/luaclass/github_android_apk.lua | 47 +++-- crates/getter-core/src/runtime.rs | 14 +- crates/getter-operations/Cargo.toml | 2 +- .../src/lua_provider_host.rs | 189 ++++++++++++++---- 8 files changed, 244 insertions(+), 100 deletions(-) diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 5cb34ef..4b1f9c5 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true [features] default = ["lua"] lua = ["dep:mlua"] -provider-luaclass-dev = ["lua"] [dependencies] json_comments = "0.2" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index 6a97c50..c8b7eae 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -457,10 +457,10 @@ mod tests { assert!(candidate .files .iter() - .any(|file| file.relative_path == PathBuf::from("metadata.jsonc") + .any(|file| file.relative_path == Path::new("metadata.jsonc") && file.content.contains("org.fdroid.fdroid"))); assert!(candidate.files.iter().any(|file| { - file.relative_path == PathBuf::from("9999.lua") + file.relative_path == Path::new("9999.lua") && file.content.starts_with("#!/bin/upa-lua v1\n") && file.content.contains(GENERATED_MARKER) })); diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index f00f014..3bdf60b 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -16,12 +16,10 @@ use std::path::{Path, PathBuf}; const BUILTIN_LUACLASS_MODULES: &[(&str, &str)] = &[ ("android", include_str!("luaclass/android.lua")), - #[cfg(feature = "provider-luaclass-dev")] ( "fdroid_android", include_str!("luaclass/fdroid_android.lua"), ), - #[cfg(feature = "provider-luaclass-dev")] ( "github_android_apk", include_str!("luaclass/github_android_apk.lua"), @@ -1250,9 +1248,8 @@ return android.package_version { assert_eq!(package.installed.len(), 1); } - #[cfg(not(feature = "provider-luaclass-dev"))] #[test] - fn provider_luaclass_dev_modules_are_not_default_builtins() { + fn provider_luaclass_builtin_requires_operation_installed_provider_host() { let temp = tempfile::tempdir().unwrap(); let package_dir = temp.path().join("android/f-droid/app/org.fdroid.fdroid"); fs::create_dir_all(&package_dir).unwrap(); @@ -1277,12 +1274,13 @@ return fdroid.package { package_name = "org.fdroid.fdroid" } let message = err.to_string(); assert!(matches!(err, LuaPackageError::Runtime { .. })); - assert!(message.contains("no builtin luaclass module 'luaclass.fdroid_android'")); + assert!(message.contains( + "luaclass.fdroid_android requires operation-installed getter.provider.fdroid.update_candidates" + )); } - #[cfg(feature = "provider-luaclass-dev")] #[test] - fn provider_luaclass_dev_builtin_can_call_injected_host() { + fn provider_luaclass_builtin_can_call_stable_injected_host() { let temp = tempfile::tempdir().unwrap(); let package_dir = temp.path().join("android/f-droid/app/org.fdroid.fdroid"); fs::create_dir_all(&package_dir).unwrap(); @@ -1313,29 +1311,36 @@ return fdroid.package { package_name = "org.fdroid.fdroid" } &metadata, script, |lua| { - let getter_dev = lua.create_table()?; - getter_dev.set( - "fdroid_update_candidates", - lua.create_function(|lua, spec: Table| { + let candidate = lua.create_table()?; + candidate.set("version", "1.20.0")?; + candidate.set("source", "fdroid")?; + let artifact = lua.create_table()?; + artifact.set("name", "org.fdroid.fdroid.apk")?; + artifact.set("url", "https://f-droid.org/repo/org.fdroid.fdroid.apk")?; + let artifacts = lua.create_table()?; + artifacts.raw_set(1, artifact)?; + candidate.set("artifacts", artifacts)?; + let candidates = lua.create_table()?; + candidates.raw_set(1, candidate)?; + + let fdroid = lua.create_table()?; + fdroid.set( + "update_candidates", + lua.create_function(move |lua, spec: Table| { let package_name: String = spec.get("package_name")?; if package_name != "org.fdroid.fdroid" { return Err(mlua::Error::external("F-Droid package mismatch")); } - let artifact = lua.create_table()?; - artifact.set("name", "org.fdroid.fdroid.apk")?; - artifact.set("url", "https://f-droid.org/repo/org.fdroid.fdroid.apk")?; - let artifacts = lua.create_table()?; - artifacts.raw_set(1, artifact)?; - let candidate = lua.create_table()?; - candidate.set("version", "1.20.0")?; - candidate.set("source", "fdroid")?; - candidate.set("artifacts", artifacts)?; - let candidates = lua.create_table()?; - candidates.raw_set(1, candidate)?; - Ok(candidates) + let envelope = lua.create_table()?; + envelope.set("candidates", candidates.clone())?; + Ok(envelope) })?, )?; - lua.globals().set("getter_dev", getter_dev) + let provider = lua.create_table()?; + provider.set("fdroid", fdroid)?; + let getter = lua.create_table()?; + getter.set("provider", provider)?; + lua.globals().set("getter", getter) }, ) .unwrap(); diff --git a/crates/getter-core/src/luaclass/fdroid_android.lua b/crates/getter-core/src/luaclass/fdroid_android.lua index 7a1237b..7029ffe 100644 --- a/crates/getter-core/src/luaclass/fdroid_android.lua +++ b/crates/getter-core/src/luaclass/fdroid_android.lua @@ -8,24 +8,42 @@ local function require_string(table, field) return value end +local function optional_string(table, field) + local value = table[field] + if value == nil then + return nil + end + if type(value) ~= "string" then + error("fdroid.package field '" .. field .. "' must be a string") + end + if value == "" then + return nil + end + return value +end + local function fdroid_update_candidates() - if type(getter_dev) ~= "table" or type(getter_dev.fdroid_update_candidates) ~= "function" then - error("luaclass.fdroid_android requires operation-installed getter_dev.fdroid_update_candidates") + if type(getter) ~= "table" + or type(getter.provider) ~= "table" + or type(getter.provider.fdroid) ~= "table" + or type(getter.provider.fdroid.update_candidates) ~= "function" then + error("luaclass.fdroid_android requires operation-installed getter.provider.fdroid.update_candidates") end - return getter_dev.fdroid_update_candidates + return getter.provider.fdroid.update_candidates end function fdroid.package(spec) if type(spec) ~= "table" then error("fdroid.package expects a table") end - local package_name = require_string(spec, "package_name") - local update_candidates = fdroid_update_candidates() + local host_spec = { + package_name = require_string(spec, "package_name"), + endpoint_id = optional_string(spec, "endpoint_id"), + } + local result = fdroid_update_candidates()(host_spec) return package_version { source_priority = { "fdroid" }, - updates = update_candidates { - package_name = package_name, - }, + updates = result.candidates, } end diff --git a/crates/getter-core/src/luaclass/github_android_apk.lua b/crates/getter-core/src/luaclass/github_android_apk.lua index 33d8791..753f7f0 100644 --- a/crates/getter-core/src/luaclass/github_android_apk.lua +++ b/crates/getter-core/src/luaclass/github_android_apk.lua @@ -8,6 +8,20 @@ local function require_string(table, field) return value end +local function optional_string(table, field) + local value = table[field] + if value == nil then + return nil + end + if type(value) ~= "string" then + error("github_android.package field '" .. field .. "' must be a string") + end + if value == "" then + return nil + end + return value +end + local function optional_table(table, field) local value = table[field] if value == nil then @@ -31,35 +45,38 @@ local function optional_boolean(table, field) end local function github_release_candidates() - if type(getter_dev) ~= "table" or type(getter_dev.github_release_candidates) ~= "function" then - error("luaclass.github_android_apk requires operation-installed getter_dev.github_release_candidates") + if type(getter) ~= "table" + or type(getter.provider) ~= "table" + or type(getter.provider.github) ~= "table" + or type(getter.provider.github.release_candidates) ~= "function" then + error("luaclass.github_android_apk requires operation-installed getter.provider.github.release_candidates") end - return getter_dev.github_release_candidates + return getter.provider.github.release_candidates end function github_android.package(spec) if type(spec) ~= "table" then error("github_android.package expects a table") end - local owner = require_string(spec, "owner") - local repo = require_string(spec, "repo") - local release_candidates = github_release_candidates() - local result = { + local host_spec = { + owner = require_string(spec, "owner"), + repo = require_string(spec, "repo"), + asset = optional_table(spec, "asset"), + include_prereleases = optional_boolean(spec, "include_prereleases"), + endpoint_id = optional_string(spec, "endpoint_id"), + } + local result = github_release_candidates()(host_spec) + local package = { name = spec.name, source_priority = { "github" }, - updates = release_candidates { - owner = owner, - repo = repo, - asset = optional_table(spec, "asset"), - include_prereleases = optional_boolean(spec, "include_prereleases"), - }, + updates = result.candidates, } if spec.android_package ~= nil then - result.installed = { + package.installed = { { kind = "android_package", package_name = require_string(spec, "android_package") }, } end - return package_version(result) + return package_version(package) end return github_android diff --git a/crates/getter-core/src/runtime.rs b/crates/getter-core/src/runtime.rs index 3dd4b7c..992978f 100644 --- a/crates/getter-core/src/runtime.rs +++ b/crates/getter-core/src/runtime.rs @@ -707,7 +707,7 @@ mod tests { assert_eq!(task.task_id, "task-1"); assert_eq!(task.status, RuntimeTaskStatus::Queued); - assert_eq!(task.capabilities.cancel, true); + assert!(task.capabilities.cancel); let error = runtime.submit_action(&action.action_id).unwrap_err(); assert_eq!(error.code(), "action.not_found"); assert_eq!(notifications.lock().unwrap().len(), 1); @@ -729,9 +729,9 @@ mod tests { TaskPhaseReason::InstallHandoff ) ); - assert_eq!(waiting.capabilities.pause, false); - assert_eq!(waiting.capabilities.resume, false); - assert_eq!(waiting.capabilities.cancel, true); + assert!(!waiting.capabilities.pause); + assert!(!waiting.capabilities.resume); + assert!(waiting.capabilities.cancel); let completed = runtime .user_result(&task_id, UserResult::Accepted, None) @@ -755,7 +755,7 @@ mod tests { .unwrap(); assert_eq!(failed.status, RuntimeTaskStatus::Failed); - assert_eq!(failed.capabilities.retry, true); + assert!(failed.capabilities.retry); assert_eq!( failed.current_diagnostic.as_ref().unwrap().code, "user.rejected" @@ -798,8 +798,8 @@ mod tests { let paused = runtime.pause_task(&task_id).unwrap(); assert_eq!(paused.status, RuntimeTaskStatus::Paused); - assert_eq!(paused.capabilities.resume, true); - assert_eq!(paused.capabilities.cancel, true); + assert!(paused.capabilities.resume); + assert!(paused.capabilities.cancel); let resumed = runtime.resume_task(&task_id).unwrap(); assert_eq!(resumed.status, RuntimeTaskStatus::Running); diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index e81dab9..81c2e82 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true [features] default = [] lua = ["getter-core/lua", "dep:mlua"] -lua-provider-host-dev = ["lua", "getter-core/provider-luaclass-dev"] +lua-provider-host-dev = ["lua"] [dependencies] json_comments = "0.2" diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 24101c7..9ad0aa3 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -75,8 +75,8 @@ const PROVIDER_CACHE_PROVENANCE_MISSING: &str = "package.provider.cache_provenan /// /// This is a development/provider-host tracer, not the stable package-eval or /// product update-check API. Repository packages can exercise repository-local -/// `luaclass/` modules that call `getter_dev.fdroid_update_candidates { ... }`, -/// while catalog parsing/cache behavior stays in `getter-operations`. +/// or built-in `luaclass/` modules while catalog parsing/cache behavior stays +/// in `getter-operations`. pub fn fdroid_package_eval_json( data_dir: &Path, request_json: &str, @@ -174,8 +174,8 @@ struct FdroidProviderHostConfig { /// /// This is a development/provider-host tracer, not the stable package-eval or /// product update-check API. Repository packages can exercise repository-local -/// `luaclass/` modules that call `getter_dev.github_release_candidates { ... }`, -/// while release parsing/cache behavior stays in `getter-operations`. +/// or built-in `luaclass/` modules while release parsing/cache behavior stays +/// in `getter-operations`. pub fn github_package_eval_json( data_dir: &Path, request_json: &str, @@ -407,29 +407,42 @@ struct GithubProviderHostConfig { } fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Result<()> { + let provider_config = FdroidProviderHostConfig { + cache_db_path: config.cache_db_path.clone(), + endpoint: config.endpoint.clone(), + mode: config.mode, + index_xml: config.index_xml.clone(), + manifest: None, + }; + let provider_calls = Rc::clone(&config.provider_calls); + let update_candidates = lua.create_function(move |lua, spec: Table| { + let result = fdroid_update_candidates_envelope(lua, spec, provider_config.clone())?; + provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + Ok(result) + })?; + + let globals = lua.globals(); + let getter = nested_table(lua, globals.clone(), "getter")?; + let provider = nested_table(lua, getter, "provider")?; + let fdroid_table = nested_table(lua, provider, "fdroid")?; + fdroid_table.set("update_candidates", update_candidates.clone())?; + + let getter_builtin = nested_table(lua, globals.clone(), "getter_builtin")?; + let builtin_provider = nested_table(lua, getter_builtin, "provider")?; + let builtin_fdroid = nested_table(lua, builtin_provider, "fdroid")?; + builtin_fdroid.set("update_candidates", update_candidates.clone())?; + let host = lua.create_table()?; host.set( "fdroid_update_candidates", - lua.create_function(move |lua, spec: Table| { - let result = fdroid_update_candidates_envelope( - lua, - spec, - FdroidProviderHostConfig { - cache_db_path: config.cache_db_path.clone(), - endpoint: config.endpoint.clone(), - mode: config.mode, - index_xml: config.index_xml.clone(), - manifest: None, - }, - )?; - config - .provider_calls - .borrow_mut() - .push(provider_call_from_envelope(&result)?); + lua.create_function(move |_, spec: Table| { + let result: Table = update_candidates.call(spec)?; envelope_candidates(result) })?, )?; - lua.globals().set("getter_dev", host) + globals.set("getter_dev", host) } struct FdroidUpdateHostRequest { @@ -450,30 +463,43 @@ impl FdroidUpdateHostRequest { } fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { + let provider_config = GithubProviderHostConfig { + cache_db_path: config.cache_db_path.clone(), + api_base_url: config.api_base_url.clone(), + mode: config.mode, + releases_json: config.releases_json.clone(), + default_include_prereleases: config.default_include_prereleases, + manifest: None, + }; + let provider_calls = Rc::clone(&config.provider_calls); + let release_candidates = lua.create_function(move |lua, spec: Table| { + let result = github_release_candidates_envelope(lua, spec, provider_config.clone())?; + provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + Ok(result) + })?; + + let globals = lua.globals(); + let getter = nested_table(lua, globals.clone(), "getter")?; + let provider = nested_table(lua, getter, "provider")?; + let github_table = nested_table(lua, provider, "github")?; + github_table.set("release_candidates", release_candidates.clone())?; + + let getter_builtin = nested_table(lua, globals.clone(), "getter_builtin")?; + let builtin_provider = nested_table(lua, getter_builtin, "provider")?; + let builtin_github = nested_table(lua, builtin_provider, "github")?; + builtin_github.set("release_candidates", release_candidates.clone())?; + let host = lua.create_table()?; host.set( "github_release_candidates", - lua.create_function(move |lua, spec: Table| { - let result = github_release_candidates_envelope( - lua, - spec, - GithubProviderHostConfig { - cache_db_path: config.cache_db_path.clone(), - api_base_url: config.api_base_url.clone(), - mode: config.mode, - releases_json: config.releases_json.clone(), - default_include_prereleases: config.default_include_prereleases, - manifest: None, - }, - )?; - config - .provider_calls - .borrow_mut() - .push(provider_call_from_envelope(&result)?); + lua.create_function(move |_, spec: Table| { + let result: Table = release_candidates.call(spec)?; envelope_candidates(result) })?, )?; - lua.globals().set("getter_dev", host) + globals.set("getter_dev", host) } fn install_stable_provider_host( @@ -1104,7 +1130,7 @@ mod tests { ]"#; #[test] - fn fdroid_package_eval_uses_dev_builtin_luaclass_and_provider_cache() { + fn fdroid_package_eval_uses_builtin_luaclass_and_provider_cache() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_fdroid_package_fixture(data_dir, "org.fdroid.fdroid"); @@ -1194,7 +1220,7 @@ mod tests { } #[test] - fn github_package_eval_uses_dev_builtin_luaclass_and_provider_cache() { + fn github_package_eval_uses_builtin_luaclass_and_provider_cache() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_github_package_fixture(data_dir, "[.]apk$"); @@ -1439,6 +1465,85 @@ return package_version { ); } + #[test] + fn stable_provider_builtin_fdroid_luaclass_calls_stable_host() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "org.fdroid.fdroid", +} +"#, + Some(FDROID_INDEX_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["provider"], FDROID_PROVIDER_ID); + assert_eq!(result["provider_calls"][0]["request"], "catalog"); + assert_eq!(result["package"]["source_priority"], json!(["fdroid"])); + assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); + assert_eq!(result["package"]["updates"][0]["source"], "fdroid"); + } + + #[test] + fn stable_provider_builtin_github_luaclass_calls_stable_host() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + android_package = "org.fdroid.fdroid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "[.]apk$" }, +} +"#, + Some(GITHUB_RELEASES_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "github_releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["provider"], GITHUB_PROVIDER_ID); + assert_eq!(result["provider_calls"][0]["request"], "releases"); + assert_eq!(result["package"]["name"], "F-Droid"); + assert_eq!( + result["package"]["installed"][0]["package_name"], + "org.fdroid.fdroid" + ); + assert_eq!(result["package"]["source_priority"], json!(["github"])); + assert_eq!(result["package"]["updates"][0]["version"], "v1.20.0"); + assert_eq!(result["package"]["updates"][0]["source"], "github"); + } + #[test] fn stable_provider_host_does_not_install_latest_commit_shape() { let temp = tempfile::tempdir().unwrap(); @@ -1662,7 +1767,7 @@ end } #[test] - fn provider_package_eval_prefers_repository_luaclass_over_dev_builtin() { + fn provider_package_eval_prefers_repository_luaclass_over_builtin() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_fdroid_package_fixture(data_dir, "org.fdroid.fdroid"); From 05b0a85358b6082c2778736febbfa1a9249fcb14 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 14:39:25 +0800 Subject: [PATCH 56/72] fix(lua): validate provider endpoint ids --- .../src/lua_provider_host.rs | 173 +++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 9ad0aa3..bf26f73 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -70,6 +70,7 @@ pub enum LuaProviderHostOperationError { const FDROID_PACKAGE_NOT_FOUND: &str = "provider.fdroid.package_not_found"; const PROVIDER_RESPONSE_NOT_IN_MANIFEST: &str = "package.provider.response_not_in_manifest"; const PROVIDER_CACHE_PROVENANCE_MISSING: &str = "package.provider.cache_provenance_missing"; +const DEFAULT_GITHUB_ENDPOINT_ID: &str = "github"; /// Evaluates a package with a fixture-backed F-Droid catalog host binding. /// @@ -183,6 +184,10 @@ pub fn github_package_eval_json( let request: GithubPackageEvalRequest = serde_json::from_str(request_json) .map_err(|source| LuaProviderHostOperationError::InvalidRequest(source.to_string()))?; let mode = provider_cache_mode(request.mode.as_deref())?; + let endpoint_id = request + .endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_ENDPOINT_ID.to_owned()); let api_base_url = request .api_base_url .filter(|value| !value.trim().is_empty()) @@ -215,6 +220,7 @@ pub fn github_package_eval_json( lua, GithubDevHostConfig { cache_db_path, + endpoint_id, api_base_url, mode, releases_json, @@ -256,6 +262,10 @@ pub fn stable_provider_package_eval_json( .filter(|value| !value.trim().is_empty()) .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), }; + let github_endpoint_id = request + .github_endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_ENDPOINT_ID.to_owned()); let github_api_base_url = request .github_api_base_url .filter(|value| !value.trim().is_empty()) @@ -303,6 +313,7 @@ pub fn stable_provider_package_eval_json( }, github: GithubProviderHostConfig { cache_db_path: data_dir.join("cache.db"), + endpoint_id: github_endpoint_id, api_base_url: github_api_base_url, mode, releases_json: github_releases_json, @@ -336,6 +347,8 @@ struct StableProviderPackageEvalRequest { #[serde(default)] fdroid_index_xml: Option, #[serde(default)] + github_endpoint_id: Option, + #[serde(default)] github_api_base_url: Option, #[serde(default)] github_releases_json: Option, @@ -378,6 +391,8 @@ struct GithubPackageEvalRequest { repository_id: RepositoryId, package_id: PackageId, #[serde(default)] + endpoint_id: Option, + #[serde(default)] api_base_url: Option, #[serde(default)] mode: Option, @@ -389,6 +404,7 @@ struct GithubPackageEvalRequest { struct GithubDevHostConfig { cache_db_path: PathBuf, + endpoint_id: String, api_base_url: String, mode: ProviderCacheMode, releases_json: Option, @@ -399,6 +415,7 @@ struct GithubDevHostConfig { #[derive(Clone)] struct GithubProviderHostConfig { cache_db_path: PathBuf, + endpoint_id: String, api_base_url: String, mode: ProviderCacheMode, releases_json: Option, @@ -447,6 +464,7 @@ fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Resu struct FdroidUpdateHostRequest { package_name: String, + endpoint_id: Option, } impl FdroidUpdateHostRequest { @@ -458,6 +476,7 @@ impl FdroidUpdateHostRequest { .ok_or_else(|| { mlua::Error::external("F-Droid provider host missing package_name") })?, + endpoint_id: optional_lua_string_field(table, "endpoint_id", "F-Droid provider host")?, }) } } @@ -465,6 +484,7 @@ impl FdroidUpdateHostRequest { fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { let provider_config = GithubProviderHostConfig { cache_db_path: config.cache_db_path.clone(), + endpoint_id: config.endpoint_id.clone(), api_base_url: config.api_base_url.clone(), mode: config.mode, releases_json: config.releases_json.clone(), @@ -584,6 +604,7 @@ struct GithubReleaseHostRequest { repo: String, asset_filter: GithubAssetFilter, include_prereleases: Option, + endpoint_id: Option, } impl GithubReleaseHostRequest { @@ -593,6 +614,11 @@ impl GithubReleaseHostRequest { repo: required_lua_string(table, "repo")?, asset_filter: asset_filter_from_lua(table.get("asset")?)?, include_prereleases: optional_lua_bool(table, "include_prereleases")?, + endpoint_id: optional_lua_string_field( + table, + "endpoint_id", + "GitHub release provider host", + )?, }) } } @@ -603,6 +629,13 @@ fn fdroid_update_candidates_envelope( config: FdroidProviderHostConfig, ) -> mlua::Result
{ let request = FdroidUpdateHostRequest::from_lua_table(&spec)?; + if let Some(endpoint_id) = &request.endpoint_id { + if endpoint_id != &config.endpoint.endpoint_id { + return Err(mlua::Error::external(format!( + "F-Droid provider host endpoint_id '{endpoint_id}' is not installed" + ))); + } + } let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; let endpoint = config.endpoint; let refresh_fixture = config.index_xml; @@ -671,6 +704,14 @@ fn github_release_candidates_envelope( config: GithubProviderHostConfig, ) -> mlua::Result
{ let request = GithubReleaseHostRequest::from_lua_table(&spec)?; + if let Some(endpoint_id) = &request.endpoint_id { + if endpoint_id != &config.endpoint_id { + return Err(mlua::Error::external(format!( + "GitHub release provider host endpoint_id '{endpoint_id}' is not installed" + ))); + } + } + let endpoint_id = config.endpoint_id.clone(); let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; let provider_config = GithubReleaseConfig { api_base_url: config.api_base_url, @@ -723,6 +764,7 @@ fn github_release_candidates_envelope( json!({ "provider": GITHUB_PROVIDER_ID, "request": "releases", + "endpoint_id": endpoint_id, "owner": result.config.owner, "repo": result.config.repo, "cache_key": result.cache_key, @@ -1032,6 +1074,24 @@ fn optional_lua_bool(table: &Table, field: &'static str) -> mlua::Result mlua::Result> { + match table.get::(field)? { + LuaValue::Nil => Ok(None), + LuaValue::String(value) => { + let value = value.to_str()?; + let value = value.trim(); + Ok((!value.is_empty()).then(|| value.to_owned())) + } + _ => Err(mlua::Error::external(format!( + "{context} {field} must be a string" + ))), + } +} + fn asset_filter_from_lua(value: LuaValue) -> mlua::Result { match value { LuaValue::Nil => Ok(GithubAssetFilter::default()), @@ -1238,6 +1298,7 @@ mod tests { assert_eq!(first["operation"], "github.package_eval.fixture"); assert_eq!(first["provider_calls"][0]["source"], "refreshed"); + assert_eq!(first["provider_calls"][0]["endpoint_id"], "github"); assert_eq!(first["package"]["name"], "F-Droid"); assert_eq!(first["package"]["source_priority"], json!(["github"])); assert_eq!(first["package"]["updates"][0]["version"], "v1.20.0"); @@ -1267,6 +1328,28 @@ mod tests { assert_eq!(second["package"]["updates"][0]["version"], "v1.20.0"); } + #[test] + fn github_package_eval_uses_configured_endpoint_id() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_github_package_fixture_with_endpoint_id(data_dir, "[.]apk$", Some("mirror")); + + let result = github_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "endpoint_id": "mirror", + "releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["endpoint_id"], "mirror"); + assert_eq!(result["package"]["updates"][0]["source"], "github"); + } + #[test] fn github_package_eval_reports_asset_filter_diagnostics() { let temp = tempfile::tempdir().unwrap(); @@ -1495,11 +1578,46 @@ return fdroid.package { assert_eq!(result["provider_calls"][0]["provider"], FDROID_PROVIDER_ID); assert_eq!(result["provider_calls"][0]["request"], "catalog"); + assert_eq!(result["provider_calls"][0]["endpoint_id"], "official"); assert_eq!(result["package"]["source_priority"], json!(["fdroid"])); assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); assert_eq!(result["package"]["updates"][0]["source"], "fdroid"); } + #[test] + fn stable_provider_builtin_fdroid_rejects_uninstalled_endpoint_id() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "org.fdroid.fdroid", + endpoint_id = "mirror", +} +"#, + Some(FDROID_INDEX_FIXTURE), + false, + ); + + let err = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap_err(); + + assert!(err + .to_string() + .contains("F-Droid provider host endpoint_id 'mirror' is not installed")); + } + #[test] fn stable_provider_builtin_github_luaclass_calls_stable_host() { let temp = tempfile::tempdir().unwrap(); @@ -1534,6 +1652,7 @@ return github_android.package { assert_eq!(result["provider_calls"][0]["provider"], GITHUB_PROVIDER_ID); assert_eq!(result["provider_calls"][0]["request"], "releases"); + assert_eq!(result["provider_calls"][0]["endpoint_id"], "github"); assert_eq!(result["package"]["name"], "F-Droid"); assert_eq!( result["package"]["installed"][0]["package_name"], @@ -1544,6 +1663,44 @@ return github_android.package { assert_eq!(result["package"]["updates"][0]["source"], "github"); } + #[test] + fn stable_provider_builtin_github_rejects_uninstalled_endpoint_id() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + android_package = "org.fdroid.fdroid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "[.]apk$" }, + endpoint_id = "github-mirror", +} +"#, + Some(GITHUB_RELEASES_FIXTURE), + false, + ); + + let err = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "github_releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap_err(); + + assert!(err + .to_string() + .contains("GitHub release provider host endpoint_id 'github-mirror' is not installed")); + } + #[test] fn stable_provider_host_does_not_install_latest_commit_shape() { let temp = tempfile::tempdir().unwrap(); @@ -1850,6 +2007,14 @@ return fdroid.package { } fn write_github_package_fixture(data_dir: &std::path::Path, asset_include: &str) { + write_github_package_fixture_with_endpoint_id(data_dir, asset_include, None); + } + + fn write_github_package_fixture_with_endpoint_id( + data_dir: &std::path::Path, + asset_include: &str, + endpoint_id: Option<&str>, + ) { let repo_root = data_dir.join("repo/official"); let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); fs::create_dir_all(&package_dir).unwrap(); @@ -1861,6 +2026,9 @@ return fdroid.package { }"#, ) .unwrap(); + let endpoint_line = endpoint_id + .map(|endpoint_id| format!(" endpoint_id = \"{endpoint_id}\",\n")) + .unwrap_or_default(); let version_script = r#"#!/bin/upa-lua v1 local github_android = require("luaclass.github_android_apk") return github_android.package { @@ -1868,9 +2036,10 @@ return github_android.package { owner = "f-droid", repo = "fdroidclient", asset = { include = "__ASSET_INCLUDE__" }, -} +__ENDPOINT_LINE__} "# - .replace("__ASSET_INCLUDE__", asset_include); + .replace("__ASSET_INCLUDE__", asset_include) + .replace("__ENDPOINT_LINE__", &endpoint_line); fs::write(package_dir.join("9999.lua"), version_script).unwrap(); register_official_repository(data_dir, &repo_root); } From 15ee5362e09b89606b9daa34d08b66f321eb8ab3 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 14:56:23 +0800 Subject: [PATCH 57/72] test(autogen): lock fdroid generated script boundary --- .../getter-operations/src/fdroid_autogen.rs | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs index 3026de4..c0208b1 100644 --- a/crates/getter-operations/src/fdroid_autogen.rs +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -454,26 +454,19 @@ mod tests { assert_eq!(record["input"]["endpoint_url"], "https://f-droid.org/repo"); assert_eq!(record["input"]["package_name"], "org.fdroid.fdroid"); let files = candidate["files"].as_array().unwrap(); - assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" - && file["content"] - .as_str() - .unwrap() - .contains("version_code = 1020000"))); - assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" - && file["content"] - .as_str() - .unwrap() - .contains("return fdroid.package"))); - assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" - && file["content"] - .as_str() - .unwrap() - .contains("package_name = \"org.fdroid.fdroid\""))); - assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" - && file["content"] - .as_str() - .unwrap() - .contains("https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk"))); + let version_lua = files + .iter() + .find(|file| file["relative_path"] == "9999.lua") + .and_then(|file| file["content"].as_str()) + .unwrap(); + assert!(version_lua.contains("local catalog_package =")); + assert!(version_lua.contains("local fdroid = {}")); + assert!(version_lua.contains("version_code = 1020000")); + assert!(version_lua.contains("return fdroid.package")); + assert!(version_lua.contains("package_name = \"org.fdroid.fdroid\"")); + assert!(version_lua.contains("https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk")); + assert!(!version_lua.contains("require(\"luaclass.fdroid_android\")")); + assert!(!version_lua.contains("getter.provider")); assert!(files.iter().any(|file| file["relative_path"] == "Manifest" && file["content"].as_str().unwrap().is_empty())); assert_eq!( From 3996cbec98fbcdb0d9b445e54ba030b8a584e92f Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 15:00:13 +0800 Subject: [PATCH 58/72] test(lua): cover configured provider endpoints --- .../src/lua_provider_host.rs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index bf26f73..20f0562 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -1584,6 +1584,40 @@ return fdroid.package { assert_eq!(result["package"]["updates"][0]["source"], "fdroid"); } + #[test] + fn stable_provider_builtin_fdroid_accepts_configured_endpoint_id() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "org.fdroid.fdroid", + endpoint_id = "mirror", +} +"#, + Some(FDROID_INDEX_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_endpoint_id": "mirror", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["endpoint_id"], "mirror"); + assert_eq!(result["package"]["updates"][0]["source"], "fdroid"); + } + #[test] fn stable_provider_builtin_fdroid_rejects_uninstalled_endpoint_id() { let temp = tempfile::tempdir().unwrap(); @@ -1663,6 +1697,44 @@ return github_android.package { assert_eq!(result["package"]["updates"][0]["source"], "github"); } + #[test] + fn stable_provider_builtin_github_accepts_configured_endpoint_id() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + android_package = "org.fdroid.fdroid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "[.]apk$" }, + endpoint_id = "github-mirror", +} +"#, + Some(GITHUB_RELEASES_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "github_endpoint_id": "github-mirror", + "github_releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["endpoint_id"], "github-mirror"); + assert_eq!(result["package"]["updates"][0]["source"], "github"); + } + #[test] fn stable_provider_builtin_github_rejects_uninstalled_endpoint_id() { let temp = tempfile::tempdir().unwrap(); From 44cf664fa2ae3b98a3d762a40fd30c402233f78b Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 16:52:11 +0800 Subject: [PATCH 59/72] feat(runtime): use provider-backed update checks --- crates/getter-operations/src/lib.rs | 4 +- .../src/lua_provider_host.rs | 227 ++++++--- crates/getter-operations/src/runtime.rs | 456 +++++++++++++++++- 3 files changed, 598 insertions(+), 89 deletions(-) diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index f3ddcb5..8d7fc30 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -14,9 +14,9 @@ pub mod legacy_room; #[cfg(feature = "lua")] #[doc(hidden)] pub mod lua_http_policy; -#[cfg(feature = "lua-provider-host-dev")] +#[cfg(feature = "lua")] #[doc(hidden)] -pub mod lua_provider_host; +mod lua_provider_host; #[cfg(feature = "lua")] #[doc(hidden)] pub mod lua_runtime_hooks; diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 20f0562..36c55fc 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -1,10 +1,10 @@ //! Operation-installed Lua provider host bindings. //! //! This module is an implementation bridge toward ADR-0012 provider-backed Lua -//! modules. It exercises higher-level getter operations installing host -//! functions for package evaluation without making `getter-core` depend on -//! provider cache/storage crates. The stable `getter.provider.*` harness here -//! is still fixture-backed, dev-feature-gated, and hidden from product APIs. +//! modules. It lets higher-level getter operations install host functions for +//! package evaluation without making `getter-core` depend on provider +//! cache/storage crates. The stable `getter.provider.*` bindings are now shared +//! by product runtime update checks and internal fixture-backed harness tests. use crate::fdroid_catalog::{ read_or_refresh_fdroid_catalog, FdroidCatalogOperationError, FdroidEndpointConfig, @@ -20,17 +20,24 @@ use crate::provider_cache::{ PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1, }; use getter_core::lua::{evaluate_package_directory_script_with_host_bindings, LuaPackageError}; +#[cfg(feature = "lua-provider-host-dev")] +use getter_core::repository::RepositoryPackageDirectoryLayout; use getter_core::repository::{ PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageVersionScript, - RepositoryLoadError, RepositoryPackageDirectoryLayout, PACKAGE_MANIFEST_FILE, + RepositoryLoadError, PACKAGE_MANIFEST_FILE, }; -use getter_core::{PackageId, RepositoryId, ResolvedPackage}; +#[cfg(feature = "lua-provider-host-dev")] +use getter_core::PackageId; +use getter_core::{RepositoryId, ResolvedPackage}; use getter_providers::{ github_release_update_candidates, FdroidEndpoint, GithubAssetFilter, GithubProviderError, GithubRelease, GithubReleaseCandidateOptions, }; -use getter_storage::{CacheDb, MainDb, StorageError, StoredRepository}; +use getter_storage::{CacheDb, StorageError}; +#[cfg(feature = "lua-provider-host-dev")] +use getter_storage::{MainDb, StoredRepository}; use mlua::{Lua, Table, Value as LuaValue}; +#[cfg(feature = "lua-provider-host-dev")] use serde::Deserialize; use serde_json::{json, Map, Value}; use sha2::{Digest, Sha512}; @@ -78,6 +85,7 @@ const DEFAULT_GITHUB_ENDPOINT_ID: &str = "github"; /// product update-check API. Repository packages can exercise repository-local /// or built-in `luaclass/` modules while catalog parsing/cache behavior stays /// in `getter-operations`. +#[cfg(feature = "lua-provider-host-dev")] pub fn fdroid_package_eval_json( data_dir: &Path, request_json: &str, @@ -140,6 +148,7 @@ pub fn fdroid_package_eval_json( })) } +#[cfg(feature = "lua-provider-host-dev")] #[derive(Debug, Deserialize)] struct FdroidPackageEvalRequest { repository_id: RepositoryId, @@ -154,6 +163,7 @@ struct FdroidPackageEvalRequest { index_xml: Option, } +#[cfg(feature = "lua-provider-host-dev")] struct FdroidDevHostConfig { cache_db_path: PathBuf, endpoint: FdroidEndpointConfig, @@ -177,6 +187,7 @@ struct FdroidProviderHostConfig { /// product update-check API. Repository packages can exercise repository-local /// or built-in `luaclass/` modules while release parsing/cache behavior stays /// in `getter-operations`. +#[cfg(feature = "lua-provider-host-dev")] pub fn github_package_eval_json( data_dir: &Path, request_json: &str, @@ -245,6 +256,7 @@ pub fn github_package_eval_json( /// This is an internal Slice 1 harness for ADR-0012 provider host API v1. It /// installs the stable namespace and runtime hooks, but still uses fixture data /// and remains hidden from CLI/native/Flutter product APIs. +#[cfg(feature = "lua-provider-host-dev")] pub fn stable_provider_package_eval_json( data_dir: &Path, request_json: &str, @@ -252,24 +264,6 @@ pub fn stable_provider_package_eval_json( let request: StableProviderPackageEvalRequest = serde_json::from_str(request_json) .map_err(|source| LuaProviderHostOperationError::InvalidRequest(source.to_string()))?; let mode = provider_cache_mode(request.mode.as_deref())?; - let endpoint = FdroidEndpointConfig { - endpoint_id: request - .fdroid_endpoint_id - .filter(|value| !value.trim().is_empty()) - .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), - endpoint_url: request - .fdroid_endpoint_url - .filter(|value| !value.trim().is_empty()) - .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), - }; - let github_endpoint_id = request - .github_endpoint_id - .filter(|value| !value.trim().is_empty()) - .unwrap_or_else(|| DEFAULT_GITHUB_ENDPOINT_ID.to_owned()); - let github_api_base_url = request - .github_api_base_url - .filter(|value| !value.trim().is_empty()) - .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()); let main_db = MainDb::open(data_dir.join("main.db"))?; let repository = find_repository(&main_db, &request.repository_id)?; let repository_path = repo_path(&repository)?; @@ -282,58 +276,34 @@ pub fn stable_provider_package_eval_json( })?; let metadata = layout.package_metadata(package_directory)?; let script = layout.unambiguous_version_script(package_directory)?; - let provider_calls = Rc::new(RefCell::new(Vec::new())); - let runtime_hooks = Rc::new(RefCell::new(Vec::new())); - let fdroid_index_xml = request.fdroid_index_xml; - let github_releases_json = request.github_releases_json; - let default_include_prereleases = request.github_include_prereleases; - let manifest = if metadata - .permissions_for(&script.file_name) - .contains(&PackageLuaPermission::AllowFreeNetwork) - { - None - } else { - Some(PackageManifest::load( - package_directory.path.join(PACKAGE_MANIFEST_FILE), - )?) - }; - let package = evaluate_with_stable_provider_host( + let result = evaluate_provider_backed_package( data_dir, &repository.id, package_directory, &metadata, script, - StableProviderHostEvalConfig { - fdroid: FdroidProviderHostConfig { - cache_db_path: data_dir.join("cache.db"), - endpoint, - mode, - index_xml: fdroid_index_xml, - manifest: manifest.clone(), - }, - github: GithubProviderHostConfig { - cache_db_path: data_dir.join("cache.db"), - endpoint_id: github_endpoint_id, - api_base_url: github_api_base_url, - mode, - releases_json: github_releases_json, - default_include_prereleases, - manifest, - }, - provider_calls: Rc::clone(&provider_calls), - runtime_hooks: Rc::clone(&runtime_hooks), + ProviderBackedPackageEvalConfig { + mode, + fdroid_endpoint_id: request.fdroid_endpoint_id, + fdroid_endpoint_url: request.fdroid_endpoint_url, + fdroid_index_xml: request.fdroid_index_xml, + github_endpoint_id: request.github_endpoint_id, + github_api_base_url: request.github_api_base_url, + github_releases_json: request.github_releases_json, + github_include_prereleases: request.github_include_prereleases, }, )?; - let package = serde_json::to_value(package)?; + let package = serde_json::to_value(result.package)?; Ok(json!({ "operation": "provider.package_eval.fixture", "package": package, - "provider_calls": provider_calls.borrow().clone(), - "runtime_hooks": runtime_hooks.borrow().clone(), + "provider_calls": result.provider_calls, + "runtime_hooks": result.runtime_hooks, })) } +#[cfg(feature = "lua-provider-host-dev")] #[derive(Debug, Deserialize)] struct StableProviderPackageEvalRequest { repository_id: RepositoryId, @@ -386,6 +356,7 @@ fn evaluate_with_stable_provider_host( .map_err(Into::into) } +#[cfg(feature = "lua-provider-host-dev")] #[derive(Debug, Deserialize)] struct GithubPackageEvalRequest { repository_id: RepositoryId, @@ -402,6 +373,7 @@ struct GithubPackageEvalRequest { include_prereleases: bool, } +#[cfg(feature = "lua-provider-host-dev")] struct GithubDevHostConfig { cache_db_path: PathBuf, endpoint_id: String, @@ -423,6 +395,100 @@ struct GithubProviderHostConfig { manifest: Option, } +#[derive(Debug, Clone)] +pub(crate) struct ProviderBackedPackageEvalConfig { + pub(crate) mode: ProviderCacheMode, + pub(crate) fdroid_endpoint_id: Option, + pub(crate) fdroid_endpoint_url: Option, + pub(crate) fdroid_index_xml: Option, + pub(crate) github_endpoint_id: Option, + pub(crate) github_api_base_url: Option, + pub(crate) github_releases_json: Option, + pub(crate) github_include_prereleases: bool, +} + +pub(crate) struct ProviderBackedPackageEvalResult { + pub(crate) package: ResolvedPackage, + pub(crate) provider_calls: Vec, + pub(crate) runtime_hooks: Vec, +} + +pub(crate) fn evaluate_provider_backed_package( + data_dir: &Path, + repository_id: &RepositoryId, + package_directory: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + config: ProviderBackedPackageEvalConfig, +) -> Result { + let fdroid_endpoint = FdroidEndpointConfig { + endpoint_id: config + .fdroid_endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), + endpoint_url: config + .fdroid_endpoint_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), + }; + let github_endpoint_id = config + .github_endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_ENDPOINT_ID.to_owned()); + let github_api_base_url = config + .github_api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()); + let manifest = if metadata + .permissions_for(&script.file_name) + .contains(&PackageLuaPermission::AllowFreeNetwork) + { + None + } else { + Some(PackageManifest::load( + package_directory.path.join(PACKAGE_MANIFEST_FILE), + )?) + }; + let provider_calls = Rc::new(RefCell::new(Vec::new())); + let runtime_hooks = Rc::new(RefCell::new(Vec::new())); + let package = evaluate_with_stable_provider_host( + data_dir, + repository_id, + package_directory, + metadata, + script, + StableProviderHostEvalConfig { + fdroid: FdroidProviderHostConfig { + cache_db_path: data_dir.join("cache.db"), + endpoint: fdroid_endpoint, + mode: config.mode, + index_xml: config.fdroid_index_xml, + manifest: manifest.clone(), + }, + github: GithubProviderHostConfig { + cache_db_path: data_dir.join("cache.db"), + endpoint_id: github_endpoint_id, + api_base_url: github_api_base_url, + mode: config.mode, + releases_json: config.github_releases_json, + default_include_prereleases: config.github_include_prereleases, + manifest, + }, + provider_calls: Rc::clone(&provider_calls), + runtime_hooks: Rc::clone(&runtime_hooks), + }, + )?; + + let provider_calls = provider_calls.borrow().clone(); + let runtime_hooks = runtime_hooks.borrow().clone(); + Ok(ProviderBackedPackageEvalResult { + package, + provider_calls, + runtime_hooks, + }) +} + +#[cfg(feature = "lua-provider-host-dev")] fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Result<()> { let provider_config = FdroidProviderHostConfig { cache_db_path: config.cache_db_path.clone(), @@ -481,6 +547,7 @@ impl FdroidUpdateHostRequest { } } +#[cfg(feature = "lua-provider-host-dev")] fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { let provider_config = GithubProviderHostConfig { cache_db_path: config.cache_db_path.clone(), @@ -641,8 +708,9 @@ fn fdroid_update_candidates_envelope( let refresh_fixture = config.index_xml; let manifest = config.manifest.clone(); let result = read_or_refresh_fdroid_catalog(&db, endpoint, config.mode, || { - let xml = refresh_fixture - .ok_or_else(|| "fixture-backed F-Droid catalog host requires index_xml".to_owned())?; + let xml = refresh_fixture.ok_or_else(|| { + "F-Droid provider host has no cached catalog and no refresh source installed".to_owned() + })?; require_manifest_body(&manifest, FDROID_PROVIDER_ID, &xml) .map_err(|source| source.to_string())?; Ok(xml) @@ -722,7 +790,8 @@ fn github_release_candidates_envelope( let manifest = config.manifest.clone(); let result = read_or_refresh_github_releases(&db, provider_config, config.mode, || { let releases = refresh_fixture.ok_or_else(|| { - "fixture-backed GitHub release host requires releases_json".to_owned() + "GitHub release provider host has no cached releases and no refresh source installed" + .to_owned() })?; require_manifest_body(&manifest, GITHUB_PROVIDER_ID, &releases) .map_err(|source| source.to_string())?; @@ -811,6 +880,7 @@ fn provider_call_from_envelope(envelope: &Table) -> mlua::Result { Ok(Value::Object(object)) } +#[cfg(feature = "lua-provider-host-dev")] fn envelope_candidates(envelope: Table) -> mlua::Result { envelope.get("candidates") } @@ -824,16 +894,28 @@ fn json_object_to_lua(lua: &Lua, value: Value) -> mlua::Result
{ } } +fn lua_integer(value: i64, original: &serde_json::Number) -> mlua::Result { + mlua::Integer::try_from(value) + .map(LuaValue::Integer) + .map_err(|_| { + mlua::Error::external(format!( + "provider host integer {original} cannot be represented" + )) + }) +} + fn json_value_to_lua(lua: &Lua, value: Value) -> mlua::Result { match value { Value::Null => Ok(LuaValue::Nil), Value::Bool(value) => Ok(LuaValue::Boolean(value)), Value::Number(number) => { if let Some(value) = number.as_i64() { - return Ok(LuaValue::Integer(value)); + return lua_integer(value, &number); } - if let Some(value) = number.as_u64().and_then(|value| i64::try_from(value).ok()) { - return Ok(LuaValue::Integer(value)); + if let Some(value) = number.as_u64() { + if let Ok(value) = i64::try_from(value) { + return lua_integer(value, &number); + } } Ok(LuaValue::Number(number.as_f64().ok_or_else(|| { mlua::Error::external(format!( @@ -1020,7 +1102,8 @@ fn reject_unproven_manifest_cache( ))) } -fn provider_cache_mode( +#[cfg(feature = "lua-provider-host-dev")] +pub(crate) fn provider_cache_mode( mode: Option<&str>, ) -> Result { match mode { @@ -1032,6 +1115,7 @@ fn provider_cache_mode( } } +#[cfg(feature = "lua-provider-host-dev")] fn find_repository( db: &MainDb, repository_id: &RepositoryId, @@ -1046,6 +1130,7 @@ fn find_repository( }) } +#[cfg(feature = "lua-provider-host-dev")] fn repo_path(repository: &StoredRepository) -> Result { repository.path.as_ref().map(PathBuf::from).ok_or_else(|| { LuaProviderHostOperationError::InvalidRequest(format!( @@ -1144,7 +1229,7 @@ fn provider_diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { }) } -#[cfg(test)] +#[cfg(all(test, feature = "lua-provider-host-dev"))] mod tests { use super::*; use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 5a3b94e..d68025c 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -7,11 +7,15 @@ //! Android glue. #[cfg(feature = "lua")] -use getter_core::{ - lua::evaluate_package_directory_script, - repository::{ - package_directory_cache_key, RepositoryLoadError, RepositoryPackageDirectoryLayout, - }, +use crate::lua_provider_host::{ + evaluate_provider_backed_package, LuaProviderHostOperationError, + ProviderBackedPackageEvalConfig, +}; +#[cfg(feature = "lua")] +use crate::provider_cache::ProviderCacheMode; +#[cfg(feature = "lua")] +use getter_core::repository::{ + package_directory_cache_key, RepositoryLoadError, RepositoryPackageDirectoryLayout, }; use getter_core::{ runtime::{ @@ -49,6 +53,9 @@ pub enum RuntimeOperationError { #[cfg(feature = "lua")] #[error("package evaluation failed: {0}")] PackageEval(String), + #[cfg(feature = "lua")] + #[error("provider-backed package evaluation failed: {0}")] + ProviderPackageEval(#[from] LuaProviderHostOperationError), #[error("runtime response serialization failed: {0}")] Serialize(String), } @@ -65,6 +72,19 @@ impl RuntimeOperationError { Self::Repository(_) => "repository.error", #[cfg(feature = "lua")] Self::PackageEval(_) => "package.eval_error", + #[cfg(feature = "lua")] + Self::ProviderPackageEval(error) => match error { + LuaProviderHostOperationError::InvalidRequest(_) => "runtime.invalid_request", + LuaProviderHostOperationError::Storage(_) => "storage.error", + LuaProviderHostOperationError::Repository(_) => "repository.error", + LuaProviderHostOperationError::PackageEval(_) => "package.eval_error", + LuaProviderHostOperationError::Fdroid(_) => "provider.fdroid.error", + LuaProviderHostOperationError::Github(_) + | LuaProviderHostOperationError::GithubProvider(_) => "provider.github.error", + LuaProviderHostOperationError::Serialization(_) => "runtime.serialize_error", + LuaProviderHostOperationError::ReadManifest { .. } + | LuaProviderHostOperationError::InvalidManifest { .. } => "package.manifest_error", + }, Self::Serialize(_) => "runtime.serialize_error", } } @@ -79,7 +99,9 @@ impl RuntimeOperationError { #[cfg(feature = "lua")] Self::Repository(_) => "Getter repository operation failed", #[cfg(feature = "lua")] - Self::PackageEval(_) => "Getter package evaluation failed", + Self::PackageEval(_) | Self::ProviderPackageEval(_) => { + "Getter package evaluation failed" + } Self::Serialize(_) => "Getter runtime response serialization failed", } } @@ -95,6 +117,8 @@ impl RuntimeOperationError { Self::Repository(error) => Some(error.to_string()), #[cfg(feature = "lua")] Self::PackageEval(detail) => Some(detail.clone()), + #[cfg(feature = "lua")] + Self::ProviderPackageEval(error) => Some(error.to_string()), } } } @@ -134,14 +158,15 @@ pub fn issue_action_from_offline_update_check_json( #[cfg(feature = "lua")] pub fn issue_action_from_registered_package_json( runtime: &mut GetterRuntime, + data_dir: &std::path::Path, db: &MainDb, request_json: &str, ) -> Result { let request: RegisteredPackageUpdateActionRequest = parse_request(request_json)?; - let (package, dependency_digest) = evaluate_registered_package(db, &request)?; - let candidates = StaticPackageUpdatesProvider.check_updates(&package); + let evaluated = evaluate_registered_package(data_dir, db, &request)?; + let candidates = StaticPackageUpdatesProvider.check_updates(&evaluated.package); let update = check_updates_offline( - package.id.clone(), + evaluated.package.id.clone(), request.installed_version, candidates, UpdateSelectionPolicy { @@ -156,14 +181,16 @@ pub fn issue_action_from_registered_package_json( actions: update.actions.clone(), lua_object: PackageVersionLuaObject { object_id: format!("package-update:{}", update.package_id), - dependency_digest, + dependency_digest: evaluated.dependency_digest, }, })) }; Ok(json!({ - "package": package, + "package": evaluated.package, "update": update, "action": action.map(issued_action_json), + "provider_calls": evaluated.provider_calls, + "runtime_hooks": evaluated.runtime_hooks, })) } @@ -365,11 +392,20 @@ struct CleanTasksRequest { mode: Option, } +#[cfg(feature = "lua")] +struct RegisteredPackageEvaluation { + package: getter_core::ResolvedPackage, + dependency_digest: String, + provider_calls: Vec, + runtime_hooks: Vec, +} + #[cfg(feature = "lua")] fn evaluate_registered_package( + data_dir: &std::path::Path, db: &MainDb, request: &RegisteredPackageUpdateActionRequest, -) -> Result<(getter_core::ResolvedPackage, String), RuntimeOperationError> { +) -> Result { let repositories = db.repositories()?; let mut missing_path = None; for repository in repositories { @@ -391,15 +427,34 @@ fn evaluate_registered_package( }; let metadata = layout.package_metadata(package_directory)?; let script = layout.unambiguous_version_script(package_directory)?; - let package = - evaluate_package_directory_script(&repository.id, package_directory, &metadata, script) - .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; + let provider_eval = evaluate_provider_backed_package( + data_dir, + &repository.id, + package_directory, + &metadata, + script, + ProviderBackedPackageEvalConfig { + mode: ProviderCacheMode::UseCached, + fdroid_endpoint_id: None, + fdroid_endpoint_url: None, + fdroid_index_xml: None, + github_endpoint_id: None, + github_api_base_url: None, + github_releases_json: None, + github_include_prereleases: false, + }, + )?; let cache_key = package_directory_cache_key(&repository.id, package_directory, script)?; let dependency_digest = format!( "repo:{}:package:{}:hash:{}", cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash ); - return Ok((package, dependency_digest)); + return Ok(RegisteredPackageEvaluation { + package: provider_eval.package, + dependency_digest, + provider_calls: provider_eval.provider_calls, + runtime_hooks: provider_eval.runtime_hooks, + }); } let detail = if let Some(repository_id) = request.repository_id.as_ref() { format!( @@ -439,6 +494,14 @@ impl CleanTasksRequest { mod tests { use super::*; #[cfg(feature = "lua")] + use crate::fdroid_catalog::{FdroidEndpointConfig, FDROID_PROVIDER_ID}; + #[cfg(feature = "lua")] + use crate::github_releases::{ + GithubReleaseConfig, DEFAULT_GITHUB_API_BASE_URL, GITHUB_PROVIDER_ID, + }; + #[cfg(feature = "lua")] + use crate::provider_cache::PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1; + #[cfg(feature = "lua")] use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; #[cfg(feature = "lua")] use getter_core::RepositoryPriority; @@ -447,8 +510,52 @@ mod tests { update::OFFLINE_UPDATE_CHECK_VERSION, UpdateAction, UpdateArtifact, UpdateCandidate, }; #[cfg(feature = "lua")] + use getter_providers::{parse_fdroid_index_xml, parse_github_releases_json}; + #[cfg(feature = "lua")] + use getter_storage::{CacheDb, ProviderResponseUpsert}; + #[cfg(feature = "lua")] + use sha2::{Digest, Sha512}; + #[cfg(feature = "lua")] use std::fs; + #[cfg(feature = "lua")] + const FDROID_INDEX_FIXTURE: &str = r#" + + + + F-Droid + App repository client + + 1.20.0 + 1020000 + org.fdroid.fdroid_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + +"#; + + #[cfg(feature = "lua")] + const GITHUB_RELEASES_FIXTURE: &str = r#"[ + { + "tag_name": "v1.20.0", + "name": "F-Droid 1.20.0", + "draft": false, + "prerelease": false, + "published_at": "2026-06-01T00:00:00Z", + "assets": [ + { + "name": "F-Droid.apk", + "browser_download_url": "https://github.com/f-droid/fdroidclient/releases/download/v1.20.0/F-Droid.apk", + "content_type": "application/vnd.android.package-archive", + "size": 12345, + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } +]"#; + #[cfg(feature = "lua")] #[test] fn registered_package_update_check_issues_action_from_package_directory_static_updates() { @@ -471,6 +578,7 @@ mod tests { let issued = issue_action_from_registered_package_json( &mut runtime, + temp.path(), &db, &json!({ "package_id": "android/app/com.example.autogen", @@ -483,6 +591,8 @@ mod tests { assert_eq!(issued["package"]["repository"], "autogen"); assert_eq!(issued["package"]["name"], "Example Autogen"); assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!(issued["provider_calls"], json!([])); + assert_eq!(issued["runtime_hooks"], json!([])); let action_id = issued["action"]["action_id"].as_str().unwrap(); let submitted = submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) @@ -512,6 +622,7 @@ mod tests { let issued = issue_action_from_registered_package_json( &mut runtime, + temp.path(), &db, &json!({ "package_id": "android/app/com.example.autogen", @@ -525,6 +636,181 @@ mod tests { assert!(issued["action"].is_null()); } + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_issues_action_from_fdroid_provider_luaclass() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_fdroid_provider_package_repo(&repo_root, Some(FDROID_INDEX_FIXTURE), false); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + seed_fdroid_provider_cache(data_dir, FDROID_INDEX_FIXTURE); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["package"]["source_priority"], json!(["fdroid"])); + assert_eq!(issued["provider_calls"][0]["provider"], "fdroid"); + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "1.20.0" + ); + assert_eq!( + issued["update"]["actions"][0]["url"], + "https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk" + ); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + assert_eq!( + submitted["package_id"], + "android/f-droid/app/org.fdroid.fdroid" + ); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_issues_action_from_github_provider_luaclass() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_github_provider_package_repo(&repo_root, GITHUB_RELEASES_FIXTURE); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + seed_github_provider_cache(data_dir, GITHUB_RELEASES_FIXTURE); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/app/org.fdroid.fdroid", + "installed_version": "v1.0.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["package"]["source_priority"], json!(["github"])); + assert_eq!(issued["provider_calls"][0]["provider"], "github"); + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "v1.20.0" + ); + assert_eq!(issued["update"]["actions"][0]["file_name"], "F-Droid.apk"); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_does_not_accept_provider_fixture_payload() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_fdroid_provider_package_repo(&repo_root, Some(FDROID_INDEX_FIXTURE), false); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + let mut runtime = GetterRuntime::new(); + + let err = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap_err(); + + assert_eq!(err.code(), "package.eval_error"); + assert!(err + .to_string() + .contains("F-Droid provider host has no cached catalog")); + assert_eq!(runtime.tasks().len(), 0); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_rejects_unmanifested_provider_cache() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_fdroid_provider_package_repo(&repo_root, None, false); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + seed_fdroid_provider_cache(data_dir, FDROID_INDEX_FIXTURE); + let mut runtime = GetterRuntime::new(); + + let err = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap_err(); + + assert_eq!(err.code(), "package.eval_error"); + assert!(err + .to_string() + .contains("package.provider.response_not_in_manifest")); + assert_eq!(runtime.tasks().len(), 0); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_accepts_manifest_compatible_provider_cache() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_fdroid_provider_package_repo(&repo_root, Some(FDROID_INDEX_FIXTURE), false); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + seed_fdroid_provider_cache(data_dir, FDROID_INDEX_FIXTURE); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "1.20.0" + ); + } + #[test] fn offline_update_check_issues_getter_owned_action_id() { let mut runtime = GetterRuntime::new(); @@ -650,6 +936,144 @@ mod tests { runtime.submit_action(&action.action_id).unwrap().task_id } + #[cfg(feature = "lua")] + fn seed_fdroid_provider_cache(data_dir: &std::path::Path, fixture_body: &str) { + let endpoint = FdroidEndpointConfig::default(); + let catalog = parse_fdroid_index_xml(fixture_body).unwrap(); + let db = CacheDb::open(data_dir.join("cache.db")).unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: endpoint.cache_key(), + provider: FDROID_PROVIDER_ID.to_owned(), + response_json: serde_json::to_value(catalog).unwrap(), + source_response_sha512: vec![sha512_hex(fixture_body.as_bytes())], + provenance_schema_version: Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1.to_owned()), + freshness_json: json!({}), + }) + .unwrap(); + } + + #[cfg(feature = "lua")] + fn seed_github_provider_cache(data_dir: &std::path::Path, fixture_body: &str) { + let config = GithubReleaseConfig { + api_base_url: DEFAULT_GITHUB_API_BASE_URL.to_owned(), + owner: "f-droid".to_owned(), + repo: "fdroidclient".to_owned(), + }; + let releases = parse_github_releases_json(fixture_body).unwrap(); + let db = CacheDb::open(data_dir.join("cache.db")).unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: config.cache_key(), + provider: GITHUB_PROVIDER_ID.to_owned(), + response_json: serde_json::to_value(releases).unwrap(), + source_response_sha512: vec![sha512_hex(fixture_body.as_bytes())], + provenance_schema_version: Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1.to_owned()), + freshness_json: json!({}), + }) + .unwrap(); + } + + #[cfg(feature = "lua")] + fn write_fdroid_provider_package_repo( + root: &std::path::Path, + manifest_body: Option<&str>, + allow_free_network: bool, + ) { + let package_dir = root.join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + let mut metadata = json!({ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } + }); + if allow_free_network { + metadata["lua"] = json!({ + "9999.lua": { "permission": ["allow_free_network"] } + }); + } + fs::write( + package_dir.join("metadata.jsonc"), + serde_json::to_string_pretty(&metadata).unwrap(), + ) + .unwrap(); + if let Some(body) = manifest_body { + fs::write( + package_dir.join("Manifest"), + format!("{} fixture-body\n", sha512_hex(body.as_bytes())), + ) + .unwrap(); + } + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "org.fdroid.fdroid", +} +"#, + ) + .unwrap(); + } + + #[cfg(feature = "lua")] + fn write_github_provider_package_repo(root: &std::path::Path, manifest_body: &str) { + let package_dir = root.join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("Manifest"), + format!("{} fixture-body\n", sha512_hex(manifest_body.as_bytes())), + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + android_package = "org.fdroid.fdroid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "[.]apk$" }, +} +"#, + ) + .unwrap(); + } + + #[cfg(feature = "lua")] + fn register_repository( + db: &MainDb, + id: &str, + name: &str, + priority: i32, + root: &std::path::Path, + ) { + db.upsert_repository( + &RepositoryMetadata { + id: id.parse().unwrap(), + name: name.to_owned(), + priority: RepositoryPriority::new(priority), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(root), + None, + ) + .unwrap(); + } + + #[cfg(feature = "lua")] + fn sha512_hex(body: &[u8]) -> String { + let mut hasher = Sha512::new(); + hasher.update(body); + format!("{:x}", hasher.finalize()) + } + #[cfg(feature = "lua")] fn write_package_directory_static_update_repo(root: &std::path::Path) { let package_dir = root.join("android/app/com.example.autogen"); From 2349451aa7c463e8805a82e87797fd47a1b3c36a Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 21:00:43 +0800 Subject: [PATCH 60/72] feat(autogen): use F-Droid provider module output --- crates/getter-cli/Cargo.toml | 2 +- crates/getter-cli/src/lib.rs | 19 +- crates/getter-cli/tests/bdd_cli.rs | 57 ++++- .../features/cli/autogen_installed.feature | 8 +- .../getter-operations/src/fdroid_autogen.rs | 242 +++++++++++++----- .../src/lua_provider_host.rs | 14 + 6 files changed, 267 insertions(+), 75 deletions(-) diff --git a/crates/getter-cli/Cargo.toml b/crates/getter-cli/Cargo.toml index a5cec89..4f1520b 100644 --- a/crates/getter-cli/Cargo.toml +++ b/crates/getter-cli/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core" } getter-downloader = { path = "../getter-downloader" } -getter-operations = { path = "../getter-operations" } +getter-operations = { path = "../getter-operations", features = ["lua"] } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 0d32f2f..ab22fd9 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -798,7 +798,7 @@ fn execute(invocation: CliInvocation) -> Result { )?) .map_err(|source| CliError::Download(format!("failed to serialize handoff: {source}"))) } - CliCommand::RuntimeScript { script } => run_runtime_script(&script), + CliCommand::RuntimeScript { script } => run_runtime_script(&invocation.data_dir, &script), CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; @@ -1547,7 +1547,7 @@ struct RuntimeScriptStep { plan: Option, } -fn run_runtime_script(path: &Path) -> Result { +fn run_runtime_script(data_dir: &Path, path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Runtime(format!("failed to read runtime script: {source}")))?; let script: RuntimeScript = serde_json::from_slice(&bytes).map_err(|source| { @@ -1557,7 +1557,7 @@ fn run_runtime_script(path: &Path) -> Result { let mut context = RuntimeScriptContext::default(); let mut outputs = Vec::new(); for step in script.steps { - let data = execute_runtime_script_step(&mut runtime, &mut context, step)?; + let data = execute_runtime_script_step(data_dir, &mut runtime, &mut context, step)?; outputs.push(data); } Ok(json!({ "steps": outputs })) @@ -1570,6 +1570,7 @@ struct RuntimeScriptContext { } fn execute_runtime_script_step( + data_dir: &Path, runtime: &mut GetterRuntime, context: &mut RuntimeScriptContext, step: RuntimeScriptStep, @@ -1585,6 +1586,18 @@ fn execute_runtime_script_step( }; runtime_operations::issue_action(runtime, plan) } + "update_check_package_issue_action" => { + let db = open_main_db(data_dir)?; + let payload = + replace_runtime_script_tokens(context, empty_object_payload(step.payload))?; + let payload = serde_json::to_string(&payload).map_err(|source| { + CliError::Runtime(format!("failed to serialize runtime request: {source}")) + })?; + runtime_operations::issue_action_from_registered_package_json( + runtime, data_dir, &db, &payload, + ) + .map_err(|source| CliError::Runtime(source.to_string()))? + } "submit_action" => runtime_json_operation( runtime, runtime_operations::submit_action_json, diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index b0a05e2..c0d8348 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -466,6 +466,33 @@ fn runtime_script_submits_completes_removes_and_cleans_task(world: &mut CliWorld world.runtime_script = Some(script); } +#[given(expr = "a runtime script checking generated package {string} at {string}")] +fn runtime_script_checks_generated_fdroid_package( + world: &mut CliWorld, + package_id: String, + installed_version: String, +) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let script = temp.path().join("runtime-script.json"); + fs::write( + &script, + serde_json::to_vec_pretty(&serde_json::json!({ + "steps": [ + { + "operation": "update_check_package_issue_action", + "payload": { + "package_id": package_id, + "installed_version": installed_version, + } + } + ] + })) + .expect("runtime script serializes"), + ) + .expect("write runtime script"); + world.runtime_script = Some(script); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -1302,6 +1329,26 @@ fn runtime_script_output_removes_completed_task(world: &mut CliWorld) { assert_eq!(steps[5]["data"]["tasks"].as_array().unwrap().len(), 0); } +#[then(expr = "the provider-backed F-Droid update check selects version_code {int}")] +fn provider_backed_fdroid_update_check_selects_version_code( + world: &mut CliWorld, + version_code: i64, +) { + let json = current_json(world); + assert_eq!(json["command"], "runtime script"); + let steps = json["data"]["steps"].as_array().expect("runtime steps"); + assert_eq!(steps.len(), 1); + assert_eq!(steps[0]["operation"], "update_check_package_issue_action"); + let data = &steps[0]["data"]; + assert_eq!(data["provider_calls"][0]["provider"], "fdroid"); + assert_eq!(data["provider_calls"][0]["source"], "cache"); + assert_eq!(data["update"]["status"], "update_available"); + assert_eq!( + data["update"]["selected"]["candidate"]["version_code"].as_i64(), + Some(version_code) + ); +} + #[then(expr = "the task list contains the remembered task with status {string}")] fn task_list_contains_remembered_task_with_status(world: &mut CliWorld, status: String) { let task_id = world @@ -1684,8 +1731,16 @@ fn autogen_repository_contains_generated_fdroid_package(world: &mut CliWorld, pa autogen_repository_contains_generated_package(world, package_id.clone()); let path = autogen_repo_path(world).join(package_relative_path(&package_id)); let content = fs::read_to_string(path.join("9999.lua")).expect("generated package readable"); + assert!(content.contains("local fdroid = require(\"luaclass.fdroid_android\")")); assert!(content.contains("return fdroid.package")); - assert!(content.contains("version_code = 1020000")); + assert!(content.contains("package_name = \"org.fdroid.fdroid\"")); + assert!(!content.contains("version_code = 1020000")); + assert!(!content.contains("local catalog_package =")); + let manifest = fs::read_to_string(path.join("Manifest")).expect("generated Manifest readable"); + assert!( + manifest.contains("fdroid-index.xml"), + "generated F-Droid Manifest should name provider source provenance: {manifest}" + ); } #[then(expr = "the autogen repository contains generated package {string}")] diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 26ce9c3..2c90255 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -19,11 +19,11 @@ Feature: Installed app autogen Then the command succeeds And the autogen repository contains generated F-Droid package "android/f-droid/app/org.fdroid.fdroid" And the app list contains autogen tracked package "android/f-droid/app/org.fdroid.fdroid" - When I run getter repo validate for autogen - Then the output reports a valid repository without network - When I run getter package eval for package "android/f-droid/app/org.fdroid.fdroid" + Given a runtime script checking generated package "android/f-droid/app/org.fdroid.fdroid" at "1.0.0" + When I run getter runtime script for that script Then the command succeeds - And the package eval contains update version_code 1020000 + And the output is valid JSON + And the provider-backed F-Droid update check selects version_code 1020000 Scenario: User previews F-Droid autogen from installed inventory Given an initialized getter data directory diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs index c0208b1..b1d33e7 100644 --- a/crates/getter-operations/src/fdroid_autogen.rs +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -18,7 +18,7 @@ use getter_core::autogen::{ InstalledInventory, InstalledInventoryItem, AUTOGEN_RECORD_VERSION, FDROID_AUTOGEN_GENERATOR, }; use getter_core::{InstalledTarget, PackageId, PackageKind}; -use getter_providers::{FdroidApp, FdroidRelease}; +use getter_providers::FdroidApp; use getter_storage::{CacheDb, MainDb}; use serde::Deserialize; use serde_json::{json, Value}; @@ -92,6 +92,7 @@ pub fn preview_fdroid_packages_json( let candidate = fdroid_candidate_json( &catalog.endpoint.endpoint_id, &catalog.endpoint.endpoint_url, + &catalog.source_response_sha512, &package_id, app, )?; @@ -216,6 +217,7 @@ fn fdroid_package_id(package_name: &str) -> AutogenOperationResult { fn fdroid_candidate_json( endpoint_id: &str, endpoint_url: &str, + source_response_sha512: &[String], package_id: &PackageId, app: &FdroidApp, ) -> AutogenOperationResult { @@ -225,7 +227,7 @@ fn fdroid_candidate_json( .as_deref() .filter(|name| !name.trim().is_empty()) .unwrap_or(&app.package_name); - let files = fdroid_generated_files(endpoint_url, app)?; + let files = fdroid_generated_files(endpoint_id, endpoint_url, source_response_sha512, app)?; let record = AutogenRecord { version: AUTOGEN_RECORD_VERSION, generator: FDROID_AUTOGEN_GENERATOR.to_owned(), @@ -278,15 +280,29 @@ fn fdroid_candidate_json( } fn fdroid_generated_files( + endpoint_id: &str, endpoint_url: &str, + source_response_sha512: &[String], app: &FdroidApp, ) -> AutogenOperationResult> { + if endpoint_id != DEFAULT_FDROID_ENDPOINT_ID || endpoint_url != DEFAULT_FDROID_ENDPOINT_URL { + return Err(AutogenOperationError::Autogen(format!( + "F-Droid provider-module autogen currently supports only endpoint '{}={}', got '{}={}'", + DEFAULT_FDROID_ENDPOINT_ID, DEFAULT_FDROID_ENDPOINT_URL, endpoint_id, endpoint_url + ))); + } + if source_response_sha512.is_empty() { + return Err(AutogenOperationError::Autogen( + "F-Droid provider-module autogen requires provider source provenance for Manifest generation" + .to_owned(), + )); + } let metadata = render_pretty_json(json!({ "type": "android:app", "android": { "package_name": app.package_name }, }))?; - let manifest = String::new(); - let version_lua = fdroid_version_lua(endpoint_url, app); + let manifest = fdroid_manifest(source_response_sha512); + let version_lua = fdroid_version_lua(app); Ok(vec![ generated_file("metadata.jsonc", metadata), generated_file("Manifest", manifest), @@ -294,54 +310,20 @@ fn fdroid_generated_files( ]) } -fn fdroid_version_lua(endpoint_url: &str, app: &FdroidApp) -> String { - let mut releases = app.packages.clone(); - releases.sort_by(|left, right| { - right - .version_code - .cmp(&left.version_code) - .then_with(|| right.version.cmp(&left.version)) - .then_with(|| left.apk_name.cmp(&right.apk_name)) - }); - let updates = releases +fn fdroid_manifest(source_response_sha512: &[String]) -> String { + source_response_sha512 .iter() - .map(|release| fdroid_update_lua(endpoint_url, release)) - .collect::>() - .join(",\n"); - format!( - "#!/bin/upa-lua v1\n-- @generated by UpgradeAll getter autogen (F-Droid catalog fixture)\nlocal catalog_package = {{\n package_name = {},\n updates = {{{updates}\n }},\n}}\n\nlocal fdroid = {{}}\nfunction fdroid.package(spec)\n if spec.package_name ~= catalog_package.package_name then\n error(\"F-Droid generated package_name mismatch\")\n end\n return package_version {{ updates = catalog_package.updates }}\nend\n\nreturn fdroid.package {{\n package_name = {},\n}}\n", - lua_string(&app.package_name), - lua_string(&app.package_name), - ) + .map(|digest| format!("{digest} fdroid-index.xml\n")) + .collect() } -fn fdroid_update_lua(endpoint_url: &str, release: &FdroidRelease) -> String { - let version_code = release - .version_code - .map(|value| format!("\n version_code = {value},")) - .unwrap_or_default(); - let sha256 = release - .sha256 - .as_deref() - .map(|value| format!("\n sha256 = {},", lua_string(value))) - .unwrap_or_default(); - let size = release - .size - .map(|value| format!("\n size = {value},")) - .unwrap_or_default(); +fn fdroid_version_lua(app: &FdroidApp) -> String { format!( - "\n {{\n version = {},{version_code}\n source = \"fdroid\",\n artifacts = {{\n {{\n name = {},\n url = {},\n file_name = {},{sha256}{size}\n }},\n }},\n }}", - lua_string(&release.version), - lua_string(&release.apk_name), - lua_string(&fdroid_artifact_url(endpoint_url, &release.apk_name)), - lua_string(&release.apk_name), + "#!/bin/upa-lua v1\n-- @generated by UpgradeAll getter autogen (F-Droid provider module)\nlocal fdroid = require(\"luaclass.fdroid_android\")\n\nreturn fdroid.package {{\n package_name = {},\n}}\n", + lua_string(&app.package_name), ) } -fn fdroid_artifact_url(endpoint_url: &str, apk_name: &str) -> String { - format!("{}/{}", endpoint_url.trim_end_matches('/'), apk_name) -} - fn render_pretty_json(value: Value) -> AutogenOperationResult { serde_json::to_string_pretty(&value) .map(|mut content| { @@ -378,10 +360,12 @@ fn lua_string(value: &str) -> String { #[cfg(test)] mod tests { use super::*; - use getter_core::lua::evaluate_package_directory_script; + use crate::provider_cache::source_response_sha512; use getter_core::repository::{ - RepositoryMetadata, RepositoryPackageDirectoryLayout, REPO_API_VERSION_V1, + PackageTypeMetadata, RepositoryMetadata, RepositoryPackageDirectoryLayout, + REPO_API_VERSION_V1, }; + use getter_core::runtime::GetterRuntime; use getter_core::RepositoryPriority; const FDROID_FIXTURE: &str = r#" @@ -459,16 +443,27 @@ mod tests { .find(|file| file["relative_path"] == "9999.lua") .and_then(|file| file["content"].as_str()) .unwrap(); - assert!(version_lua.contains("local catalog_package =")); - assert!(version_lua.contains("local fdroid = {}")); - assert!(version_lua.contains("version_code = 1020000")); + assert!(version_lua.contains("local fdroid = require(\"luaclass.fdroid_android\")")); assert!(version_lua.contains("return fdroid.package")); assert!(version_lua.contains("package_name = \"org.fdroid.fdroid\"")); - assert!(version_lua.contains("https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk")); - assert!(!version_lua.contains("require(\"luaclass.fdroid_android\")")); + assert!(!version_lua.contains("endpoint_id")); + assert!(!version_lua.contains("local catalog_package =")); + assert!(!version_lua.contains("version_code = 1020000")); + assert!(!version_lua.contains("https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk")); + assert!(!version_lua.contains("getter_dev")); assert!(!version_lua.contains("getter.provider")); - assert!(files.iter().any(|file| file["relative_path"] == "Manifest" - && file["content"].as_str().unwrap().is_empty())); + let manifest = files + .iter() + .find(|file| file["relative_path"] == "Manifest") + .and_then(|file| file["content"].as_str()) + .unwrap(); + assert_eq!( + manifest, + format!( + "{} fdroid-index.xml\n", + source_response_sha512(FDROID_FIXTURE) + ) + ); assert_eq!( preview["skipped"][0]["reason"], "provider_package_not_found" @@ -480,6 +475,57 @@ mod tests { assert!(!temp.path().join("repo/autogen").exists()); } + #[test] + fn provider_module_generation_rejects_non_default_endpoint() { + let app = FdroidApp { + package_name: "org.fdroid.fdroid".to_owned(), + name: None, + summary: None, + packages: Vec::new(), + }; + let digest = vec![source_response_sha512(FDROID_FIXTURE)]; + + let custom_id_error = + fdroid_generated_files("custom", DEFAULT_FDROID_ENDPOINT_URL, &digest, &app) + .unwrap_err(); + assert!( + matches!(custom_id_error, AutogenOperationError::Autogen(detail) if detail.contains("supports only endpoint 'official=https://f-droid.org/repo'")) + ); + + let custom_url_error = fdroid_generated_files( + DEFAULT_FDROID_ENDPOINT_ID, + "https://example.invalid/fdroid", + &digest, + &app, + ) + .unwrap_err(); + assert!( + matches!(custom_url_error, AutogenOperationError::Autogen(detail) if detail.contains("got 'official=https://example.invalid/fdroid'")) + ); + } + + #[test] + fn provider_module_generation_requires_source_provenance() { + let app = FdroidApp { + package_name: "org.fdroid.fdroid".to_owned(), + name: None, + summary: None, + packages: Vec::new(), + }; + + let error = fdroid_generated_files( + DEFAULT_FDROID_ENDPOINT_ID, + DEFAULT_FDROID_ENDPOINT_URL, + &[], + &app, + ) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("requires provider source provenance")) + ); + } + #[test] fn preview_skips_fdroid_package_covered_by_higher_priority_repo() { let temp = tempfile::tempdir().unwrap(); @@ -567,22 +613,86 @@ mod tests { .package(&"android/f-droid/app/org.fdroid.fdroid".parse().unwrap()) .unwrap(); let metadata = layout.package_metadata(package_dir).unwrap(); - let script = layout.unambiguous_version_script(package_dir).unwrap(); - let package = evaluate_package_directory_script( - &"autogen".parse().unwrap(), - package_dir, - &metadata, - script, + assert!(matches!( + &metadata.package, + PackageTypeMetadata::AndroidApp { android } + if android.package_name == "org.fdroid.fdroid" + )); + + let mut runtime = GetterRuntime::new(); + let issued = crate::runtime::issue_action_from_registered_package_json( + &mut runtime, + temp.path(), + &main_db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), ) .unwrap(); - assert_eq!(package.name, "android/f-droid/app/org.fdroid.fdroid"); - assert_eq!(package.installed.len(), 1); - assert_eq!(package.updates.len(), 2); - assert_eq!(package.updates[0].version_code, Some(1020000)); + assert_eq!(issued["provider_calls"][0]["provider"], "fdroid"); + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); assert_eq!( - package.updates[0].artifacts[0].sha256.as_deref(), - Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + issued["update"]["selected"]["candidate"]["version"], + "1.20.0" ); + assert_eq!( + issued["update"]["selected"]["candidate"]["version_code"], + 1020000 + ); + assert_eq!( + issued["update"]["actions"][0]["url"], + "https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk" + ); + } + + #[test] + fn generated_fdroid_package_update_check_rejects_manifest_mismatch() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + apply_fdroid_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap(); + let manifest_path = temp + .path() + .join("repo/autogen/android/f-droid/app/org.fdroid.fdroid/Manifest"); + std::fs::write(&manifest_path, "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 fdroid-index.xml\n").unwrap(); + + let mut runtime = GetterRuntime::new(); + let error = crate::runtime::issue_action_from_registered_package_json( + &mut runtime, + temp.path(), + &main_db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap_err(); + + assert_eq!(error.code(), "package.eval_error"); + assert!(error + .to_string() + .contains("package.provider.response_not_in_manifest")); } #[test] diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 36c55fc..12756cf 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -86,6 +86,7 @@ const DEFAULT_GITHUB_ENDPOINT_ID: &str = "github"; /// or built-in `luaclass/` modules while catalog parsing/cache behavior stays /// in `getter-operations`. #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] pub fn fdroid_package_eval_json( data_dir: &Path, request_json: &str, @@ -149,6 +150,7 @@ pub fn fdroid_package_eval_json( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] #[derive(Debug, Deserialize)] struct FdroidPackageEvalRequest { repository_id: RepositoryId, @@ -164,6 +166,7 @@ struct FdroidPackageEvalRequest { } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] struct FdroidDevHostConfig { cache_db_path: PathBuf, endpoint: FdroidEndpointConfig, @@ -188,6 +191,7 @@ struct FdroidProviderHostConfig { /// or built-in `luaclass/` modules while release parsing/cache behavior stays /// in `getter-operations`. #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] pub fn github_package_eval_json( data_dir: &Path, request_json: &str, @@ -257,6 +261,7 @@ pub fn github_package_eval_json( /// installs the stable namespace and runtime hooks, but still uses fixture data /// and remains hidden from CLI/native/Flutter product APIs. #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] pub fn stable_provider_package_eval_json( data_dir: &Path, request_json: &str, @@ -304,6 +309,7 @@ pub fn stable_provider_package_eval_json( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] #[derive(Debug, Deserialize)] struct StableProviderPackageEvalRequest { repository_id: RepositoryId, @@ -357,6 +363,7 @@ fn evaluate_with_stable_provider_host( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] #[derive(Debug, Deserialize)] struct GithubPackageEvalRequest { repository_id: RepositoryId, @@ -374,6 +381,7 @@ struct GithubPackageEvalRequest { } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] struct GithubDevHostConfig { cache_db_path: PathBuf, endpoint_id: String, @@ -489,6 +497,7 @@ pub(crate) fn evaluate_provider_backed_package( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Result<()> { let provider_config = FdroidProviderHostConfig { cache_db_path: config.cache_db_path.clone(), @@ -548,6 +557,7 @@ impl FdroidUpdateHostRequest { } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { let provider_config = GithubProviderHostConfig { cache_db_path: config.cache_db_path.clone(), @@ -881,6 +891,7 @@ fn provider_call_from_envelope(envelope: &Table) -> mlua::Result { } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] fn envelope_candidates(envelope: Table) -> mlua::Result { envelope.get("candidates") } @@ -1103,6 +1114,7 @@ fn reject_unproven_manifest_cache( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] pub(crate) fn provider_cache_mode( mode: Option<&str>, ) -> Result { @@ -1116,6 +1128,7 @@ pub(crate) fn provider_cache_mode( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] fn find_repository( db: &MainDb, repository_id: &RepositoryId, @@ -1131,6 +1144,7 @@ fn find_repository( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] fn repo_path(repository: &StoredRepository) -> Result { repository.path.as_ref().map(PathBuf::from).ok_or_else(|| { LuaProviderHostOperationError::InvalidRequest(format!( From 969a605c1a9d780548bfa9a4d6cc253bc579092e Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 2 Jul 2026 11:37:10 +0800 Subject: [PATCH 61/72] feat(provider): preserve GitHub release metadata --- crates/getter-core/src/lib.rs | 4 + crates/getter-core/src/lua.rs | 12 ++ crates/getter-core/src/update.rs | 3 + .../getter-operations/src/fdroid_autogen.rs | 10 +- crates/getter-operations/src/runtime.rs | 163 ++++++++++++++++-- crates/getter-providers/src/lib.rs | 10 ++ 6 files changed, 183 insertions(+), 19 deletions(-) diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 957386c..3fce46b 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -283,6 +283,8 @@ pub struct UpdateCandidate { #[serde(default)] pub version_code: Option, #[serde(default)] + pub changelog: Option, + #[serde(default)] pub channel: Option, #[serde(default)] pub source: Option, @@ -295,6 +297,8 @@ pub struct UpdateArtifact { pub name: String, pub url: String, #[serde(default)] + pub content_type: Option, + #[serde(default)] pub file_name: Option, #[serde(default)] pub sha256: Option, diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 3bdf60b..fa6044a 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -789,6 +789,7 @@ fn parse_update_candidates( Ok(UpdateCandidate { version: required_string(path, object, "version")?.to_owned(), version_code: optional_i64(path, object, "version_code")?, + changelog: optional_string(object, "changelog"), channel: optional_string(object, "channel"), source: optional_string(object, "source"), artifacts, @@ -818,6 +819,7 @@ fn parse_update_artifacts( Ok(UpdateArtifact { name: required_string(path, object, "name")?.to_owned(), url: required_string(path, object, "url")?.to_owned(), + content_type: optional_string(object, "content_type"), file_name: optional_string(object, "file_name"), sha256: optional_string(object, "sha256"), size: optional_u64(path, object, "size")?, @@ -974,12 +976,14 @@ return package_version { { version = "1.2.0", version_code = 120, + changelog = "Release notes from provider snapshot", channel = "stable", source = "fixture", artifacts = { { name = "app.apk", url = "https://example.invalid/app.apk", + content_type = "application/vnd.android.package-archive", file_name = "fdroid.apk", sha256 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", size = 12345, @@ -1007,8 +1011,16 @@ return package_version { assert_eq!(package.updates.len(), 1); assert_eq!(package.updates[0].version, "1.2.0"); assert_eq!(package.updates[0].version_code, Some(120)); + assert_eq!( + package.updates[0].changelog.as_deref(), + Some("Release notes from provider snapshot") + ); assert_eq!(package.updates[0].channel.as_deref(), Some("stable")); assert_eq!(package.updates[0].source.as_deref(), Some("fixture")); + assert_eq!( + package.updates[0].artifacts[0].content_type.as_deref(), + Some("application/vnd.android.package-archive") + ); assert_eq!( package.updates[0].artifacts[0].file_name.as_deref(), Some("fdroid.apk") diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs index 6e9260c..4315365 100644 --- a/crates/getter-core/src/update.rs +++ b/crates/getter-core/src/update.rs @@ -505,6 +505,7 @@ mod tests { vec![UpdateCandidate { version: "1.2.0".to_owned(), version_code: None, + changelog: None, channel: None, source: None, artifacts: Vec::new(), @@ -541,11 +542,13 @@ mod tests { UpdateCandidate { version: version.to_owned(), version_code: None, + changelog: None, channel: None, source: None, artifacts: vec![UpdateArtifact { name: "APK".to_owned(), url: format!("https://example.invalid/{version}.apk"), + content_type: None, file_name: Some("app.apk".to_owned()), sha256: None, size: None, diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs index b1d33e7..3e3ea1a 100644 --- a/crates/getter-operations/src/fdroid_autogen.rs +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -361,10 +361,10 @@ fn lua_string(value: &str) -> String { mod tests { use super::*; use crate::provider_cache::source_response_sha512; - use getter_core::repository::{ - PackageTypeMetadata, RepositoryMetadata, RepositoryPackageDirectoryLayout, - REPO_API_VERSION_V1, - }; + #[cfg(feature = "lua")] + use getter_core::repository::{PackageTypeMetadata, RepositoryPackageDirectoryLayout}; + use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; + #[cfg(feature = "lua")] use getter_core::runtime::GetterRuntime; use getter_core::RepositoryPriority; @@ -576,6 +576,7 @@ mod tests { assert_eq!(preview["skipped"][0]["covering_repo_id"], "official"); } + #[cfg(feature = "lua")] #[test] fn apply_writes_valid_fdroid_package_directory() { let temp = tempfile::tempdir().unwrap(); @@ -648,6 +649,7 @@ mod tests { ); } + #[cfg(feature = "lua")] #[test] fn generated_fdroid_package_update_check_rejects_manifest_mismatch() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index d68025c..08d8c88 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -556,6 +556,14 @@ mod tests { } ]"#; + #[cfg(feature = "lua")] + const UPGRADEALL_GITHUB_RELEASES_SNAPSHOT: &str = + include_str!("../../../tests/files/web/github_api_release.json"); + + #[cfg(feature = "lua")] + const UPGRADEALL_OLD_GITHUB_RELEASES_NORMALIZED: &str = + include_str!("../../../tests/files/data/provider_github_release.json"); + #[cfg(feature = "lua")] #[test] fn registered_package_update_check_issues_action_from_package_directory_static_updates() { @@ -717,6 +725,83 @@ mod tests { assert_eq!(issued["update"]["actions"][0]["file_name"], "F-Droid.apk"); } + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_matches_old_upgradeall_github_release_snapshot() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_upgradeall_github_provider_package_repo( + &repo_root, + UPGRADEALL_GITHUB_RELEASES_SNAPSHOT, + ); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + seed_github_provider_cache_for( + data_dir, + "DUpdateSystem", + "UpgradeAll", + UPGRADEALL_GITHUB_RELEASES_SNAPSHOT, + ); + let old_releases: Value = + serde_json::from_str(UPGRADEALL_OLD_GITHUB_RELEASES_NORMALIZED).unwrap(); + let expected_release = &old_releases[0]; + let expected_asset = &expected_release["assets"][0]; + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/app/net.xzos.upgradeall", + "installed_version": "0.13-beta.3" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["package"]["source_priority"], json!(["github"])); + assert_eq!( + issued["package"]["installed"][0], + json!({ "kind": "android_package", "package_name": "net.xzos.upgradeall" }) + ); + assert_eq!(issued["provider_calls"][0]["provider"], "github"); + assert_eq!(issued["provider_calls"][0]["owner"], "DUpdateSystem"); + assert_eq!(issued["provider_calls"][0]["repo"], "UpgradeAll"); + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + expected_release["version_number"] + ); + assert_eq!( + issued["update"]["selected"]["candidate"]["changelog"], + expected_release["changelog"] + ); + assert_eq!( + issued["update"]["selected"]["artifact"]["name"], + expected_asset["file_name"] + ); + assert_eq!( + issued["update"]["selected"]["artifact"]["content_type"], + expected_asset["file_type"] + ); + assert_eq!( + issued["update"]["actions"][0]["url"], + expected_asset["download_url"] + ); + assert_eq!( + issued["update"]["actions"][0]["file_name"], + expected_asset["file_name"] + ); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + assert_eq!(submitted["package_id"], "android/app/net.xzos.upgradeall"); + } + #[cfg(feature = "lua")] #[test] fn registered_package_update_check_does_not_accept_provider_fixture_payload() { @@ -954,10 +1039,20 @@ mod tests { #[cfg(feature = "lua")] fn seed_github_provider_cache(data_dir: &std::path::Path, fixture_body: &str) { + seed_github_provider_cache_for(data_dir, "f-droid", "fdroidclient", fixture_body); + } + + #[cfg(feature = "lua")] + fn seed_github_provider_cache_for( + data_dir: &std::path::Path, + owner: &str, + repo: &str, + fixture_body: &str, + ) { let config = GithubReleaseConfig { api_base_url: DEFAULT_GITHUB_API_BASE_URL.to_owned(), - owner: "f-droid".to_owned(), - repo: "fdroidclient".to_owned(), + owner: owner.to_owned(), + repo: repo.to_owned(), }; let releases = parse_github_releases_json(fixture_body).unwrap(); let db = CacheDb::open(data_dir.join("cache.db")).unwrap(); @@ -1016,13 +1111,47 @@ return fdroid.package { #[cfg(feature = "lua")] fn write_github_provider_package_repo(root: &std::path::Path, manifest_body: &str) { let package_dir = root.join("android/app/org.fdroid.fdroid"); - fs::create_dir_all(&package_dir).unwrap(); + write_github_provider_package( + &package_dir, + manifest_body, + "F-Droid", + "org.fdroid.fdroid", + "f-droid", + "fdroidclient", + ); + } + + #[cfg(feature = "lua")] + fn write_upgradeall_github_provider_package_repo(root: &std::path::Path, manifest_body: &str) { + let package_dir = root.join("android/app/net.xzos.upgradeall"); + write_github_provider_package( + &package_dir, + manifest_body, + "UpgradeAll", + "net.xzos.upgradeall", + "DUpdateSystem", + "UpgradeAll", + ); + } + + #[cfg(feature = "lua")] + fn write_github_provider_package( + package_dir: &std::path::Path, + manifest_body: &str, + name: &str, + android_package: &str, + owner: &str, + repo: &str, + ) { + fs::create_dir_all(package_dir).unwrap(); fs::write( package_dir.join("metadata.jsonc"), - r#"{ + format!( + r#"{{ "type": "android:app", - "android": { "package_name": "org.fdroid.fdroid" } -}"#, + "android": {{ "package_name": "{android_package}" }} +}}"# + ), ) .unwrap(); fs::write( @@ -1032,16 +1161,18 @@ return fdroid.package { .unwrap(); fs::write( package_dir.join("9999.lua"), - r#"#!/bin/upa-lua v1 + format!( + r#"#!/bin/upa-lua v1 local github_android = require("luaclass.github_android_apk") -return github_android.package { - name = "F-Droid", - android_package = "org.fdroid.fdroid", - owner = "f-droid", - repo = "fdroidclient", - asset = { include = "[.]apk$" }, -} -"#, +return github_android.package {{ + name = "{name}", + android_package = "{android_package}", + owner = "{owner}", + repo = "{repo}", + asset = {{ include = "[.]apk$" }}, +}} +"# + ), ) .unwrap(); } @@ -1126,11 +1257,13 @@ return package_version { .map(|version| UpdateCandidate { version: version.to_owned(), version_code: None, + changelog: None, channel: None, source: None, artifacts: vec![UpdateArtifact { name: "app.apk".to_owned(), url: "https://example.invalid/app.apk".to_owned(), + content_type: None, file_name: Some("app.apk".to_owned()), sha256: None, size: None, diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index a71cd49..001eae5 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -77,6 +77,7 @@ impl FdroidRelease { UpdateCandidate { version: self.version.clone(), version_code: self.version_code, + changelog: None, channel: None, source: Some("fdroid".to_owned()), artifacts: vec![UpdateArtifact { @@ -86,6 +87,7 @@ impl FdroidRelease { .as_deref() .map(|base| fdroid_artifact_url(base, &self.apk_name)) .unwrap_or_else(|| self.apk_name.clone()), + content_type: None, file_name: Some(self.apk_name.clone()), sha256: self.sha256.clone(), size: self.size, @@ -399,6 +401,7 @@ fn github_release_update_candidate( Some(UpdateCandidate { version: release.tag_name.clone(), version_code: None, + changelog: release.body.clone(), channel: release.prerelease.then(|| "prerelease".to_owned()), source: Some("github".to_owned()), artifacts, @@ -409,6 +412,7 @@ fn github_asset_update_artifact(asset: &GithubReleaseAsset) -> UpdateArtifact { UpdateArtifact { name: asset.name.clone(), url: asset.browser_download_url.clone(), + content_type: asset.content_type.clone(), file_name: Some(asset.name.clone()), sha256: github_asset_sha256(asset.digest.as_deref()), size: asset.size, @@ -505,6 +509,7 @@ mod tests { updates: vec![UpdateCandidate { version: "1.2.0".to_owned(), version_code: None, + changelog: None, channel: Some("stable".to_owned()), source: Some("fixture".to_owned()), artifacts: Vec::new(), @@ -623,6 +628,7 @@ mod tests { assert_eq!(candidates.len(), 1); assert_eq!(candidates[0].version, "v1.2.0"); + assert_eq!(candidates[0].changelog.as_deref(), Some("Release notes")); assert_eq!(candidates[0].source.as_deref(), Some("github")); assert_eq!(candidates[0].artifacts.len(), 1); let artifact = &candidates[0].artifacts[0]; @@ -631,6 +637,10 @@ mod tests { artifact.url, "https://github.com/example/app/releases/download/v1.2.0/app-release.apk" ); + assert_eq!( + artifact.content_type.as_deref(), + Some("application/vnd.android.package-archive") + ); assert_eq!(artifact.file_name.as_deref(), Some("app-release.apk")); assert_eq!( artifact.sha256.as_deref(), From 4d49476471a6567dfa5e59aa01c02a230c93cd00 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 2 Jul 2026 14:58:26 +0800 Subject: [PATCH 62/72] feat(autogen): add GitHub Android package generation --- crates/getter-cli/src/lib.rs | 292 ++++++- crates/getter-cli/tests/bdd_cli.rs | 136 ++++ .../features/cli/autogen_installed.feature | 19 + crates/getter-core/src/autogen.rs | 12 + .../getter-operations/src/github_autogen.rs | 733 ++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + 6 files changed, 1192 insertions(+), 1 deletion(-) create mode 100644 crates/getter-operations/src/github_autogen.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index ab22fd9..b062204 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -23,6 +23,7 @@ use getter_downloader::{ }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; use getter_operations::fdroid_autogen; +use getter_operations::github_autogen; use getter_operations::github_latest_commit::{self, GithubLatestCommitOperationError}; use getter_operations::github_releases::{self, GithubReleaseOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; @@ -138,6 +139,20 @@ pub enum CliCommand { preview: PathBuf, acceptance: AutogenAcceptance, }, + AutogenGithubPreview { + owner: String, + repo: String, + android_package: String, + display_name: Option, + releases: PathBuf, + asset_include: Option, + asset_exclude: Option, + include_prereleases: bool, + }, + AutogenGithubApply { + preview: PathBuf, + acceptance: AutogenAcceptance, + }, ProviderGithubReleases { owner: String, repo: String, @@ -603,6 +618,32 @@ where acceptance: parse_autogen_acceptance(rest)?, } } + [domain, subject, action, rest @ ..] + if domain == "autogen" && subject == "github" && action == "preview" => + { + let args = parse_autogen_github_preview_args(rest)?; + CliCommand::AutogenGithubPreview { + owner: args.owner, + repo: args.repo, + android_package: args.android_package, + display_name: args.display_name, + releases: args.releases, + asset_include: args.asset_include, + asset_exclude: args.asset_exclude, + include_prereleases: args.include_prereleases, + } + } + [domain, subject, action, flag, preview, rest @ ..] + if domain == "autogen" + && subject == "github" + && action == "apply" + && flag == "--preview" => + { + CliCommand::AutogenGithubApply { + preview: PathBuf::from(preview), + acceptance: parse_autogen_acceptance(rest)?, + } + } [domain, provider, action, rest @ ..] if domain == "provider" && provider == "github" && action == "releases" => { @@ -869,6 +910,53 @@ fn execute(invocation: CliInvocation) -> Result { ) .map_err(CliError::from) } + CliCommand::AutogenGithubPreview { + owner, + repo, + android_package, + display_name, + releases, + asset_include, + asset_exclude, + include_prereleases, + } => { + let db = open_main_db(&invocation.data_dir)?; + let cache_db = open_cache_db(&invocation.data_dir)?; + let releases_json = read_github_releases_fixture(&releases)?; + let request = json!({ + "owner": owner, + "repo": repo, + "android_package": android_package, + "display_name": display_name, + "releases_json": releases_json, + "include_prereleases": include_prereleases, + "asset": { + "include": asset_include, + "exclude": asset_exclude, + }, + }); + github_autogen::preview_github_android_package_json( + &invocation.data_dir, + &db, + &cache_db, + &request.to_string(), + ) + .map_err(CliError::from) + } + CliCommand::AutogenGithubApply { + preview, + acceptance, + } => { + let db = open_main_db(&invocation.data_dir)?; + let preview = read_autogen_preview(&preview, "github.autogen.preview")?; + github_autogen::apply_github_preview_json( + &invocation.data_dir, + &db, + &preview, + &acceptance, + ) + .map_err(CliError::from) + } CliCommand::ProviderGithubReleases { owner, repo, @@ -1137,6 +1225,18 @@ fn parse_fdroid_autogen_preview_args( Ok((index, inventory, package_names)) } +#[derive(Debug, Default, PartialEq, Eq)] +struct AutogenGithubPreviewArgs { + owner: String, + repo: String, + android_package: String, + display_name: Option, + releases: PathBuf, + asset_include: Option, + asset_exclude: Option, + include_prereleases: bool, +} + #[derive(Debug, Default, PartialEq, Eq)] struct ProviderGithubReleasesArgs { owner: String, @@ -1157,6 +1257,128 @@ struct ProviderGithubLatestCommitArgs { refresh: bool, } +fn parse_autogen_github_preview_args( + args: &[String], +) -> Result { + let mut parsed = AutogenGithubPreviewArgs::default(); + let mut position = 0; + while position < args.len() { + match args[position].as_str() { + "--owner" => { + parsed.owner = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "autogen github preview --owner requires an owner".to_owned(), + ) + })? + .clone(); + position += 2; + } + "--repo" => { + parsed.repo = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage("autogen github preview --repo requires a repo".to_owned()) + })? + .clone(); + position += 2; + } + "--android-package" => { + parsed.android_package = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "autogen github preview --android-package requires a package name" + .to_owned(), + ) + })? + .clone(); + position += 2; + } + "--display-name" => { + parsed.display_name = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "autogen github preview --display-name requires a name".to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--releases" => { + let path = args.get(position + 1).ok_or_else(|| { + CliError::Usage( + "autogen github preview --releases requires a fixture path".to_owned(), + ) + })?; + parsed.releases = PathBuf::from(path); + position += 2; + } + "--asset-include" => { + parsed.asset_include = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "autogen github preview --asset-include requires a regex" + .to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--asset-exclude" => { + parsed.asset_exclude = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "autogen github preview --asset-exclude requires a regex" + .to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--include-prereleases" => { + parsed.include_prereleases = true; + position += 1; + } + other => { + return Err(CliError::Usage(format!( + "unsupported autogen github preview argument '{other}'" + ))) + } + } + } + + if parsed.owner.trim().is_empty() { + return Err(CliError::Usage( + "autogen github preview requires --owner ".to_owned(), + )); + } + if parsed.repo.trim().is_empty() { + return Err(CliError::Usage( + "autogen github preview requires --repo ".to_owned(), + )); + } + if parsed.android_package.trim().is_empty() { + return Err(CliError::Usage( + "autogen github preview requires --android-package ".to_owned(), + )); + } + if parsed.releases.as_os_str().is_empty() { + return Err(CliError::Usage( + "autogen github preview requires --releases ".to_owned(), + )); + } + + Ok(parsed) +} + fn parse_provider_github_releases_args( args: &[String], ) -> Result { @@ -2027,7 +2249,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -2096,6 +2318,8 @@ impl CliCommand { Self::AutogenCleanupApply { .. } => "autogen cleanup apply", Self::AutogenFdroidPreview { .. } => "autogen fdroid preview", Self::AutogenFdroidApply { .. } => "autogen fdroid apply", + Self::AutogenGithubPreview { .. } => "autogen github preview", + Self::AutogenGithubApply { .. } => "autogen github apply", Self::ProviderGithubReleases { .. } => "provider github releases", Self::ProviderGithubLatestCommit { .. } => "provider github latest-commit", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", @@ -2188,6 +2412,72 @@ mod tests { ); } + #[test] + fn parses_autogen_github_preview_and_apply_commands() { + let preview = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "autogen", + "github", + "preview", + "--owner", + "DUpdateSystem", + "--repo", + "UpgradeAll", + "--android-package", + "net.xzos.upgradeall", + "--display-name", + "UpgradeAll", + "--releases", + "/tmp/releases.json", + "--asset-include", + "UpgradeAll_.*[.]apk$", + "--asset-exclude", + "debug", + "--include-prereleases", + ]) + .unwrap(); + assert_eq!( + preview.command, + CliCommand::AutogenGithubPreview { + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + android_package: "net.xzos.upgradeall".to_owned(), + display_name: Some("UpgradeAll".to_owned()), + releases: PathBuf::from("/tmp/releases.json"), + asset_include: Some("UpgradeAll_.*[.]apk$".to_owned()), + asset_exclude: Some("debug".to_owned()), + include_prereleases: true, + } + ); + + let apply = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "autogen", + "github", + "apply", + "--preview", + "/tmp/preview.json", + "--accept", + "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall", + ]) + .unwrap(); + assert_eq!( + apply.command, + CliCommand::AutogenGithubApply { + preview: PathBuf::from("/tmp/preview.json"), + acceptance: AutogenAcceptance::Accept(vec![ + "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall" + .parse() + .unwrap(), + ]), + } + ); + } + #[test] fn provider_github_latest_commit_missing_cli_flags_is_usage_error() { let output = run([ diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index c0d8348..6a38e3c 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -241,6 +241,18 @@ fn fixture_github_releases_response(world: &mut CliWorld, project: String) { world.github_releases = Some(releases); } +#[given(expr = "the committed GitHub releases snapshot for {string}")] +fn committed_github_releases_snapshot(world: &mut CliWorld, _project: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let releases = temp.path().join("github-releases-snapshot.json"); + fs::write( + &releases, + include_str!("../../../tests/files/web/github_api_release.json"), + ) + .expect("write committed GitHub releases snapshot"); + world.github_releases = Some(releases); +} + #[given(expr = "a fixture GitHub commit response for {string}")] fn fixture_github_commit_response(world: &mut CliWorld, project: String) { let temp = world.temp.as_ref().expect("tempdir exists"); @@ -1010,6 +1022,49 @@ fn run_getter_autogen_fdroid_preview_for_inventory(world: &mut CliWorld) { world.json = None; } +#[when( + expr = "I run getter autogen github preview for owner {string} repo {string} android package {string}" +)] +fn run_getter_autogen_github_preview( + world: &mut CliWorld, + owner: String, + repo: String, + android_package: String, +) { + let releases = world + .github_releases + .as_ref() + .expect("GitHub releases fixture exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "github".to_owned(), + "preview".to_owned(), + "--owner".to_owned(), + owner, + "--repo".to_owned(), + repo.clone(), + "--android-package".to_owned(), + android_package, + "--display-name".to_owned(), + "UpgradeAll".to_owned(), + "--releases".to_owned(), + releases.to_string_lossy().to_string(), + "--asset-include".to_owned(), + if repo == "UpgradeAll" { + "UpgradeAll_.*[.]apk$".to_owned() + } else { + r"\.apk$".to_owned() + }, + "--asset-exclude".to_owned(), + "debug".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when(expr = "I run getter provider github releases for owner {string} repo {string}")] fn run_getter_provider_github_releases(world: &mut CliWorld, owner: String, repo: String) { let releases = world @@ -1062,6 +1117,27 @@ fn run_getter_provider_github_latest_commit(world: &mut CliWorld, owner: String, world.json = None; } +#[when("I run getter autogen github apply for that preview with accept-all")] +fn run_getter_autogen_github_apply_accept_all(world: &mut CliWorld) { + let preview = world + .autogen_preview + .as_ref() + .expect("autogen preview exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "github".to_owned(), + "apply".to_owned(), + "--preview".to_owned(), + preview.to_string_lossy().to_string(), + "--accept-all".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen fdroid apply for that preview with accept-all")] fn run_getter_autogen_fdroid_apply_accept_all(world: &mut CliWorld) { let preview = world @@ -1329,6 +1405,30 @@ fn runtime_script_output_removes_completed_task(world: &mut CliWorld) { assert_eq!(steps[5]["data"]["tasks"].as_array().unwrap().len(), 0); } +#[then( + expr = "the provider-backed GitHub update check selects version {string} with artifact {string}" +)] +fn provider_backed_github_update_check_selects_version_with_artifact( + world: &mut CliWorld, + version: String, + artifact_name: String, +) { + let json = current_json(world); + assert_eq!(json["command"], "runtime script"); + let steps = json["data"]["steps"].as_array().expect("runtime steps"); + assert_eq!(steps.len(), 1); + assert_eq!(steps[0]["operation"], "update_check_package_issue_action"); + let data = &steps[0]["data"]; + assert_eq!(data["provider_calls"][0]["provider"], "github"); + assert_eq!(data["provider_calls"][0]["source"], "cache"); + assert_eq!(data["update"]["status"], "update_available"); + assert_eq!(data["update"]["selected"]["candidate"]["version"], version); + assert_eq!( + data["update"]["selected"]["candidate"]["artifacts"][0]["name"], + artifact_name + ); +} + #[then(expr = "the provider-backed F-Droid update check selects version_code {int}")] fn provider_backed_fdroid_update_check_selects_version_code( world: &mut CliWorld, @@ -1617,6 +1717,23 @@ fn update_check_has_no_selected_update(world: &mut CliWorld) { assert_eq!(json["data"]["actions"], Value::Array(Vec::new())); } +#[then(expr = "the GitHub autogen preview contains candidate {string}")] +fn github_autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen github preview"); + assert_eq!(json["data"]["operation"], "github.autogen.preview"); + let candidates = json["data"]["candidates"] + .as_array() + .expect("candidates array"); + assert!( + candidates + .iter() + .any(|candidate| candidate["package_id"].as_str() == Some(package_id.as_str())), + "preview should contain {package_id}: {candidates:?}" + ); +} + #[then(expr = "the F-Droid autogen preview contains candidate {string}")] fn fdroid_autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { let json = current_json(world); @@ -1726,6 +1843,25 @@ fn github_latest_commit_provider_returns_live_revision(world: &mut CliWorld, rev assert!(json["data"]["diagnostics"].as_array().unwrap().is_empty()); } +#[then(expr = "the autogen repository contains generated GitHub package {string}")] +fn autogen_repository_contains_generated_github_package(world: &mut CliWorld, package_id: String) { + autogen_repository_contains_generated_package(world, package_id.clone()); + let path = autogen_repo_path(world).join(package_relative_path(&package_id)); + let content = fs::read_to_string(path.join("9999.lua")).expect("generated package readable"); + assert!(content.contains("local github_android = require(\"luaclass.github_android_apk\")")); + assert!(content.contains("return github_android.package")); + assert!(content.contains("android_package = \"net.xzos.upgradeall\"")); + assert!(content.contains("owner = \"DUpdateSystem\"")); + assert!(content.contains("repo = \"UpgradeAll\"")); + assert!(!content.contains("releases_json")); + assert!(!content.contains("getter.provider")); + let manifest = fs::read_to_string(path.join("Manifest")).expect("generated Manifest readable"); + assert!( + manifest.contains("github-releases.json"), + "generated GitHub Manifest should name provider source provenance: {manifest}" + ); +} + #[then(expr = "the autogen repository contains generated F-Droid package {string}")] fn autogen_repository_contains_generated_fdroid_package(world: &mut CliWorld, package_id: String) { autogen_repository_contains_generated_package(world, package_id.clone()); diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 2c90255..e9c1094 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -25,6 +25,25 @@ Feature: Installed app autogen And the output is valid JSON And the provider-backed F-Droid update check selects version_code 1020000 + Scenario: User applies GitHub Android APK autogen and validates the generated package directory + Given an initialized getter data directory + And the committed GitHub releases snapshot for "DUpdateSystem/UpgradeAll" + When I run getter autogen github preview for owner "DUpdateSystem" repo "UpgradeAll" android package "net.xzos.upgradeall" + Then the command succeeds + And the output is valid JSON + And the GitHub autogen preview contains candidate "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall" + And the autogen repository has not been written + And I save the autogen preview to a file + When I run getter autogen github apply for that preview with accept-all + Then the command succeeds + And the autogen repository contains generated GitHub package "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall" + And the app list contains autogen tracked package "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall" + Given a runtime script checking generated package "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall" at "0.12.0" + When I run getter runtime script for that script + Then the command succeeds + And the output is valid JSON + And the provider-backed GitHub update check selects version "0.13-beta.4" with artifact "UpgradeAll_0.13-beta.4.apk" + Scenario: User previews F-Droid autogen from installed inventory Given an initialized getter data directory And a fixture F-Droid catalog index with package "org.fdroid.fdroid" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index c8b7eae..8c34680 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -16,6 +16,7 @@ pub const AUTOGEN_RECORD_FILE: &str = ".autogen.jsonc"; pub const AUTOGEN_RECORD_VERSION: u32 = 1; pub const INSTALLED_AUTOGEN_GENERATOR: &str = "installed-inventory"; pub const FDROID_AUTOGEN_GENERATOR: &str = "fdroid-catalog"; +pub const GITHUB_AUTOGEN_GENERATOR: &str = "github-releases"; pub const DEFAULT_AUTOGEN_REPOSITORY_ID: &str = "autogen"; pub const DEFAULT_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Autogen"; pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter autogen"; @@ -117,6 +118,17 @@ pub enum AutogenRecordInput { endpoint_url: String, package_name: String, }, + GithubAndroidApk { + owner: String, + repo: String, + android_package: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + asset_include: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + asset_exclude: Option, + #[serde(default)] + include_prereleases: bool, + }, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/crates/getter-operations/src/github_autogen.rs b/crates/getter-operations/src/github_autogen.rs new file mode 100644 index 0000000..988a334 --- /dev/null +++ b/crates/getter-operations/src/github_autogen.rs @@ -0,0 +1,733 @@ +//! Fixture-backed GitHub Android APK autogen preview/apply operations. +//! +//! This module generates ordinary package directories that use the standard +//! `luaclass.github_android_apk` provider module. It consumes cached or +//! fixture-backed GitHub release facts owned by getter and deliberately avoids +//! live GitHub transport or Flutter/Kotlin provider parsing. + +use crate::autogen::{self, AutogenAcceptance, AutogenOperationError, AutogenOperationResult}; +use crate::github_releases::{ + read_or_refresh_github_releases, GithubReleaseConfig, DEFAULT_GITHUB_API_BASE_URL, + GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, +}; +use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource}; +use getter_core::autogen::{ + content_hash, package_relative_path, record_file_key, render_autogen_record, AutogenRecord, + AutogenRecordInput, GeneratedPackageFile, AUTOGEN_RECORD_VERSION, GITHUB_AUTOGEN_GENERATOR, +}; +use getter_core::{InstalledTarget, PackageId, PackageKind}; +use getter_providers::{ + github_release_update_candidates, GithubAssetFilter, GithubRelease, + GithubReleaseCandidateOptions, +}; +use getter_storage::{CacheDb, MainDb}; +use serde::Deserialize; +use serde_json::{json, Value}; +use std::path::{Path, PathBuf}; + +pub fn preview_github_android_package_json( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + request_json: &str, +) -> AutogenOperationResult { + let request: GithubAutogenPreviewRequest = + serde_json::from_str(request_json).map_err(|source| { + AutogenOperationError::Autogen(format!( + "invalid GitHub autogen preview request: {source}" + )) + })?; + let owner = required_segment(request.owner, "owner")?; + let repo = required_segment(request.repo, "repo")?; + let android_package = required_non_empty(request.android_package, "android_package")?; + let display_name = request + .display_name + .as_deref() + .map(str::trim) + .filter(|value| !value.is_empty()) + .unwrap_or(&repo) + .to_owned(); + let api_base_url = request + .api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()); + if api_base_url != DEFAULT_GITHUB_API_BASE_URL { + return Err(AutogenOperationError::Autogen(format!( + "GitHub provider-module autogen currently supports only API base URL '{DEFAULT_GITHUB_API_BASE_URL}', got '{api_base_url}'" + ))); + } + let mode = match request.mode.as_deref() { + Some("force_refresh") => ProviderCacheMode::ForceRefresh, + Some("use_cached") | None => ProviderCacheMode::UseCached, + Some(other) => { + return Err(AutogenOperationError::Autogen(format!( + "unknown GitHub autogen preview mode '{other}'" + ))) + } + }; + let asset_filter = request.asset.unwrap_or_default().into_filter(); + let options = GithubReleaseCandidateOptions { + include_prereleases: request.include_prereleases, + asset_filter: asset_filter.clone(), + }; + let package_id = github_android_package_id(&owner, &repo, &android_package)?; + let result = read_or_refresh_github_releases( + cache_db, + GithubReleaseConfig { + api_base_url, + owner: owner.clone(), + repo: repo.clone(), + }, + mode, + || { + request.releases_json.ok_or_else(|| { + "fixture-backed GitHub autogen refresh requires releases_json".to_owned() + }) + }, + ) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + let (target_alias, target_path, target_priority) = + autogen::generated_repository_config(data_dir)?; + let covered = + autogen::higher_priority_package_coverage(main_db, &target_alias, target_priority)?; + let mut diagnostics: Vec = result + .diagnostics + .iter() + .map(provider_diagnostic_json) + .collect(); + let mut candidates = Vec::new(); + let mut skipped = Vec::new(); + + if let Some(repository_id) = covered.get(&package_id) { + skipped.push(json!({ + "package_id": package_id.to_string(), + "reason": "covered_by_higher_priority_repo", + "covering_repo_id": repository_id.as_str(), + })); + } else { + let update_candidates = github_release_update_candidates(&result.releases, &options) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + if update_candidates.is_empty() + && github_eligible_release_count(&result.releases, &options) > 0 + { + diagnostics.push(json!({ + "code": GITHUB_ASSET_NOT_FOUND, + "message": "no GitHub release assets matched the requested filters", + "provider": GITHUB_PROVIDER_ID, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + })); + skipped.push(json!({ + "package_id": package_id.to_string(), + "reason": "provider_asset_not_found", + })); + } else if update_candidates.is_empty() { + diagnostics.push(json!({ + "code": "provider.github.release_not_found", + "message": format!("GitHub repository '{owner}/{repo}' has no eligible releases"), + "provider": GITHUB_PROVIDER_ID, + "owner": owner, + "repo": repo, + "cache_key": result.cache_key, + })); + skipped.push(json!({ + "package_id": package_id.to_string(), + "reason": "provider_release_not_found", + })); + } else { + candidates.push(github_candidate_json( + &result.source_response_sha512, + &package_id, + GithubGeneratedPackageInput { + owner: &owner, + repo: &repo, + android_package: &android_package, + display_name: &display_name, + asset_filter: &asset_filter, + include_prereleases: request.include_prereleases, + }, + )?); + } + } + + Ok(json!({ + "operation": "github.autogen.preview", + "provider": GITHUB_PROVIDER_ID, + "api_base_url": result.config.api_base_url, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "target_repo_id": target_alias.as_str(), + "target_repo_path": target_path, + "summary": { + "candidate_count": candidates.len(), + "skipped_count": skipped.len(), + "write_count": candidates.len(), + "delete_count": 0, + }, + "candidates": candidates, + "skipped": skipped, + "diagnostics": diagnostics, + })) +} + +pub fn apply_github_preview_json( + data_dir: &Path, + main_db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> AutogenOperationResult { + autogen::apply_preview( + data_dir, + main_db, + preview, + acceptance, + "github.autogen.preview", + GITHUB_AUTOGEN_GENERATOR, + ) +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct GithubAutogenPreviewRequest { + #[serde(default)] + api_base_url: Option, + #[serde(default)] + owner: Option, + #[serde(default)] + repo: Option, + #[serde(default)] + android_package: Option, + #[serde(default)] + display_name: Option, + #[serde(default)] + asset: Option, + #[serde(default)] + include_prereleases: bool, + #[serde(default)] + mode: Option, + #[serde(default)] + releases_json: Option, +} + +#[derive(Debug, Default, Deserialize)] +#[serde(deny_unknown_fields)] +struct GithubAssetFilterRequest { + #[serde(default)] + include: Option, + #[serde(default)] + exclude: Option, +} + +impl GithubAssetFilterRequest { + fn into_filter(self) -> GithubAssetFilter { + GithubAssetFilter { + include: self + .include + .map(|value| value.trim().to_owned()) + .filter(|value| !value.is_empty()) + .or_else(|| Some("[.]apk$".to_owned())), + exclude: self + .exclude + .map(|value| value.trim().to_owned()) + .filter(|value| !value.is_empty()), + } + } +} + +struct GithubGeneratedPackageInput<'a> { + owner: &'a str, + repo: &'a str, + android_package: &'a str, + display_name: &'a str, + asset_filter: &'a GithubAssetFilter, + include_prereleases: bool, +} + +fn github_candidate_json( + source_response_sha512: &[String], + package_id: &PackageId, + input: GithubGeneratedPackageInput<'_>, +) -> AutogenOperationResult { + let relative_path = package_relative_path(package_id); + let files = github_generated_files(source_response_sha512, &input)?; + let record = AutogenRecord { + version: AUTOGEN_RECORD_VERSION, + generator: GITHUB_AUTOGEN_GENERATOR.to_owned(), + package_id: package_id.clone(), + output_relative_path: relative_path.clone(), + input: AutogenRecordInput::GithubAndroidApk { + owner: input.owner.to_owned(), + repo: input.repo.to_owned(), + android_package: input.android_package.to_owned(), + asset_include: input.asset_filter.include.clone(), + asset_exclude: input.asset_filter.exclude.clone(), + include_prereleases: input.include_prereleases, + }, + files: files + .iter() + .map(|file| { + ( + record_file_key(&file.relative_path), + file.content_hash.clone(), + ) + }) + .collect(), + }; + let record_content = render_autogen_record(&record) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + let content_hash = content_hash(&record_content); + let file_json: Vec = files + .iter() + .map(|file| { + json!({ + "relative_path": file.relative_path, + "content_hash": file.content_hash, + "content": file.content, + }) + }) + .collect(); + + Ok(json!({ + "package_id": package_id.to_string(), + "kind": package_id.kind().as_str(), + "display_name": input.display_name, + "installed_target": InstalledTarget::AndroidPackage { package_name: input.android_package.to_owned() }, + "action": "create", + "output_relative_path": relative_path, + "content_hash": content_hash, + "content": record_content, + "autogen_record_content": record_content, + "files": file_json, + "provider": GITHUB_PROVIDER_ID, + "upstream_id": format!("{}/{}", input.owner, input.repo), + })) +} + +fn github_generated_files( + source_response_sha512: &[String], + input: &GithubGeneratedPackageInput<'_>, +) -> AutogenOperationResult> { + if source_response_sha512.is_empty() { + return Err(AutogenOperationError::Autogen( + "GitHub provider-module autogen requires provider source provenance for Manifest generation" + .to_owned(), + )); + } + let metadata = render_pretty_json(json!({ + "type": "android:app", + "display_name": input.display_name, + "android": { "package_name": input.android_package }, + }))?; + let manifest = github_manifest(source_response_sha512); + let version_lua = github_version_lua(input); + Ok(vec![ + generated_file("metadata.jsonc", metadata), + generated_file("Manifest", manifest), + generated_file("9999.lua", version_lua), + ]) +} + +fn github_manifest(source_response_sha512: &[String]) -> String { + source_response_sha512 + .iter() + .map(|digest| format!("{digest} github-releases.json\n")) + .collect() +} + +fn github_version_lua(input: &GithubGeneratedPackageInput<'_>) -> String { + let mut fields = vec![ + format!(" name = {},", lua_string(input.display_name)), + format!(" android_package = {},", lua_string(input.android_package)), + format!(" owner = {},", lua_string(input.owner)), + format!(" repo = {},", lua_string(input.repo)), + ]; + if input.asset_filter.include.is_some() || input.asset_filter.exclude.is_some() { + fields.push(" asset = {".to_owned()); + if let Some(include) = &input.asset_filter.include { + fields.push(format!(" include = {},", lua_string(include))); + } + if let Some(exclude) = &input.asset_filter.exclude { + fields.push(format!(" exclude = {},", lua_string(exclude))); + } + fields.push(" },".to_owned()); + } + if input.include_prereleases { + fields.push(" include_prereleases = true,".to_owned()); + } + format!( + "#!/bin/upa-lua v1\n-- @generated by UpgradeAll getter autogen (GitHub release provider module)\nlocal github_android = require(\"luaclass.github_android_apk\")\n\nreturn github_android.package {{\n{}\n}}\n", + fields.join("\n"), + ) +} + +fn github_android_package_id( + owner: &str, + repo: &str, + android_package: &str, +) -> AutogenOperationResult { + PackageId::new( + PackageKind::Android, + format!("github/{owner}/{repo}/{android_package}"), + ) + .map_err(|source| AutogenOperationError::Autogen(source.to_string())) +} + +fn render_pretty_json(value: Value) -> AutogenOperationResult { + serde_json::to_string_pretty(&value) + .map(|mut content| { + content.push('\n'); + content + }) + .map_err(|source| AutogenOperationError::Autogen(source.to_string())) +} + +fn generated_file(relative_path: &str, content: String) -> GeneratedPackageFile { + GeneratedPackageFile { + relative_path: PathBuf::from(relative_path), + content_hash: content_hash(&content), + content, + } +} + +fn lua_string(value: &str) -> String { + let mut escaped = String::from("\""); + for ch in value.chars() { + match ch { + '\\' => escaped.push_str("\\\\"), + '"' => escaped.push_str("\\\""), + '\n' => escaped.push_str("\\n"), + '\r' => escaped.push_str("\\r"), + '\t' => escaped.push_str("\\t"), + other => escaped.push(other), + } + } + escaped.push('"'); + escaped +} + +fn required_segment(value: Option, field: &'static str) -> AutogenOperationResult { + let value = required_non_empty(value, field)?; + if value.contains('/') { + return Err(AutogenOperationError::Autogen(format!( + "GitHub autogen {field} must not contain '/'" + ))); + } + Ok(value) +} + +fn required_non_empty( + value: Option, + field: &'static str, +) -> AutogenOperationResult { + value + .map(|value| value.trim().to_owned()) + .filter(|value| !value.is_empty()) + .ok_or_else(|| AutogenOperationError::Autogen(format!("missing {field}"))) +} + +fn github_eligible_release_count( + releases: &[GithubRelease], + options: &GithubReleaseCandidateOptions, +) -> usize { + releases + .iter() + .filter(|release| !release.draft) + .filter(|release| options.include_prereleases || !release.prerelease) + .count() +} + +fn provider_source_json(source: ProviderCacheSource) -> &'static str { + match source { + ProviderCacheSource::Cache => "cache", + ProviderCacheSource::Refreshed => "refreshed", + ProviderCacheSource::Stale => "stale", + } +} + +fn provider_diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + #[cfg(feature = "lua")] + use getter_core::repository::{PackageTypeMetadata, RepositoryPackageDirectoryLayout}; + use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; + #[cfg(feature = "lua")] + use getter_core::runtime::GetterRuntime; + use getter_core::RepositoryPriority; + use serde_json::{json, Value}; + use sha2::{Digest, Sha512}; + + const UPGRADEALL_GITHUB_RELEASES: &str = + include_str!("../../../tests/files/web/github_api_release.json"); + const UPGRADEALL_PACKAGE_ID: &str = + "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall"; + + #[test] + fn preview_generates_github_android_package_directory_from_release_snapshot() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + + let preview = preview_github_android_package_json( + temp.path(), + &main_db, + &cache_db, + &upgradeall_request().to_string(), + ) + .unwrap(); + + assert_eq!(preview["operation"], "github.autogen.preview"); + assert_eq!(preview["provider"], "github"); + assert_eq!(preview["owner"], "DUpdateSystem"); + assert_eq!(preview["repo"], "UpgradeAll"); + assert_eq!(preview["target_repo_id"], "autogen"); + assert_eq!(preview["source"], "refreshed"); + assert_eq!(preview["candidates"].as_array().unwrap().len(), 1); + let candidate = &preview["candidates"][0]; + assert_eq!(candidate["package_id"], UPGRADEALL_PACKAGE_ID); + assert_eq!(candidate["output_relative_path"], UPGRADEALL_PACKAGE_ID); + assert_eq!(candidate["display_name"], "UpgradeAll"); + assert_eq!( + candidate["installed_target"]["package_name"], + "net.xzos.upgradeall" + ); + assert_eq!(candidate["provider"], "github"); + assert_eq!(candidate["upstream_id"], "DUpdateSystem/UpgradeAll"); + + let record: Value = + serde_json::from_str(candidate["autogen_record_content"].as_str().unwrap()).unwrap(); + assert_eq!(record["generator"], "github-releases"); + assert_eq!(record["input"]["kind"], "github_android_apk"); + assert_eq!(record["input"]["owner"], "DUpdateSystem"); + assert_eq!(record["input"]["repo"], "UpgradeAll"); + assert_eq!(record["input"]["android_package"], "net.xzos.upgradeall"); + assert_eq!(record["input"]["asset_include"], "UpgradeAll_.*[.]apk$"); + assert_eq!(record["input"]["include_prereleases"], false); + + let files = candidate["files"].as_array().unwrap(); + let version_lua = generated_file_content(files, "9999.lua"); + assert!( + version_lua.contains("local github_android = require(\"luaclass.github_android_apk\")") + ); + assert!(version_lua.contains("return github_android.package")); + assert!(version_lua.contains("name = \"UpgradeAll\"")); + assert!(version_lua.contains("android_package = \"net.xzos.upgradeall\"")); + assert!(version_lua.contains("owner = \"DUpdateSystem\"")); + assert!(version_lua.contains("repo = \"UpgradeAll\"")); + assert!(version_lua.contains("include = \"UpgradeAll_.*[.]apk$\"")); + assert!(!version_lua.contains("releases_json")); + assert!(!version_lua.contains("getter.provider")); + assert!(!version_lua.contains("getter_dev")); + let manifest = generated_file_content(files, "Manifest"); + assert_eq!( + manifest, + format!( + "{} github-releases.json\n", + sha512_hex(UPGRADEALL_GITHUB_RELEASES.as_bytes()) + ) + ); + assert!(!temp.path().join("repo/autogen").exists()); + } + + #[cfg(feature = "lua")] + #[test] + fn apply_writes_valid_github_package_directory_and_runtime_uses_cache() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let preview = preview_github_android_package_json( + temp.path(), + &main_db, + &cache_db, + &upgradeall_request().to_string(), + ) + .unwrap(); + + let result = apply_github_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap(); + + assert_eq!(result["applied_count"], 1); + let repo_root = temp.path().join("repo/autogen"); + let package_dir = repo_root.join(UPGRADEALL_PACKAGE_ID); + assert!(package_dir.join("metadata.jsonc").is_file()); + assert!(package_dir.join("Manifest").is_file()); + assert!(package_dir.join("9999.lua").is_file()); + assert!(package_dir.join(".autogen.jsonc").is_file()); + let layout = RepositoryPackageDirectoryLayout::load(&repo_root).unwrap(); + let package_dir = layout + .package(&UPGRADEALL_PACKAGE_ID.parse().unwrap()) + .unwrap(); + let metadata = layout.package_metadata(package_dir).unwrap(); + assert!(matches!( + &metadata.package, + PackageTypeMetadata::AndroidApp { android } + if android.package_name == "net.xzos.upgradeall" + )); + + let mut runtime = GetterRuntime::new(); + let issued = crate::runtime::issue_action_from_registered_package_json( + &mut runtime, + temp.path(), + &main_db, + &json!({ + "package_id": UPGRADEALL_PACKAGE_ID, + "installed_version": "0.12.0" + }) + .to_string(), + ) + .unwrap(); + assert_eq!(issued["provider_calls"][0]["provider"], "github"); + assert_eq!(issued["provider_calls"][0]["owner"], "DUpdateSystem"); + assert_eq!(issued["provider_calls"][0]["repo"], "UpgradeAll"); + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "0.13-beta.4" + ); + assert_eq!( + issued["update"]["selected"]["candidate"]["artifacts"][0]["name"], + "UpgradeAll_0.13-beta.4.apk" + ); + assert_eq!( + issued["update"]["selected"]["candidate"]["artifacts"][0]["content_type"], + "application/vnd.android.package-archive" + ); + assert!(issued["update"]["selected"]["candidate"]["changelog"] + .as_str() + .unwrap() + .contains("Ukrainian")); + } + + #[test] + fn provider_module_generation_requires_source_provenance() { + let asset_filter = GithubAssetFilter { + include: Some("[.]apk$".to_owned()), + exclude: None, + }; + let error = github_generated_files( + &[], + &GithubGeneratedPackageInput { + owner: "DUpdateSystem", + repo: "UpgradeAll", + android_package: "net.xzos.upgradeall", + display_name: "UpgradeAll", + asset_filter: &asset_filter, + include_prereleases: false, + }, + ) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("requires provider source provenance")) + ); + } + + #[test] + fn preview_rejects_custom_github_api_base_url_generation() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let mut request = upgradeall_request(); + request["api_base_url"] = json!("https://example.invalid/api"); + + let error = preview_github_android_package_json( + temp.path(), + &main_db, + &cache_db, + &request.to_string(), + ) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("supports only API base URL")) + ); + } + + #[test] + fn preview_skips_github_package_covered_by_higher_priority_repo() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let official_root = temp.path().join("repo/official"); + let package_dir = official_root.join(UPGRADEALL_PACKAGE_ID); + std::fs::create_dir_all(&package_dir).unwrap(); + std::fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "net.xzos.upgradeall" } }"#, + ) + .unwrap(); + main_db + .upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::DEFAULT, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&official_root), + None, + ) + .unwrap(); + + let preview = preview_github_android_package_json( + temp.path(), + &main_db, + &cache_db, + &upgradeall_request().to_string(), + ) + .unwrap(); + + assert!(preview["candidates"].as_array().unwrap().is_empty()); + assert_eq!(preview["skipped"][0]["package_id"], UPGRADEALL_PACKAGE_ID); + assert_eq!( + preview["skipped"][0]["reason"], + "covered_by_higher_priority_repo" + ); + assert_eq!(preview["skipped"][0]["covering_repo_id"], "official"); + } + + fn upgradeall_request() -> Value { + json!({ + "owner": "DUpdateSystem", + "repo": "UpgradeAll", + "android_package": "net.xzos.upgradeall", + "display_name": "UpgradeAll", + "asset": { + "include": "UpgradeAll_.*[.]apk$" + }, + "releases_json": UPGRADEALL_GITHUB_RELEASES, + }) + } + + fn generated_file_content<'a>(files: &'a [Value], relative_path: &str) -> &'a str { + files + .iter() + .find(|file| file["relative_path"] == relative_path) + .and_then(|file| file["content"].as_str()) + .unwrap_or_else(|| panic!("generated file {relative_path} not found: {files:?}")) + } + + fn sha512_hex(body: &[u8]) -> String { + let mut hasher = Sha512::new(); + hasher.update(body); + format!("{:x}", hasher.finalize()) + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 8d7fc30..1851139 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -8,6 +8,7 @@ pub mod autogen; pub mod fdroid_autogen; pub mod fdroid_catalog; +pub mod github_autogen; pub mod github_latest_commit; pub mod github_releases; pub mod legacy_room; From 1f7b181a127527fce53f10381502d8b9f600cc1a Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 2 Jul 2026 18:04:38 +0800 Subject: [PATCH 63/72] feat(provider): refresh GitHub releases through live transport --- Cargo.lock | 117 ++++ Cargo.toml | 2 +- crates/getter-cli/src/lib.rs | 82 ++- crates/getter-operations/Cargo.toml | 2 + .../getter-operations/src/github_releases.rs | 540 +++++++++++++++++- .../src/lua_provider_host.rs | 41 +- crates/getter-operations/src/runtime.rs | 269 ++++++++- 7 files changed, 988 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a65d2b4..b1df2b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "ahash" version = "0.8.12" @@ -85,6 +91,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "2.13.0" @@ -247,6 +259,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -430,6 +451,16 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "futures" version = "0.3.32" @@ -624,6 +655,7 @@ dependencies = [ "sha2", "tempfile", "thiserror 1.0.69", + "ureq", ] [[package]] @@ -734,6 +766,22 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + [[package]] name = "humantime" version = "2.3.0" @@ -921,6 +969,16 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.1" @@ -1057,6 +1115,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pin-project" version = "1.1.13" @@ -1242,7 +1306,9 @@ version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ + "log", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -1447,6 +1513,12 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + [[package]] name = "slab" version = "0.4.12" @@ -1744,6 +1816,42 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" +dependencies = [ + "base64", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "ureq-proto", + "utf8-zero", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1787,6 +1895,15 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webpki-roots" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "7.0.3" diff --git a/Cargo.toml b/Cargo.toml index ba37ca3..400474f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ default = ["cli", "domain"] domain = ["dep:getter-core", "dep:getter-storage", "dep:getter-operations"] lua = ["getter-core/lua", "getter-operations/lua"] cli = ["dep:getter-cli"] -rustls-platform-verifier = ["dep:rustls-platform-verifier"] +rustls-platform-verifier = ["dep:rustls-platform-verifier", "getter-operations?/rustls-platform-verifier"] rustls-platform-verifier-android = ["rustls-platform-verifier", "rustls-platform-verifier/jni"] webpki-roots = [] native-tokio = [] diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index b062204..6a87d37 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -156,7 +156,7 @@ pub enum CliCommand { ProviderGithubReleases { owner: String, repo: String, - releases: PathBuf, + releases: Option, asset_include: Option, asset_exclude: Option, include_prereleases: bool, @@ -967,7 +967,10 @@ fn execute(invocation: CliInvocation) -> Result { refresh, } => { let db = open_cache_db(&invocation.data_dir)?; - let releases_json = read_github_releases_fixture(&releases)?; + let releases_json = releases + .as_deref() + .map(read_github_releases_fixture) + .transpose()?; let request = json!({ "owner": owner, "repo": repo, @@ -1241,7 +1244,7 @@ struct AutogenGithubPreviewArgs { struct ProviderGithubReleasesArgs { owner: String, repo: String, - releases: PathBuf, + releases: Option, asset_include: Option, asset_exclude: Option, include_prereleases: bool, @@ -1414,7 +1417,7 @@ fn parse_provider_github_releases_args( "provider github releases --releases requires a fixture path".to_owned(), ) })?; - parsed.releases = PathBuf::from(path); + parsed.releases = Some(PathBuf::from(path)); position += 2; } "--asset-include" => { @@ -1469,12 +1472,6 @@ fn parse_provider_github_releases_args( "provider github releases requires --repo ".to_owned(), )); } - if parsed.releases.as_os_str().is_empty() { - return Err(CliError::Usage( - "provider github releases requires --releases ".to_owned(), - )); - } - Ok(parsed) } @@ -2249,7 +2246,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo [--releases ] [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -2379,6 +2376,69 @@ mod tests { ); } + #[test] + fn parses_provider_github_releases_command_with_optional_fixture() { + let parsed = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "provider", + "github", + "releases", + "--owner", + "DUpdateSystem", + "--repo", + "UpgradeAll", + "--releases", + "/tmp/releases.json", + "--asset-include", + "UpgradeAll_.*[.]apk$", + "--asset-exclude", + "debug", + "--include-prereleases", + "--refresh", + ]) + .unwrap(); + assert_eq!( + parsed.command, + CliCommand::ProviderGithubReleases { + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + releases: Some(PathBuf::from("/tmp/releases.json")), + asset_include: Some("UpgradeAll_.*[.]apk$".to_owned()), + asset_exclude: Some("debug".to_owned()), + include_prereleases: true, + refresh: true, + } + ); + + let live = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "provider", + "github", + "releases", + "--owner", + "DUpdateSystem", + "--repo", + "UpgradeAll", + ]) + .unwrap(); + assert_eq!( + live.command, + CliCommand::ProviderGithubReleases { + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + releases: None, + asset_include: None, + asset_exclude: None, + include_prereleases: false, + refresh: false, + } + ); + } + #[test] fn parses_provider_github_latest_commit_command() { let parsed = parse_args([ diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 81c2e82..0d51558 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -7,6 +7,7 @@ edition.workspace = true default = [] lua = ["getter-core/lua", "dep:mlua"] lua-provider-host-dev = ["lua"] +rustls-platform-verifier = ["ureq/platform-verifier"] [dependencies] json_comments = "0.2" @@ -18,6 +19,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" sha2 = "0.10" thiserror = "1" +ureq = { version = "3.3.0", default-features = false, features = ["gzip", "rustls"] } [dev-dependencies] tempfile = "3" diff --git a/crates/getter-operations/src/github_releases.rs b/crates/getter-operations/src/github_releases.rs index cdac38b..32f700c 100644 --- a/crates/getter-operations/src/github_releases.rs +++ b/crates/getter-operations/src/github_releases.rs @@ -1,9 +1,9 @@ -//! Fixture-backed GitHub release cache/query operations. +//! GitHub release cache/query operations. //! -//! This is the first getter-owned GitHub provider slice. It parses controlled -//! GitHub REST release fixtures, runs them through the shared provider cache, -//! and normalizes release assets into getter update candidates without adding -//! live HTTP, Flutter/Kotlin provider parsing, downloader, or installer logic. +//! Getter owns both fixture-backed and live GitHub REST release refreshes, +//! runs them through the shared provider cache, and normalizes release assets +//! into getter update candidates without moving provider parsing, transport +//! controls, downloader, or installer logic into Flutter/Kotlin. use crate::provider_cache::{ read_or_refresh_provider_response_with_provenance, source_response_sha512, @@ -18,11 +18,13 @@ use getter_storage::{CacheDb, StorageError}; use serde::Deserialize; use serde_json::{json, Value}; use sha2::{Digest, Sha512}; +use std::time::Duration; pub const GITHUB_PROVIDER_ID: &str = "github"; pub const GITHUB_RELEASES_CACHE_VERSION: &str = "github-releases-v1"; pub const DEFAULT_GITHUB_API_BASE_URL: &str = "https://api.github.com"; pub const GITHUB_ASSET_NOT_FOUND: &str = "provider.github.asset_not_found"; +const GITHUB_RELEASE_TRANSPORT_TIMEOUT: Duration = Duration::from_secs(30); #[derive(Debug, Clone, PartialEq, Eq)] pub struct GithubReleaseConfig { @@ -53,6 +55,110 @@ pub struct GithubReleaseResult { pub diagnostics: Vec, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct GithubReleaseTransportRequest<'a> { + pub api_base_url: &'a str, + pub owner: &'a str, + pub repo: &'a str, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GithubReleaseTransportResponse { + pub body: String, + pub etag: Option, + pub last_modified: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum GithubReleaseTransportError { + #[error("GitHub release transport failed: {0}")] + Transport(String), + #[error("GitHub release transport returned HTTP {status}: {message}")] + HttpStatus { status: u16, message: String }, +} + +pub trait GithubReleaseTransport { + fn fetch_releases( + &self, + request: &GithubReleaseTransportRequest<'_>, + ) -> Result; +} + +pub struct UreqGithubReleaseTransport { + agent: ureq::Agent, +} + +impl Default for UreqGithubReleaseTransport { + fn default() -> Self { + #[cfg(feature = "rustls-platform-verifier")] + { + return Self { + agent: ureq::Agent::config_builder() + .timeout_global(Some(GITHUB_RELEASE_TRANSPORT_TIMEOUT)) + .tls_config( + ureq::tls::TlsConfig::builder() + .root_certs(ureq::tls::RootCerts::PlatformVerifier) + .build(), + ) + .build() + .new_agent(), + }; + } + + #[cfg(not(feature = "rustls-platform-verifier"))] + { + Self { + agent: ureq::Agent::config_builder() + .timeout_global(Some(GITHUB_RELEASE_TRANSPORT_TIMEOUT)) + .build() + .new_agent(), + } + } + } +} + +impl UreqGithubReleaseTransport { + pub fn new() -> Self { + Self::default() + } +} + +impl GithubReleaseTransport for UreqGithubReleaseTransport { + fn fetch_releases( + &self, + request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + let url = github_releases_url(request.api_base_url, request.owner, request.repo); + let mut response = self + .agent + .get(&url) + .header("Accept", "application/vnd.github+json") + .header("X-GitHub-Api-Version", "2022-11-28") + .header("User-Agent", "UpgradeAll-getter") + .call() + .map_err(github_transport_error)?; + let etag = response + .headers() + .get("etag") + .and_then(|value| value.to_str().ok()) + .map(str::to_owned); + let last_modified = response + .headers() + .get("last-modified") + .and_then(|value| value.to_str().ok()) + .map(str::to_owned); + let body = response + .body_mut() + .read_to_string() + .map_err(|source| GithubReleaseTransportError::Transport(source.to_string()))?; + Ok(GithubReleaseTransportResponse { + body, + etag, + last_modified, + }) + } +} + #[derive(Debug, thiserror::Error)] pub enum GithubReleaseOperationError { #[error("provider cache operation failed: {0}")] @@ -75,6 +181,76 @@ pub fn read_or_refresh_github_releases( ) -> Result where F: FnOnce() -> Result, +{ + read_or_refresh_github_releases_with_refresh(db, config, mode, || { + let body = refresh_json()?; + github_release_refresh_from_body(body, json!({})) + }) +} + +pub fn read_or_refresh_github_releases_from_default_transport( + db: &CacheDb, + config: GithubReleaseConfig, + mode: ProviderCacheMode, +) -> Result { + read_or_refresh_github_releases_from_transport( + db, + config, + mode, + &UreqGithubReleaseTransport::new(), + ) +} + +pub fn read_or_refresh_github_releases_from_transport( + db: &CacheDb, + config: GithubReleaseConfig, + mode: ProviderCacheMode, + transport: &T, +) -> Result +where + T: GithubReleaseTransport + ?Sized, +{ + read_or_refresh_github_releases_from_transport_checked(db, config, mode, transport, |_| Ok(())) +} + +pub fn read_or_refresh_github_releases_from_transport_checked( + db: &CacheDb, + config: GithubReleaseConfig, + mode: ProviderCacheMode, + transport: &T, + validate_body: F, +) -> Result +where + T: GithubReleaseTransport + ?Sized, + F: FnOnce(&str) -> Result<(), String>, +{ + let refresh_config = config.clone(); + read_or_refresh_github_releases_with_refresh(db, config, mode, || { + let request = GithubReleaseTransportRequest { + api_base_url: &refresh_config.api_base_url, + owner: &refresh_config.owner, + repo: &refresh_config.repo, + }; + let response = transport + .fetch_releases(&request) + .map_err(|source| source.to_string())?; + validate_body(&response.body)?; + let freshness_json = json!({ + "etag": response.etag, + "last_modified": response.last_modified, + }); + github_release_refresh_from_body(response.body, freshness_json) + }) +} + +fn read_or_refresh_github_releases_with_refresh( + db: &CacheDb, + config: GithubReleaseConfig, + mode: ProviderCacheMode, + refresh: F, +) -> Result +where + F: FnOnce() -> Result, { let cache_key = config.cache_key(); let cache_result = read_or_refresh_provider_response_with_provenance( @@ -84,19 +260,7 @@ where provider: GITHUB_PROVIDER_ID, mode, }, - || { - let json = refresh_json()?; - let source_digest = source_response_sha512(json.as_bytes()); - let releases = - parse_github_releases_json(&json).map_err(|source| source.to_string())?; - let response_json = - serde_json::to_value(releases).map_err(|source| source.to_string())?; - Ok(ProviderResponseRefresh { - response_json, - source_response_sha512: vec![source_digest], - freshness_json: serde_json::json!({}), - }) - }, + refresh, )?; let response = cache_result.response; let releases = serde_json::from_value(response.response_json)?; @@ -112,10 +276,61 @@ where }) } +fn github_releases_url(api_base_url: &str, owner: &str, repo: &str) -> String { + format!( + "{}/repos/{owner}/{repo}/releases", + api_base_url.trim_end_matches('/') + ) +} + +fn github_transport_error(source: ureq::Error) -> GithubReleaseTransportError { + match source { + ureq::Error::StatusCode(status) => GithubReleaseTransportError::HttpStatus { + status, + message: github_http_status_message(status).to_owned(), + }, + other => GithubReleaseTransportError::Transport(other.to_string()), + } +} + +fn github_http_status_message(status: u16) -> &'static str { + match status { + 403 | 429 => "GitHub API rate limit or access restriction", + 404 => "GitHub repository or release endpoint was not found", + 401 => "GitHub API authentication is required", + _ => "GitHub API request failed", + } +} + +fn github_release_refresh_from_body( + body: String, + freshness_json: Value, +) -> Result { + let source_digest = source_response_sha512(body.as_bytes()); + let releases = parse_github_releases_json(&body).map_err(|source| source.to_string())?; + let response_json = serde_json::to_value(releases).map_err(|source| source.to_string())?; + Ok(ProviderResponseRefresh { + response_json, + source_response_sha512: vec![source_digest], + freshness_json, + }) +} + pub fn github_releases_json( db: &CacheDb, request_json: &str, ) -> Result { + github_releases_json_with_transport(db, request_json, &UreqGithubReleaseTransport::new()) +} + +pub fn github_releases_json_with_transport( + db: &CacheDb, + request_json: &str, + transport: &T, +) -> Result +where + T: GithubReleaseTransport + ?Sized, +{ let request: GithubReleasesJsonRequest = serde_json::from_str(request_json)?; let owner = required_non_empty(request.owner, "owner")?; let repo = required_non_empty(request.repo, "repo")?; @@ -144,11 +359,11 @@ pub fn github_releases_json( exclude: asset_filter.exclude, }, }; - let result = read_or_refresh_github_releases(db, config, mode, || { - request.releases_json.ok_or_else(|| { - "fixture-backed GitHub release refresh requires releases_json".to_owned() - }) - })?; + let result = if let Some(releases_json) = request.releases_json { + read_or_refresh_github_releases(db, config, mode, || Ok(releases_json)) + } else { + read_or_refresh_github_releases_from_transport(db, config, mode, transport) + }?; let candidates = github_release_update_candidates(&result.releases, &options)?; let mut diagnostics = result .diagnostics @@ -253,10 +468,13 @@ fn digest_hex(value: &str) -> String { #[cfg(test)] mod tests { use super::*; - use crate::provider_cache::{CACHE_REFRESH_FAILED, USED_STALE_CACHE}; + use crate::provider_cache::{source_response_sha512, CACHE_REFRESH_FAILED, USED_STALE_CACHE}; use getter_storage::ProviderResponseUpsert; use serde_json::json; - use std::cell::Cell; + use std::cell::{Cell, RefCell}; + use std::io::{Read, Write}; + use std::net::TcpListener; + use std::thread; const GITHUB_RELEASES_FIXTURE: &str = r#"[ { @@ -326,6 +544,189 @@ mod tests { assert_eq!(cached.response_json[0]["tag_name"], "v1.2.0"); } + #[test] + fn cache_miss_fetches_live_github_releases_through_transport() { + let db = CacheDb::open_in_memory().unwrap(); + let config = GithubReleaseConfig { + api_base_url: "https://api.github.test/".to_owned(), + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + }; + let cache_key = config.cache_key(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Ok(GithubReleaseTransportResponse { + body: GITHUB_RELEASES_FIXTURE.to_owned(), + etag: Some("W/\"release-snapshot\"".to_owned()), + last_modified: Some("Wed, 01 Jul 2026 00:00:00 GMT".to_owned()), + })), + requests: RefCell::new(Vec::new()), + }; + + let result = read_or_refresh_github_releases_from_transport( + &db, + config, + ProviderCacheMode::UseCached, + &transport, + ) + .unwrap(); + + assert_eq!(transport.requests.borrow().len(), 1); + let request = &transport.requests.borrow()[0]; + assert_eq!(request.api_base_url, "https://api.github.test/"); + assert_eq!(request.owner, "DUpdateSystem"); + assert_eq!(request.repo, "UpgradeAll"); + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + assert_eq!( + result.source_response_sha512, + vec![source_response_sha512(GITHUB_RELEASES_FIXTURE.as_bytes())] + ); + let cached = db.provider_response(&cache_key).unwrap().unwrap(); + assert_eq!(cached.provider, GITHUB_PROVIDER_ID); + assert_eq!(cached.response_json[0]["tag_name"], "v1.2.0"); + assert_eq!(cached.freshness_json["etag"], "W/\"release-snapshot\""); + assert_eq!( + cached.freshness_json["last_modified"], + "Wed, 01 Jul 2026 00:00:00 GMT" + ); + } + + #[test] + fn default_transport_fetches_releases_from_mock_http_server() { + let db = CacheDb::open_in_memory().unwrap(); + let (base_url, handle) = serve_one_github_response( + 200, + &[ + ("ETag", "W/\"mock-releases\""), + ("Last-Modified", "Wed, 01 Jul 2026 00:00:00 GMT"), + ("Content-Type", "application/json"), + ], + GITHUB_RELEASES_FIXTURE, + ); + let config = GithubReleaseConfig { + api_base_url: base_url, + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + }; + + let result = read_or_refresh_github_releases_from_default_transport( + &db, + config, + ProviderCacheMode::UseCached, + ) + .unwrap(); + let request = handle.join().unwrap(); + + assert!(request.starts_with("GET /repos/DUpdateSystem/UpgradeAll/releases HTTP/1.1")); + assert!(request.contains("accept: application/vnd.github+json")); + assert!(request.contains("x-github-api-version: 2022-11-28")); + assert!(request.contains("user-agent: UpgradeAll-getter")); + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + assert_eq!(result.source_response_sha512.len(), 1); + } + + #[test] + fn transport_cache_hit_does_not_call_live_refresh() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + read_or_refresh_github_releases(&db, config.clone(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Err(GithubReleaseTransportError::Transport( + "transport should not run".to_owned(), + ))), + requests: RefCell::new(Vec::new()), + }; + + let result = read_or_refresh_github_releases_from_transport( + &db, + config, + ProviderCacheMode::UseCached, + &transport, + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Cache); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + assert!(transport.requests.borrow().is_empty()); + } + + #[test] + fn transport_refresh_failure_without_stale_cache_is_provider_error() { + let db = CacheDb::open_in_memory().unwrap(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Err(GithubReleaseTransportError::HttpStatus { + status: 429, + message: "GitHub API rate limit or access restriction".to_owned(), + })), + requests: RefCell::new(Vec::new()), + }; + + let error = read_or_refresh_github_releases_from_transport( + &db, + config(), + ProviderCacheMode::ForceRefresh, + &transport, + ) + .unwrap_err(); + + assert!(matches!( + error, + GithubReleaseOperationError::Cache(ProviderCacheOperationError::RefreshFailed(_)) + )); + assert!(error.to_string().contains("HTTP 429")); + } + + #[test] + fn forced_transport_refresh_failure_returns_stale_cache_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + let cache_key = config.cache_key(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key, + provider: GITHUB_PROVIDER_ID.to_owned(), + response_json: serde_json::from_str(GITHUB_RELEASES_FIXTURE).unwrap(), + source_response_sha512: vec![source_response_sha512( + GITHUB_RELEASES_FIXTURE.as_bytes(), + )], + provenance_schema_version: Some( + crate::provider_cache::PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1.to_owned(), + ), + freshness_json: serde_json::json!({}), + }) + .unwrap(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Err(GithubReleaseTransportError::HttpStatus { + status: 429, + message: "GitHub API rate limit or access restriction".to_owned(), + })), + requests: RefCell::new(Vec::new()), + }; + + let result = read_or_refresh_github_releases_from_transport( + &db, + config, + ProviderCacheMode::ForceRefresh, + &transport, + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + assert!(result.diagnostics[0] + .message + .contains("HTTP 429: GitHub API rate limit")); + } + #[test] fn cache_hit_avoids_refreshing_fixture_releases() { let db = CacheDb::open_in_memory().unwrap(); @@ -419,6 +820,35 @@ mod tests { assert!(response["diagnostics"].as_array().unwrap().is_empty()); } + #[test] + fn json_operation_without_fixture_uses_live_transport() { + let db = CacheDb::open_in_memory().unwrap(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Ok(GithubReleaseTransportResponse { + body: GITHUB_RELEASES_FIXTURE.to_owned(), + etag: Some("W/\"json-live\"".to_owned()), + last_modified: None, + })), + requests: RefCell::new(Vec::new()), + }; + let request = json!({ + "owner": "example", + "repo": "app", + "asset": { "include": "\\.apk$", "exclude": "debug" } + }); + + let response = + github_releases_json_with_transport(&db, &request.to_string(), &transport).unwrap(); + + assert_eq!(transport.requests.borrow().len(), 1); + assert_eq!(response["source"], "refreshed"); + assert_eq!(response["candidates"][0]["version"], "v1.2.0"); + assert_eq!(response["diagnostics"], json!([])); + let cache_key = response["cache_key"].as_str().unwrap(); + let cached = db.provider_response(cache_key).unwrap().unwrap(); + assert_eq!(cached.freshness_json["etag"], "W/\"json-live\""); + } + #[test] fn json_operation_reports_asset_not_found_diagnostic() { let db = CacheDb::open_in_memory().unwrap(); @@ -451,4 +881,64 @@ mod tests { GithubReleaseOperationError::Cache(ProviderCacheOperationError::RefreshFailed(_)) )); } + + #[derive(Debug, Clone, PartialEq, Eq)] + struct RecordedGithubReleaseRequest { + api_base_url: String, + owner: String, + repo: String, + } + + struct MockGithubReleaseTransport { + response: RefCell>, + requests: RefCell>, + } + + impl GithubReleaseTransport for MockGithubReleaseTransport { + fn fetch_releases( + &self, + request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + self.requests + .borrow_mut() + .push(RecordedGithubReleaseRequest { + api_base_url: request.api_base_url.to_owned(), + owner: request.owner.to_owned(), + repo: request.repo.to_owned(), + }); + self.response.borrow().clone() + } + } + + fn serve_one_github_response( + status: u16, + headers: &[(&str, &str)], + body: &'static str, + ) -> (String, thread::JoinHandle) { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let headers = headers + .iter() + .map(|(name, value)| (name.to_string(), value.to_string())) + .collect::>(); + let handle = thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut buffer = [0; 4096]; + let bytes = stream.read(&mut buffer).unwrap(); + let request = String::from_utf8_lossy(&buffer[..bytes]).into_owned(); + let reason = if status == 200 { "OK" } else { "Error" }; + write!( + stream, + "HTTP/1.1 {status} {reason}\r\nContent-Length: {}\r\nConnection: close\r\n", + body.len() + ) + .unwrap(); + for (name, value) in headers { + write!(stream, "{name}: {value}\r\n").unwrap(); + } + write!(stream, "\r\n{body}").unwrap(); + request + }); + (format!("http://{address}"), handle) + } } diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 12756cf..4e0f8da 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -10,8 +10,11 @@ use crate::fdroid_catalog::{ read_or_refresh_fdroid_catalog, FdroidCatalogOperationError, FdroidEndpointConfig, DEFAULT_FDROID_ENDPOINT_ID, DEFAULT_FDROID_ENDPOINT_URL, FDROID_PROVIDER_ID, }; +#[cfg(feature = "lua-provider-host-dev")] +use crate::github_releases::UreqGithubReleaseTransport; use crate::github_releases::{ - read_or_refresh_github_releases, GithubReleaseConfig, GithubReleaseOperationError, + read_or_refresh_github_releases, read_or_refresh_github_releases_from_transport_checked, + GithubReleaseConfig, GithubReleaseOperationError, GithubReleaseTransport, DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, }; use crate::lua_runtime_hooks::load_runtime_hooks; @@ -296,6 +299,7 @@ pub fn stable_provider_package_eval_json( github_api_base_url: request.github_api_base_url, github_releases_json: request.github_releases_json, github_include_prereleases: request.github_include_prereleases, + github_release_transport: Some(Rc::new(UreqGithubReleaseTransport::new())), }, )?; let package = serde_json::to_value(result.package)?; @@ -399,11 +403,12 @@ struct GithubProviderHostConfig { api_base_url: String, mode: ProviderCacheMode, releases_json: Option, + release_transport: Option>, default_include_prereleases: bool, manifest: Option, } -#[derive(Debug, Clone)] +#[derive(Clone)] pub(crate) struct ProviderBackedPackageEvalConfig { pub(crate) mode: ProviderCacheMode, pub(crate) fdroid_endpoint_id: Option, @@ -412,6 +417,7 @@ pub(crate) struct ProviderBackedPackageEvalConfig { pub(crate) github_endpoint_id: Option, pub(crate) github_api_base_url: Option, pub(crate) github_releases_json: Option, + pub(crate) github_release_transport: Option>, pub(crate) github_include_prereleases: bool, } @@ -479,6 +485,7 @@ pub(crate) fn evaluate_provider_backed_package( api_base_url: github_api_base_url, mode: config.mode, releases_json: config.github_releases_json, + release_transport: config.github_release_transport, default_include_prereleases: config.github_include_prereleases, manifest, }, @@ -565,6 +572,7 @@ fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Resu api_base_url: config.api_base_url.clone(), mode: config.mode, releases_json: config.releases_json.clone(), + release_transport: None, default_include_prereleases: config.default_include_prereleases, manifest: None, }; @@ -797,16 +805,27 @@ fn github_release_candidates_envelope( repo: request.repo, }; let refresh_fixture = config.releases_json; + let refresh_transport = config.release_transport; let manifest = config.manifest.clone(); - let result = read_or_refresh_github_releases(&db, provider_config, config.mode, || { - let releases = refresh_fixture.ok_or_else(|| { - "GitHub release provider host has no cached releases and no refresh source installed" - .to_owned() - })?; - require_manifest_body(&manifest, GITHUB_PROVIDER_ID, &releases) - .map_err(|source| source.to_string())?; - Ok(releases) - }) + let result = if let Some(releases) = refresh_fixture { + read_or_refresh_github_releases(&db, provider_config, config.mode, || { + require_manifest_body(&manifest, GITHUB_PROVIDER_ID, &releases) + .map_err(|source| source.to_string())?; + Ok(releases) + }) + } else if let Some(transport) = refresh_transport { + read_or_refresh_github_releases_from_transport_checked( + &db, + provider_config, + config.mode, + transport.as_ref(), + |body| require_manifest_body(&manifest, GITHUB_PROVIDER_ID, body).map_err(|source| source.to_string()), + ) + } else { + read_or_refresh_github_releases(&db, provider_config, config.mode, || { + Err("GitHub release provider host has no cached releases and no refresh source installed".to_owned()) + }) + } .map_err(mlua::Error::external)?; reject_unproven_manifest_cache( &manifest, diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 08d8c88..8c5f971 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -6,6 +6,8 @@ //! persisted task state or duplicating task/control semantics in Flutter or //! Android glue. +#[cfg(feature = "lua")] +use crate::github_releases::{GithubReleaseTransport, UreqGithubReleaseTransport}; #[cfg(feature = "lua")] use crate::lua_provider_host::{ evaluate_provider_backed_package, LuaProviderHostOperationError, @@ -35,6 +37,8 @@ use serde::Deserialize; use serde_json::{json, Value}; #[cfg(feature = "lua")] use std::path::PathBuf; +#[cfg(feature = "lua")] +use std::rc::Rc; #[derive(Debug, thiserror::Error)] pub enum RuntimeOperationError { @@ -161,9 +165,26 @@ pub fn issue_action_from_registered_package_json( data_dir: &std::path::Path, db: &MainDb, request_json: &str, +) -> Result { + issue_action_from_registered_package_json_with_github_transport( + runtime, + data_dir, + db, + request_json, + Some(Rc::new(UreqGithubReleaseTransport::new())), + ) +} + +#[cfg(feature = "lua")] +fn issue_action_from_registered_package_json_with_github_transport( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + db: &MainDb, + request_json: &str, + github_release_transport: Option>, ) -> Result { let request: RegisteredPackageUpdateActionRequest = parse_request(request_json)?; - let evaluated = evaluate_registered_package(data_dir, db, &request)?; + let evaluated = evaluate_registered_package(data_dir, db, &request, github_release_transport)?; let candidates = StaticPackageUpdatesProvider.check_updates(&evaluated.package); let update = check_updates_offline( evaluated.package.id.clone(), @@ -342,6 +363,7 @@ struct OfflineUpdateActionRequest { #[cfg(feature = "lua")] #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] struct RegisteredPackageUpdateActionRequest { package_id: PackageId, #[serde(default)] @@ -405,6 +427,7 @@ fn evaluate_registered_package( data_dir: &std::path::Path, db: &MainDb, request: &RegisteredPackageUpdateActionRequest, + github_release_transport: Option>, ) -> Result { let repositories = db.repositories()?; let mut missing_path = None; @@ -441,6 +464,7 @@ fn evaluate_registered_package( github_endpoint_id: None, github_api_base_url: None, github_releases_json: None, + github_release_transport: github_release_transport.clone(), github_include_prereleases: false, }, )?; @@ -497,7 +521,9 @@ mod tests { use crate::fdroid_catalog::{FdroidEndpointConfig, FDROID_PROVIDER_ID}; #[cfg(feature = "lua")] use crate::github_releases::{ - GithubReleaseConfig, DEFAULT_GITHUB_API_BASE_URL, GITHUB_PROVIDER_ID, + GithubReleaseConfig, GithubReleaseTransport, GithubReleaseTransportError, + GithubReleaseTransportRequest, GithubReleaseTransportResponse, DEFAULT_GITHUB_API_BASE_URL, + GITHUB_PROVIDER_ID, }; #[cfg(feature = "lua")] use crate::provider_cache::PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1; @@ -516,7 +542,11 @@ mod tests { #[cfg(feature = "lua")] use sha2::{Digest, Sha512}; #[cfg(feature = "lua")] + use std::cell::RefCell; + #[cfg(feature = "lua")] use std::fs; + #[cfg(feature = "lua")] + use std::rc::Rc; #[cfg(feature = "lua")] const FDROID_INDEX_FIXTURE: &str = r#" @@ -725,6 +755,140 @@ mod tests { assert_eq!(issued["update"]["actions"][0]["file_name"], "F-Droid.apk"); } + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_refreshes_github_cache_on_miss_with_getter_transport() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_github_provider_package_repo(&repo_root, GITHUB_RELEASES_FIXTURE); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + let transport = + github_transport_with_body(GITHUB_RELEASES_FIXTURE, Some("W/\"runtime-github\"")); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json_with_github_transport( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/app/org.fdroid.fdroid", + "installed_version": "v1.0.0" + }) + .to_string(), + Some(transport.clone()), + ) + .unwrap(); + + assert_eq!(transport.requests.borrow().len(), 1); + assert_eq!(transport.requests.borrow()[0].owner, "f-droid"); + assert_eq!(transport.requests.borrow()[0].repo, "fdroidclient"); + assert_eq!(issued["provider_calls"][0]["provider"], "github"); + assert_eq!(issued["provider_calls"][0]["source"], "refreshed"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "v1.20.0" + ); + let config = GithubReleaseConfig { + api_base_url: DEFAULT_GITHUB_API_BASE_URL.to_owned(), + owner: "f-droid".to_owned(), + repo: "fdroidclient".to_owned(), + }; + let cached = CacheDb::open(data_dir.join("cache.db")) + .unwrap() + .provider_response(&config.cache_key()) + .unwrap() + .unwrap(); + assert_eq!(cached.response_json[0]["tag_name"], "v1.20.0"); + assert_eq!(cached.freshness_json["etag"], "W/\"runtime-github\""); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_live_github_refresh_allows_free_network_without_manifest() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); + write_github_provider_package_with_options( + &package_dir, + None, + true, + "F-Droid", + "org.fdroid.fdroid", + "f-droid", + "fdroidclient", + ); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + let transport = github_transport_with_body(GITHUB_RELEASES_FIXTURE, None); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json_with_github_transport( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/app/org.fdroid.fdroid", + "installed_version": "v1.0.0" + }) + .to_string(), + Some(transport.clone()), + ) + .unwrap(); + + assert_eq!(transport.requests.borrow().len(), 1); + assert_eq!(issued["provider_calls"][0]["source"], "refreshed"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "v1.20.0" + ); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_rejects_unmanifested_live_github_refresh() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); + write_github_provider_package_with_options( + &package_dir, + None, + false, + "F-Droid", + "org.fdroid.fdroid", + "f-droid", + "fdroidclient", + ); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + let transport = github_transport_with_body(GITHUB_RELEASES_FIXTURE, None); + let mut runtime = GetterRuntime::new(); + + let err = issue_action_from_registered_package_json_with_github_transport( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/app/org.fdroid.fdroid", + "installed_version": "v1.0.0" + }) + .to_string(), + Some(transport), + ) + .unwrap_err(); + + assert_eq!(err.code(), "package.eval_error"); + assert!(err + .to_string() + .contains("package.provider.response_not_in_manifest")); + assert_eq!(runtime.tasks().len(), 0); + } + #[cfg(feature = "lua")] #[test] fn registered_package_update_check_matches_old_upgradeall_github_release_snapshot() { @@ -826,10 +990,8 @@ mod tests { ) .unwrap_err(); - assert_eq!(err.code(), "package.eval_error"); - assert!(err - .to_string() - .contains("F-Droid provider host has no cached catalog")); + assert_eq!(err.code(), "runtime.invalid_request"); + assert!(err.to_string().contains("unknown field `fdroid_index_xml`")); assert_eq!(runtime.tasks().len(), 0); } @@ -1142,23 +1304,50 @@ return fdroid.package { android_package: &str, owner: &str, repo: &str, + ) { + write_github_provider_package_with_options( + package_dir, + Some(manifest_body), + false, + name, + android_package, + owner, + repo, + ); + } + + #[cfg(feature = "lua")] + fn write_github_provider_package_with_options( + package_dir: &std::path::Path, + manifest_body: Option<&str>, + allow_free_network: bool, + name: &str, + android_package: &str, + owner: &str, + repo: &str, ) { fs::create_dir_all(package_dir).unwrap(); + let mut metadata = json!({ + "type": "android:app", + "android": { "package_name": android_package } + }); + if allow_free_network { + metadata["lua"] = json!({ + "9999.lua": { "permission": ["allow_free_network"] } + }); + } fs::write( package_dir.join("metadata.jsonc"), - format!( - r#"{{ - "type": "android:app", - "android": {{ "package_name": "{android_package}" }} -}}"# - ), - ) - .unwrap(); - fs::write( - package_dir.join("Manifest"), - format!("{} fixture-body\n", sha512_hex(manifest_body.as_bytes())), + serde_json::to_string_pretty(&metadata).unwrap(), ) .unwrap(); + if let Some(manifest_body) = manifest_body { + fs::write( + package_dir.join("Manifest"), + format!("{} fixture-body\n", sha512_hex(manifest_body.as_bytes())), + ) + .unwrap(); + } fs::write( package_dir.join("9999.lua"), format!( @@ -1205,6 +1394,52 @@ return github_android.package {{ format!("{:x}", hasher.finalize()) } + #[cfg(feature = "lua")] + #[derive(Debug, Clone, PartialEq, Eq)] + struct RecordedGithubReleaseRequest { + api_base_url: String, + owner: String, + repo: String, + } + + #[cfg(feature = "lua")] + struct MockGithubReleaseTransport { + response: RefCell>, + requests: RefCell>, + } + + #[cfg(feature = "lua")] + fn github_transport_with_body( + body: &str, + etag: Option<&str>, + ) -> Rc { + Rc::new(MockGithubReleaseTransport { + response: RefCell::new(Ok(GithubReleaseTransportResponse { + body: body.to_owned(), + etag: etag.map(str::to_owned), + last_modified: Some("Wed, 01 Jul 2026 00:00:00 GMT".to_owned()), + })), + requests: RefCell::new(Vec::new()), + }) + } + + #[cfg(feature = "lua")] + impl GithubReleaseTransport for MockGithubReleaseTransport { + fn fetch_releases( + &self, + request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + self.requests + .borrow_mut() + .push(RecordedGithubReleaseRequest { + api_base_url: request.api_base_url.to_owned(), + owner: request.owner.to_owned(), + repo: request.repo.to_owned(), + }); + self.response.borrow().clone() + } + } + #[cfg(feature = "lua")] fn write_package_directory_static_update_repo(root: &std::path::Path) { let package_dir = root.join("android/app/com.example.autogen"); From 3d96140e7203702456b8d58bd8dda004aee087e2 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 2 Jul 2026 21:06:13 +0800 Subject: [PATCH 64/72] feat(autogen): refresh GitHub autogen through getter transport --- .../getter-operations/src/github_autogen.rs | 130 +++++++++++++++--- 1 file changed, 110 insertions(+), 20 deletions(-) diff --git a/crates/getter-operations/src/github_autogen.rs b/crates/getter-operations/src/github_autogen.rs index 988a334..bd20d0f 100644 --- a/crates/getter-operations/src/github_autogen.rs +++ b/crates/getter-operations/src/github_autogen.rs @@ -1,14 +1,16 @@ -//! Fixture-backed GitHub Android APK autogen preview/apply operations. +//! GitHub Android APK autogen preview/apply operations. //! //! This module generates ordinary package directories that use the standard -//! `luaclass.github_android_apk` provider module. It consumes cached or -//! fixture-backed GitHub release facts owned by getter and deliberately avoids -//! live GitHub transport or Flutter/Kotlin provider parsing. +//! `luaclass.github_android_apk` provider module. It consumes cached, +//! fixture-backed, or getter-live GitHub release facts owned by getter and +//! deliberately avoids Flutter/Kotlin provider parsing, cache policy, or live +//! transport controls. use crate::autogen::{self, AutogenAcceptance, AutogenOperationError, AutogenOperationResult}; use crate::github_releases::{ - read_or_refresh_github_releases, GithubReleaseConfig, DEFAULT_GITHUB_API_BASE_URL, - GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, + read_or_refresh_github_releases, read_or_refresh_github_releases_from_transport, + GithubReleaseConfig, GithubReleaseTransport, UreqGithubReleaseTransport, + DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, }; use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource}; use getter_core::autogen::{ @@ -31,6 +33,25 @@ pub fn preview_github_android_package_json( cache_db: &CacheDb, request_json: &str, ) -> AutogenOperationResult { + preview_github_android_package_json_with_transport( + data_dir, + main_db, + cache_db, + request_json, + &UreqGithubReleaseTransport::new(), + ) +} + +pub fn preview_github_android_package_json_with_transport( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + request_json: &str, + transport: &T, +) -> AutogenOperationResult +where + T: GithubReleaseTransport + ?Sized, +{ let request: GithubAutogenPreviewRequest = serde_json::from_str(request_json).map_err(|source| { AutogenOperationError::Autogen(format!( @@ -71,20 +92,16 @@ pub fn preview_github_android_package_json( asset_filter: asset_filter.clone(), }; let package_id = github_android_package_id(&owner, &repo, &android_package)?; - let result = read_or_refresh_github_releases( - cache_db, - GithubReleaseConfig { - api_base_url, - owner: owner.clone(), - repo: repo.clone(), - }, - mode, - || { - request.releases_json.ok_or_else(|| { - "fixture-backed GitHub autogen refresh requires releases_json".to_owned() - }) - }, - ) + let config = GithubReleaseConfig { + api_base_url, + owner: owner.clone(), + repo: repo.clone(), + }; + let result = if let Some(releases_json) = request.releases_json { + read_or_refresh_github_releases(cache_db, config, mode, || Ok(releases_json)) + } else { + read_or_refresh_github_releases_from_transport(cache_db, config, mode, transport) + } .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; let (target_alias, target_path, target_priority) = autogen::generated_repository_config(data_dir)?; @@ -468,6 +485,11 @@ mod tests { use getter_core::RepositoryPriority; use serde_json::{json, Value}; use sha2::{Digest, Sha512}; + use std::cell::RefCell; + + use crate::github_releases::{ + GithubReleaseTransport, GithubReleaseTransportRequest, GithubReleaseTransportResponse, + }; const UPGRADEALL_GITHUB_RELEASES: &str = include_str!("../../../tests/files/web/github_api_release.json"); @@ -541,6 +563,49 @@ mod tests { assert!(!temp.path().join("repo/autogen").exists()); } + #[test] + fn preview_without_fixture_refreshes_github_releases_through_getter_transport() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Ok(GithubReleaseTransportResponse { + body: UPGRADEALL_GITHUB_RELEASES.to_owned(), + etag: Some("W/\"autogen-live\"".to_owned()), + last_modified: None, + })), + requests: RefCell::new(Vec::new()), + }; + let request = json!({ + "owner": "DUpdateSystem", + "repo": "UpgradeAll", + "android_package": "net.xzos.upgradeall", + "display_name": "UpgradeAll", + "asset": { "include": "UpgradeAll_.*[.]apk$" }, + }); + + let preview = preview_github_android_package_json_with_transport( + temp.path(), + &main_db, + &cache_db, + &request.to_string(), + &transport, + ) + .unwrap(); + + assert_eq!(transport.requests.borrow().len(), 1); + assert_eq!(preview["source"], "refreshed"); + assert_eq!( + preview["candidates"][0]["package_id"], + UPGRADEALL_PACKAGE_ID + ); + let cached = cache_db + .provider_response(preview["cache_key"].as_str().unwrap()) + .unwrap() + .unwrap(); + assert_eq!(cached.freshness_json["etag"], "W/\"autogen-live\""); + } + #[cfg(feature = "lua")] #[test] fn apply_writes_valid_github_package_directory_and_runtime_uses_cache() { @@ -725,6 +790,31 @@ mod tests { .unwrap_or_else(|| panic!("generated file {relative_path} not found: {files:?}")) } + struct MockGithubReleaseTransport { + response: RefCell>, + requests: RefCell>, + } + + impl GithubReleaseTransport for MockGithubReleaseTransport { + fn fetch_releases( + &self, + request: &GithubReleaseTransportRequest<'_>, + ) -> Result< + GithubReleaseTransportResponse, + crate::github_releases::GithubReleaseTransportError, + > { + self.requests.borrow_mut().push(( + request.api_base_url.to_owned(), + request.owner.to_owned(), + request.repo.to_owned(), + )); + self.response + .borrow() + .clone() + .map_err(crate::github_releases::GithubReleaseTransportError::Transport) + } + } + fn sha512_hex(body: &[u8]) -> String { let mut hasher = Sha512::new(); hasher.update(body); From d02b86fe5311b1c2762c31f326155bece179f5f7 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Fri, 3 Jul 2026 00:46:22 +0800 Subject: [PATCH 65/72] feat(runtime): download bytes through getter tasks --- crates/getter-core/src/runtime.rs | 149 +++++- crates/getter-operations/src/download.rs | 580 +++++++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + crates/getter-operations/src/runtime.rs | 147 +++++- 4 files changed, 856 insertions(+), 21 deletions(-) create mode 100644 crates/getter-operations/src/download.rs diff --git a/crates/getter-core/src/runtime.rs b/crates/getter-core/src/runtime.rs index 992978f..6736506 100644 --- a/crates/getter-core/src/runtime.rs +++ b/crates/getter-core/src/runtime.rs @@ -30,6 +30,30 @@ impl SealedActionPlan { .iter() .any(|action| matches!(action, UpdateAction::Install { .. })) } + + fn download_action(&self) -> Option { + self.actions.iter().find_map(|action| match action { + UpdateAction::Download { url, file_name } => Some(RuntimeDownloadPlan { + url: url.clone(), + file_name: file_name.clone(), + }), + _ => None, + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RuntimeDownloadPlan { + pub url: String, + pub file_name: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DownloadedFile { + pub file_name: String, + pub local_path: String, + pub size_bytes: u64, + pub sha256: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -49,6 +73,8 @@ pub struct TaskSnapshot { pub capabilities: TaskCapabilities, #[serde(default)] pub current_diagnostic: Option, + #[serde(default)] + pub downloaded_file: Option, pub updated_at: u64, } @@ -293,6 +319,13 @@ impl GetterRuntime { .collect() } + pub fn download_plan( + &self, + task_id: &str, + ) -> Result, RuntimeError> { + Ok(self.task_ref(task_id)?.plan.download_action()) + } + pub fn start_task(&mut self, task_id: &str) -> Result { { let clock = self.tick(); @@ -329,27 +362,34 @@ impl GetterRuntime { } pub fn complete_download(&mut self, task_id: &str) -> Result { - let needs_install = { - let task = self.task_ref(task_id)?; - task.plan.has_install_action() - }; + self.complete_download_internal(task_id, None) + } - if needs_install { - self.enter_install_handoff(task_id) - } else { - { - let clock = self.tick(); - let task = self.task_mut(task_id)?; - if !matches!( - task.status, - RuntimeTaskStatus::Queued | RuntimeTaskStatus::Running - ) { - return Err(RuntimeError::RetryNotSupported(task_id.to_owned())); - } - task.complete(clock); - } - self.snapshot_and_notify(task_id) + pub fn complete_downloaded_file( + &mut self, + task_id: &str, + downloaded_file: DownloadedFile, + ) -> Result { + self.complete_download_internal(task_id, Some(downloaded_file)) + } + + pub fn fail_download( + &mut self, + task_id: &str, + code: impl Into, + message: impl Into, + ) -> Result { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.fail( + clock, + TaskPhase::with_reason(TaskPhaseCategory::Failed, TaskPhaseReason::DownloadFailed), + TaskDiagnostic::error(code, message), + RetryResume::Download, + ); } + self.snapshot_and_notify(task_id) } pub fn pause_task(&mut self, task_id: &str) -> Result { @@ -492,6 +532,43 @@ impl GetterRuntime { .collect() } + fn complete_download_internal( + &mut self, + task_id: &str, + downloaded_file: Option, + ) -> Result { + let needs_install = { + let task = self.task_ref(task_id)?; + task.plan.has_install_action() + }; + + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + if !matches!( + task.status, + RuntimeTaskStatus::Queued | RuntimeTaskStatus::Running + ) { + return Err(RuntimeError::RetryNotSupported(task_id.to_owned())); + } + if let Some(downloaded_file) = downloaded_file { + task.downloaded_file = Some(downloaded_file); + } + task.updated_at = clock; + } + + if needs_install { + self.enter_install_handoff(task_id) + } else { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.complete(clock); + } + self.snapshot_and_notify(task_id) + } + } + fn enter_install_handoff(&mut self, task_id: &str) -> Result { let package_id = self.task_ref(task_id)?.plan.package_id.clone(); if self.package_locks.contains_key(&package_id) { @@ -575,6 +652,7 @@ struct RuntimeTask { phase: TaskPhase, progress: Option, current_diagnostic: Option, + downloaded_file: Option, retry_resume: RetryResume, updated_at: u64, } @@ -588,6 +666,7 @@ impl RuntimeTask { phase: TaskPhase::new(TaskPhaseCategory::Queued), progress: None, current_diagnostic: None, + downloaded_file: None, retry_resume: RetryResume::Download, updated_at, } @@ -602,6 +681,7 @@ impl RuntimeTask { progress: self.progress.clone(), capabilities: self.capabilities(), current_diagnostic: self.current_diagnostic.clone(), + downloaded_file: self.downloaded_file.clone(), updated_at: self.updated_at, } } @@ -641,6 +721,7 @@ impl RuntimeTask { total: None, }); self.current_diagnostic = None; + self.downloaded_file = None; self.updated_at = updated_at; } @@ -713,6 +794,36 @@ mod tests { assert_eq!(notifications.lock().unwrap().len(), 1); } + #[test] + fn runtime_exposes_download_plan_and_downloaded_file_snapshot() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan_without_install("generic/example")); + + let download = runtime.download_plan(&task_id).unwrap().unwrap(); + assert_eq!(download.url, "https://example.invalid/file.bin"); + assert_eq!(download.file_name, "file.bin"); + + runtime.start_task(&task_id).unwrap(); + let completed = runtime + .complete_downloaded_file( + &task_id, + DownloadedFile { + file_name: "file.bin".to_owned(), + local_path: "/tmp/getter/downloads/task-1/file.bin".to_owned(), + size_bytes: 11, + sha256: "sha256-test".to_owned(), + }, + ) + .unwrap(); + + assert_eq!(completed.status, RuntimeTaskStatus::Completed); + assert_eq!(completed.downloaded_file.as_ref().unwrap().size_bytes, 11); + assert_eq!( + completed.downloaded_file.as_ref().unwrap().local_path, + "/tmp/getter/downloads/task-1/file.bin" + ); + } + #[test] fn fake_install_waits_for_generic_user_result() { let mut runtime = GetterRuntime::new(); diff --git a/crates/getter-operations/src/download.rs b/crates/getter-operations/src/download.rs new file mode 100644 index 0000000..9bd8e41 --- /dev/null +++ b/crates/getter-operations/src/download.rs @@ -0,0 +1,580 @@ +//! Getter-owned runtime download executor. +//! +//! This is the first real-byte side-effect executor for ADR-0011 tasks. It is +//! deliberately current-process/task-local: it writes bytes for the submitted +//! task and updates the in-memory runtime snapshot, but it does not create a +//! durable task database, background worker, installer, or recovery mechanism. + +use getter_core::runtime::{ + DownloadedFile, GetterRuntime, RuntimeError, TaskPhaseCategory, TaskSnapshot, +}; +use sha2::{Digest, Sha256}; +use std::fs::{self, File}; +use std::io::{Read, Write}; +use std::path::Path; +use std::time::Duration; + +const RUNTIME_DOWNLOAD_TRANSPORT_TIMEOUT: Duration = Duration::from_secs(300); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct RuntimeDownloadTransportRequest<'a> { + pub url: &'a str, +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum RuntimeDownloadTransportError { + #[error("download transport failed: {0}")] + Transport(String), + #[error("download transport returned HTTP {status}: {message}")] + HttpStatus { status: u16, message: String }, + #[error("download sink failed: {0}")] + Sink(String), +} + +pub trait RuntimeDownloadSink { + fn set_total_bytes(&mut self, total_bytes: Option) -> Result<(), String>; + fn write_chunk(&mut self, chunk: &[u8]) -> Result<(), String>; +} + +pub trait RuntimeDownloadTransport { + fn fetch( + &self, + request: &RuntimeDownloadTransportRequest<'_>, + sink: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError>; +} + +pub struct UreqRuntimeDownloadTransport { + agent: ureq::Agent, +} + +impl Default for UreqRuntimeDownloadTransport { + fn default() -> Self { + #[cfg(feature = "rustls-platform-verifier")] + { + return Self { + agent: ureq::Agent::config_builder() + .timeout_global(Some(RUNTIME_DOWNLOAD_TRANSPORT_TIMEOUT)) + .tls_config( + ureq::tls::TlsConfig::builder() + .root_certs(ureq::tls::RootCerts::PlatformVerifier) + .build(), + ) + .build() + .new_agent(), + }; + } + + #[cfg(not(feature = "rustls-platform-verifier"))] + { + Self { + agent: ureq::Agent::config_builder() + .timeout_global(Some(RUNTIME_DOWNLOAD_TRANSPORT_TIMEOUT)) + .build() + .new_agent(), + } + } + } +} + +impl UreqRuntimeDownloadTransport { + pub fn new() -> Self { + Self::default() + } +} + +impl RuntimeDownloadTransport for UreqRuntimeDownloadTransport { + fn fetch( + &self, + request: &RuntimeDownloadTransportRequest<'_>, + sink: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError> { + let mut response = self + .agent + .get(request.url) + .header("Accept", "application/octet-stream, */*") + .header("User-Agent", "UpgradeAll-getter") + .call() + .map_err(download_transport_error)?; + sink.set_total_bytes(response.body().content_length()) + .map_err(RuntimeDownloadTransportError::Sink)?; + let mut reader = response.body_mut().as_reader(); + let mut buffer = [0_u8; 64 * 1024]; + loop { + let read = reader + .read(&mut buffer) + .map_err(|source| RuntimeDownloadTransportError::Transport(source.to_string()))?; + if read == 0 { + break; + } + sink.write_chunk(&buffer[..read]) + .map_err(RuntimeDownloadTransportError::Sink)?; + } + Ok(()) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum RuntimeDownloadExecutionError { + #[error("task has no download action")] + MissingDownloadAction, + #[error("failed to prepare download file: {0}")] + PrepareFile(std::io::Error), + #[error("download failed: {0}")] + Transport(#[from] RuntimeDownloadTransportError), + #[error("failed to finish download file: {0}")] + FinishFile(std::io::Error), + #[error("runtime error: {0}")] + Runtime(#[from] RuntimeError), +} + +impl RuntimeDownloadExecutionError { + pub fn diagnostic_code(&self) -> &'static str { + match self { + Self::MissingDownloadAction => "download.action_missing", + Self::PrepareFile(_) | Self::FinishFile(_) => "download.file_error", + Self::Transport(RuntimeDownloadTransportError::HttpStatus { .. }) => { + "download.http_status" + } + Self::Transport(_) => "download.transport_error", + Self::Runtime(error) => error.code(), + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum RuntimeDownloadOperationError { + #[error("runtime error: {0}")] + Runtime(#[from] RuntimeError), +} + +pub fn submit_action_and_download( + runtime: &mut GetterRuntime, + data_dir: &Path, + action_id: &str, + transport: &T, +) -> Result +where + T: RuntimeDownloadTransport + ?Sized, +{ + let submitted = runtime.submit_action(action_id)?; + let task_id = submitted.task_id.clone(); + match execute_download_task(runtime, data_dir, &task_id, transport) { + Ok(task) => Ok(task), + Err(error) => { + Ok(runtime.fail_download(&task_id, error.diagnostic_code(), error.to_string())?) + } + } +} + +pub fn retry_download_task( + runtime: &mut GetterRuntime, + data_dir: &Path, + task_id: &str, + transport: &T, +) -> Result +where + T: RuntimeDownloadTransport + ?Sized, +{ + let retried = runtime.retry_task(task_id)?; + if retried.phase.category != TaskPhaseCategory::Download { + return Ok(retried); + } + match execute_download_task(runtime, data_dir, task_id, transport) { + Ok(task) => Ok(task), + Err(error) => { + Ok(runtime.fail_download(task_id, error.diagnostic_code(), error.to_string())?) + } + } +} + +pub fn execute_download_task( + runtime: &mut GetterRuntime, + data_dir: &Path, + task_id: &str, + transport: &T, +) -> Result +where + T: RuntimeDownloadTransport + ?Sized, +{ + runtime.start_task(task_id)?; + let download = runtime + .download_plan(task_id)? + .ok_or(RuntimeDownloadExecutionError::MissingDownloadAction)?; + let file_name = safe_download_file_name(&download.file_name); + let download_dir = data_dir.join("downloads").join(task_id); + fs::create_dir_all(&download_dir).map_err(RuntimeDownloadExecutionError::PrepareFile)?; + let final_path = download_dir.join(&file_name); + let partial_path = download_dir.join(format!(".{file_name}.part")); + let file = File::create(&partial_path).map_err(RuntimeDownloadExecutionError::PrepareFile)?; + + let mut sink = RuntimeFileDownloadSink::new(runtime, task_id, file); + let fetch_result = transport.fetch( + &RuntimeDownloadTransportRequest { url: &download.url }, + &mut sink, + ); + let write_result = sink.finish(); + + if let Err(error) = fetch_result { + let _ = fs::remove_file(&partial_path); + return Err(RuntimeDownloadExecutionError::Transport(error)); + } + let written = write_result.map_err(RuntimeDownloadExecutionError::FinishFile)?; + fs::rename(&partial_path, &final_path).map_err(RuntimeDownloadExecutionError::FinishFile)?; + + let downloaded_file = DownloadedFile { + file_name, + local_path: final_path.to_string_lossy().into_owned(), + size_bytes: written.size_bytes, + sha256: written.sha256, + }; + Ok(runtime.complete_downloaded_file(task_id, downloaded_file)?) +} + +struct RuntimeFileDownloadSink<'a> { + runtime: &'a mut GetterRuntime, + task_id: String, + file: File, + hasher: Sha256, + size_bytes: u64, + total_bytes: Option, +} + +impl<'a> RuntimeFileDownloadSink<'a> { + fn new(runtime: &'a mut GetterRuntime, task_id: &str, file: File) -> Self { + Self { + runtime, + task_id: task_id.to_owned(), + file, + hasher: Sha256::new(), + size_bytes: 0, + total_bytes: None, + } + } + + fn finish(mut self) -> std::io::Result { + self.file.flush()?; + self.file.sync_all()?; + Ok(RuntimeFileDownloadResult { + size_bytes: self.size_bytes, + sha256: format!("{:x}", self.hasher.finalize()), + }) + } + + fn total_bits(&self) -> Option { + self.total_bytes.map(|bytes| bytes.saturating_mul(8)) + } + + fn current_bits(&self) -> u64 { + self.size_bytes.saturating_mul(8) + } + + fn publish_progress(&mut self) -> Result<(), String> { + self.runtime + .set_download_progress(&self.task_id, self.current_bits(), self.total_bits()) + .map(|_| ()) + .map_err(|source| source.to_string()) + } +} + +impl RuntimeDownloadSink for RuntimeFileDownloadSink<'_> { + fn set_total_bytes(&mut self, total_bytes: Option) -> Result<(), String> { + self.total_bytes = total_bytes; + self.publish_progress() + } + + fn write_chunk(&mut self, chunk: &[u8]) -> Result<(), String> { + self.file + .write_all(chunk) + .map_err(|source| source.to_string())?; + self.hasher.update(chunk); + self.size_bytes = self + .size_bytes + .checked_add(chunk.len() as u64) + .ok_or_else(|| "download size overflowed u64".to_owned())?; + self.publish_progress() + } +} + +struct RuntimeFileDownloadResult { + size_bytes: u64, + sha256: String, +} + +fn safe_download_file_name(raw: &str) -> String { + let base = raw + .trim() + .rsplit(['/', '\\']) + .next() + .unwrap_or_default() + .trim(); + let sanitized: String = base + .chars() + .map(|ch| { + if ch.is_control() || matches!(ch, '/' | '\\') { + '_' + } else { + ch + } + }) + .collect(); + if sanitized.is_empty() || sanitized == "." || sanitized == ".." { + "artifact.bin".to_owned() + } else { + sanitized + } +} + +fn download_transport_error(source: ureq::Error) -> RuntimeDownloadTransportError { + match source { + ureq::Error::StatusCode(status) => RuntimeDownloadTransportError::HttpStatus { + status, + message: download_http_status_message(status).to_owned(), + }, + other => RuntimeDownloadTransportError::Transport(other.to_string()), + } +} + +fn download_http_status_message(status: u16) -> &'static str { + match status { + 401 => "download requires authentication", + 403 => "download is forbidden", + 404 => "download artifact was not found", + 429 => "download was rate limited", + _ => "download request failed", + } +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::runtime::{PackageVersionLuaObject, RuntimeNotification, SealedActionPlan}; + use getter_core::{PackageId, PackageKind, UpdateAction}; + use std::cell::RefCell; + use std::io::{Read, Write}; + use std::net::TcpListener; + use std::sync::{Arc, Mutex}; + use std::thread; + + struct ChunkedDownloadTransport { + chunks: Vec<&'static [u8]>, + total_bytes: Option, + requests: RefCell>, + } + + impl RuntimeDownloadTransport for ChunkedDownloadTransport { + fn fetch( + &self, + request: &RuntimeDownloadTransportRequest<'_>, + sink: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError> { + self.requests.borrow_mut().push(request.url.to_owned()); + sink.set_total_bytes(self.total_bytes) + .map_err(RuntimeDownloadTransportError::Sink)?; + for chunk in &self.chunks { + sink.write_chunk(chunk) + .map_err(RuntimeDownloadTransportError::Sink)?; + } + Ok(()) + } + } + + #[test] + fn submit_action_downloads_bytes_to_getter_owned_file_and_notifies_progress() { + let temp = tempfile::tempdir().unwrap(); + let notifications = Arc::new(Mutex::new(Vec::new())); + let sink_notifications = notifications.clone(); + let mut runtime = GetterRuntime::new(); + runtime.set_notification_sink(move |notification| { + sink_notifications.lock().unwrap().push(notification); + }); + let action = runtime.issue_action(download_plan("generic/example", "../app.bin")); + let transport = ChunkedDownloadTransport { + chunks: vec![b"hello ", b"getter"], + total_bytes: Some(12), + requests: RefCell::new(Vec::new()), + }; + + let task = + submit_action_and_download(&mut runtime, temp.path(), &action.action_id, &transport) + .unwrap(); + + assert_eq!( + transport.requests.borrow().as_slice(), + &["https://example.invalid/app.bin"] + ); + assert_eq!( + task.status, + getter_core::runtime::RuntimeTaskStatus::Completed + ); + assert_eq!(task.downloaded_file.as_ref().unwrap().file_name, "app.bin"); + assert_eq!(task.downloaded_file.as_ref().unwrap().size_bytes, 12); + assert_eq!( + fs::read(&task.downloaded_file.as_ref().unwrap().local_path).unwrap(), + b"hello getter" + ); + assert_eq!( + task.downloaded_file.as_ref().unwrap().sha256, + "e20360c4483ac234e3233f2f82671d46f67dd09f8d1da753c8c81236aed04690" + ); + + let notifications = notifications.lock().unwrap(); + assert!(notifications.iter().any(|notification| matches!( + notification, + RuntimeNotification::TaskChanged { task } + if task.phase.category == getter_core::runtime::TaskPhaseCategory::Download + && task.progress.as_ref().is_some_and(|progress| progress.current == 48 && progress.total == Some(96)) + ))); + assert!(notifications.iter().any(|notification| matches!( + notification, + RuntimeNotification::TaskChanged { task } + if task.status == getter_core::runtime::RuntimeTaskStatus::Completed + && task.downloaded_file.is_some() + ))); + } + + #[test] + fn transport_failure_marks_existing_task_failed_without_restoring_action() { + let temp = tempfile::tempdir().unwrap(); + let mut runtime = GetterRuntime::new(); + let action = runtime.issue_action(download_plan("generic/example", "app.bin")); + let transport = FailingDownloadTransport; + + let task = + submit_action_and_download(&mut runtime, temp.path(), &action.action_id, &transport) + .unwrap(); + + assert_eq!(task.status, getter_core::runtime::RuntimeTaskStatus::Failed); + assert_eq!( + task.current_diagnostic.as_ref().unwrap().code, + "download.http_status" + ); + assert_eq!( + runtime.submit_action(&action.action_id).unwrap_err().code(), + "action.not_found" + ); + } + + #[test] + fn retry_download_reuses_same_task_and_writes_bytes() { + let temp = tempfile::tempdir().unwrap(); + let mut runtime = GetterRuntime::new(); + let action = runtime.issue_action(download_plan("generic/example", "app.bin")); + let failed = submit_action_and_download( + &mut runtime, + temp.path(), + &action.action_id, + &FailingDownloadTransport, + ) + .unwrap(); + let transport = ChunkedDownloadTransport { + chunks: vec![b"retry bytes"], + total_bytes: Some(11), + requests: RefCell::new(Vec::new()), + }; + + let retried = retry_download_task(&mut runtime, temp.path(), &failed.task_id, &transport) + .expect("retry download"); + + assert_eq!(retried.task_id, failed.task_id); + assert_eq!( + retried.status, + getter_core::runtime::RuntimeTaskStatus::Completed + ); + assert_eq!(retried.downloaded_file.as_ref().unwrap().size_bytes, 11); + assert_eq!( + fs::read(&retried.downloaded_file.as_ref().unwrap().local_path).unwrap(), + b"retry bytes" + ); + } + + struct FailingDownloadTransport; + + impl RuntimeDownloadTransport for FailingDownloadTransport { + fn fetch( + &self, + _: &RuntimeDownloadTransportRequest<'_>, + _: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError> { + Err(RuntimeDownloadTransportError::HttpStatus { + status: 404, + message: "download artifact was not found".to_owned(), + }) + } + } + + #[test] + fn default_transport_fetches_bytes_from_mock_http_server() { + let temp = tempfile::tempdir().unwrap(); + let (url, handle) = serve_one_download_response(b"mock artifact bytes"); + let mut runtime = GetterRuntime::new(); + let action = + runtime.issue_action(download_plan_with_url("generic/example", &url, "app.bin")); + + let task = submit_action_and_download( + &mut runtime, + temp.path(), + &action.action_id, + &UreqRuntimeDownloadTransport::new(), + ) + .unwrap(); + let request = handle.join().unwrap(); + + assert!(request.starts_with("GET /artifact.bin HTTP/1.1")); + assert!(request.contains("accept: application/octet-stream, */*")); + assert!(request.contains("user-agent: UpgradeAll-getter")); + assert_eq!(task.downloaded_file.as_ref().unwrap().size_bytes, 19); + assert_eq!( + fs::read(&task.downloaded_file.as_ref().unwrap().local_path).unwrap(), + b"mock artifact bytes" + ); + } + + fn download_plan(package_id: &str, file_name: &str) -> SealedActionPlan { + download_plan_with_url(package_id, "https://example.invalid/app.bin", file_name) + } + + fn download_plan_with_url(package_id: &str, url: &str, file_name: &str) -> SealedActionPlan { + SealedActionPlan { + package_id: PackageId::new(PackageKind::Generic, package_id.split('/').nth(1).unwrap()) + .unwrap(), + lua_object: PackageVersionLuaObject { + object_id: format!("lua:{package_id}:1.0"), + dependency_digest: "sha256-test".to_owned(), + }, + actions: vec![UpdateAction::Download { + url: url.to_owned(), + file_name: file_name.to_owned(), + }], + } + } + + fn serve_one_download_response(body: &'static [u8]) -> (String, thread::JoinHandle) { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let handle = thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut request = Vec::new(); + let mut buffer = [0_u8; 1024]; + loop { + let read = stream.read(&mut buffer).unwrap(); + if read == 0 { + break; + } + request.extend_from_slice(&buffer[..read]); + if request.windows(4).any(|window| window == b"\r\n\r\n") { + break; + } + } + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nContent-Type: application/octet-stream\r\n\r\n", + body.len() + ) + .unwrap(); + stream.write_all(body).unwrap(); + String::from_utf8_lossy(&request).into_owned() + }); + (format!("http://{address}/artifact.bin"), handle) + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 1851139..a6fb495 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -6,6 +6,7 @@ //! generated package files, manifests, and tracked state live here. pub mod autogen; +pub mod download; pub mod fdroid_autogen; pub mod fdroid_catalog; pub mod github_autogen; diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 8c5f971..fd08d33 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -6,6 +6,10 @@ //! persisted task state or duplicating task/control semantics in Flutter or //! Android glue. +use crate::download::{ + retry_download_task, submit_action_and_download, RuntimeDownloadOperationError, + RuntimeDownloadTransport, UreqRuntimeDownloadTransport, +}; #[cfg(feature = "lua")] use crate::github_releases::{GithubReleaseTransport, UreqGithubReleaseTransport}; #[cfg(feature = "lua")] @@ -64,6 +68,14 @@ pub enum RuntimeOperationError { Serialize(String), } +impl From for RuntimeOperationError { + fn from(error: RuntimeDownloadOperationError) -> Self { + match error { + RuntimeDownloadOperationError::Runtime(error) => Self::Runtime(error), + } + } +} + impl RuntimeOperationError { pub fn code(&self) -> &'static str { match self { @@ -223,6 +235,37 @@ pub fn submit_action_json( task_json(runtime.submit_action(&request.action_id)?) } +pub fn submit_action_and_download_json( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + request_json: &str, +) -> Result { + submit_action_and_download_json_with_transport( + runtime, + data_dir, + request_json, + &UreqRuntimeDownloadTransport::new(), + ) +} + +pub fn submit_action_and_download_json_with_transport( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + request_json: &str, + transport: &T, +) -> Result +where + T: RuntimeDownloadTransport + ?Sized, +{ + let request: SubmitActionRequest = parse_request(request_json)?; + task_json(submit_action_and_download( + runtime, + data_dir, + &request.action_id, + transport, + )?) +} + pub fn task_get_json( runtime: &GetterRuntime, request_json: &str, @@ -314,6 +357,37 @@ pub fn task_retry_json( task_json(runtime.retry_task(&request.task_id)?) } +pub fn task_retry_download_json( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + request_json: &str, +) -> Result { + task_retry_download_json_with_transport( + runtime, + data_dir, + request_json, + &UreqRuntimeDownloadTransport::new(), + ) +} + +pub fn task_retry_download_json_with_transport( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + request_json: &str, + transport: &T, +) -> Result +where + T: RuntimeDownloadTransport + ?Sized, +{ + let request: TaskIdRequest = parse_request(request_json)?; + task_json(retry_download_task( + runtime, + data_dir, + &request.task_id, + transport, + )?) +} + pub fn task_remove_json( runtime: &mut GetterRuntime, request_json: &str, @@ -375,6 +449,7 @@ struct RegisteredPackageUpdateActionRequest { } #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] struct SubmitActionRequest { action_id: String, } @@ -541,9 +616,7 @@ mod tests { use getter_storage::{CacheDb, ProviderResponseUpsert}; #[cfg(feature = "lua")] use sha2::{Digest, Sha512}; - #[cfg(feature = "lua")] use std::cell::RefCell; - #[cfg(feature = "lua")] use std::fs; #[cfg(feature = "lua")] use std::rc::Rc; @@ -1081,6 +1154,57 @@ mod tests { assert_eq!(submitted["status"], "queued"); } + #[test] + fn json_submit_and_download_writes_file_through_injected_transport() { + let temp = tempfile::tempdir().unwrap(); + let mut runtime = GetterRuntime::new(); + let issued = issue_action(&mut runtime, plan_without_install("generic/example")); + let action_id = issued["action_id"].as_str().unwrap(); + let transport = RecordingDownloadTransport { + requests: RefCell::new(Vec::new()), + }; + + let completed = submit_action_and_download_json_with_transport( + &mut runtime, + temp.path(), + &json!({ "action_id": action_id }).to_string(), + &transport, + ) + .unwrap(); + + assert_eq!( + transport.requests.borrow().as_slice(), + &["https://example.invalid/archive.zip"] + ); + assert_eq!(completed["status"], "completed"); + assert_eq!(completed["downloaded_file"]["file_name"], "archive.zip"); + assert_eq!(completed["downloaded_file"]["size_bytes"], 4); + let local_path = completed["downloaded_file"]["local_path"].as_str().unwrap(); + assert_eq!(fs::read(local_path).unwrap(), b"data"); + assert!(runtime.submit_action(action_id).is_err()); + } + + #[test] + fn json_submit_rejects_product_supplied_download_fields() { + let mut runtime = GetterRuntime::new(); + let issued = issue_action(&mut runtime, plan_without_install("generic/example")); + let action_id = issued["action_id"].as_str().unwrap(); + + let error = submit_action_json( + &mut runtime, + &json!({ + "action_id": action_id, + "url": "https://example.invalid/other.bin" + }) + .to_string(), + ) + .unwrap_err(); + + assert_eq!(error.code(), "runtime.invalid_request"); + assert!(error.detail().unwrap().contains("unknown field `url`")); + assert_eq!(runtime.tasks().len(), 0); + } + #[test] fn offline_update_check_without_update_does_not_issue_action() { let mut runtime = GetterRuntime::new(); @@ -1178,6 +1302,25 @@ mod tests { ); } + struct RecordingDownloadTransport { + requests: RefCell>, + } + + impl crate::download::RuntimeDownloadTransport for RecordingDownloadTransport { + fn fetch( + &self, + request: &crate::download::RuntimeDownloadTransportRequest<'_>, + sink: &mut dyn crate::download::RuntimeDownloadSink, + ) -> Result<(), crate::download::RuntimeDownloadTransportError> { + self.requests.borrow_mut().push(request.url.to_owned()); + sink.set_total_bytes(Some(4)) + .map_err(crate::download::RuntimeDownloadTransportError::Sink)?; + sink.write_chunk(b"data") + .map_err(crate::download::RuntimeDownloadTransportError::Sink)?; + Ok(()) + } + } + fn submit_plan(runtime: &mut GetterRuntime, plan: SealedActionPlan) -> String { let action = runtime.issue_action(plan); runtime.submit_action(&action.action_id).unwrap().task_id From 3c19debc5209243e078ab66a15ed1c9e8e23b93e Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 14 Jul 2026 21:02:03 +0800 Subject: [PATCH 66/72] feat(startup): expose unified app read model --- Cargo.lock | 1 + crates/getter-cli/src/lib.rs | 77 ++- crates/getter-cli/tests/startup_cli.rs | 105 ++++ crates/getter-operations/Cargo.toml | 1 + crates/getter-operations/src/lib.rs | 1 + .../getter-operations/src/provider_cache.rs | 45 +- crates/getter-operations/src/startup.rs | 472 ++++++++++++++++++ crates/getter-operations/tests/startup.rs | 343 +++++++++++++ crates/getter-storage/src/lib.rs | 88 +++- 9 files changed, 1061 insertions(+), 72 deletions(-) create mode 100644 crates/getter-cli/tests/startup_cli.rs create mode 100644 crates/getter-operations/src/startup.rs create mode 100644 crates/getter-operations/tests/startup.rs diff --git a/Cargo.lock b/Cargo.lock index b1df2b7..b0add8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -650,6 +650,7 @@ dependencies = [ "getter-storage", "json_comments", "mlua", + "rusqlite", "serde", "serde_json", "sha2", diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 6a87d37..5de2e57 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -8,8 +8,8 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_directory_script; use getter_core::repository::{ - default_repository_priority, GetterDataDirLayout, RepositoryMetadata, - RepositoryPackageDirectoryLayout, REPOSITORY_ROOT_METADATA_FILE, REPO_API_VERSION_V1, + default_repository_priority, RepositoryMetadata, RepositoryPackageDirectoryLayout, + REPO_API_VERSION_V1, }; use getter_core::runtime::{GetterRuntime, SealedActionPlan}; use getter_core::task::{ @@ -28,6 +28,7 @@ use getter_operations::github_latest_commit::{self, GithubLatestCommitOperationE use getter_operations::github_releases::{self, GithubReleaseOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; use getter_operations::runtime as runtime_operations; +use getter_operations::startup as startup_operations; use getter_storage::legacy_room::{ map_legacy_app, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, }; @@ -44,19 +45,6 @@ const MAIN_DB_FILE: &str = "main.db"; const CACHE_DB_FILE: &str = "cache.db"; const MIGRATION_REPORTS_DIR: &str = "migration-reports"; const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; -const REPOSITORY_ROOT_METADATA_STARTER: &str = r#"{ - "version": 1, - // Autogen writes to "autogen" by default. Uncomment and change this - // if generated packages should target another existing repository alias. - // "generated_repository": "autogen", - "priority": { - "local": 100, - "official": 0, - "autogen": -1 - } -} -"#; - #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliInvocation { pub data_dir: PathBuf, @@ -66,6 +54,9 @@ pub struct CliInvocation { #[derive(Debug, Clone, PartialEq, Eq)] pub enum CliCommand { Init, + Startup { + inventory: Option, + }, AppList, HubList, RepoList, @@ -445,6 +436,12 @@ where let command_args = &args[2..]; let command = match command_args { [command] if command == "init" => CliCommand::Init, + [command] if command == "startup" => CliCommand::Startup { inventory: None }, + [command, flag, inventory] if command == "startup" && flag == "--inventory" => { + CliCommand::Startup { + inventory: Some(PathBuf::from(inventory)), + } + } [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, [domain, command] if domain == "hub" && command == "list" => CliCommand::HubList, [domain, command] if domain == "repo" && command == "list" => CliCommand::RepoList, @@ -707,16 +704,27 @@ where fn execute(invocation: CliInvocation) -> Result { match invocation.command { + CliCommand::Startup { inventory } => { + let inventory = match inventory { + Some(path) => read_installed_inventory(&path)?, + None => InstalledInventory::new(Vec::new()), + }; + serde_json::to_value( + startup_operations::startup(&invocation.data_dir, inventory) + .map_err(|error| CliError::Storage(error.to_string()))?, + ) + .map_err(|error| CliError::Storage(error.to_string())) + } CliCommand::Init => { - initialize_storage(&invocation.data_dir)?; - let layout = initialize_data_dir_layout(&invocation.data_dir)?; + let bootstrap = startup_operations::bootstrap_data_dir(&invocation.data_dir) + .map_err(|error| CliError::Storage(error.to_string()))?; Ok(json!({ - "data_dir": layout.root, - "main_db": layout.main_db, - "cache_db": layout.cache_db, - "repo": layout.repository_root, - "rc": layout.runtime_config_root, - "repo_metadata": layout.repository_root.join(REPOSITORY_ROOT_METADATA_FILE), + "data_dir": bootstrap.data_dir, + "main_db": bootstrap.main_db.path, + "cache_db": bootstrap.cache_db.path, + "repo": bootstrap.repo, + "rc": bootstrap.rc, + "repo_metadata": bootstrap.repo_metadata, })) } CliCommand::AppList => { @@ -1092,26 +1100,6 @@ fn initialize_storage(data_dir: &Path) -> Result<(), CliError> { Ok(()) } -fn initialize_data_dir_layout(data_dir: &Path) -> Result { - let layout = GetterDataDirLayout::new(data_dir); - fs::create_dir_all(&layout.repository_root).map_err(|source| { - CliError::Storage(format!("failed to create repository root: {source}")) - })?; - fs::create_dir_all(&layout.runtime_config_root).map_err(|source| { - CliError::Storage(format!("failed to create runtime config root: {source}")) - })?; - let metadata_path = layout.repository_root.join(REPOSITORY_ROOT_METADATA_FILE); - if !metadata_path.exists() { - fs::write(&metadata_path, REPOSITORY_ROOT_METADATA_STARTER).map_err(|source| { - CliError::Storage(format!( - "failed to write repository root metadata '{}': {source}", - metadata_path.display() - )) - })?; - } - Ok(layout) -} - fn open_initialized_storage(data_dir: &Path) -> Result<(), CliError> { initialize_storage(data_dir) } @@ -2246,7 +2234,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo [--releases ] [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir ]|app list|repo list|repo add [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo [--releases ] [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -2291,6 +2279,7 @@ impl CliCommand { fn name(&self) -> &'static str { match self { Self::Init => "init", + Self::Startup { .. } => "startup", Self::AppList => "app list", Self::HubList => "hub list", Self::RepoList => "repo list", diff --git a/crates/getter-cli/tests/startup_cli.rs b/crates/getter-cli/tests/startup_cli.rs new file mode 100644 index 0000000..15a587d --- /dev/null +++ b/crates/getter-cli/tests/startup_cli.rs @@ -0,0 +1,105 @@ +use getter_cli::{run, ExitCode}; +use getter_core::repository::RepositoryMetadata; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; +use serde_json::{json, Value}; + +#[test] +fn startup_command_proves_shared_getter_snapshot_with_empty_inventory_default() { + let temp = tempfile::tempdir().unwrap(); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "startup", + ]); + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let envelope: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(envelope["command"], "startup"); + assert_eq!(envelope["data"]["format"], "getter-startup-snapshot"); + assert_eq!(envelope["data"]["version"], 1); + assert_eq!(envelope["data"]["bootstrap"]["lifecycle"], "initialized"); + assert_eq!(envelope["data"]["apps"], serde_json::json!([])); +} + +#[test] +fn startup_command_uses_inventory_file_to_join_installed_version() { + let temp = tempfile::tempdir().unwrap(); + let repo = temp.path().join("repo/official"); + let package = repo.join("android/com.example"); + std::fs::create_dir_all(&package).unwrap(); + std::fs::write(package.join("Manifest"), "").unwrap(); + std::fs::write( + package.join("metadata.jsonc"), + r#"{ + "type":"android:app", + "display_name":"Example", + "android":{"package_name":"com.example"} + }"#, + ) + .unwrap(); + std::fs::write( + package.join("1.lua"), + r#"#!/bin/upa-lua v1 +return package_version { updates = { { version="2.4.0", artifacts={{name="app.apk",url="https://example.invalid/app.apk"}} } } }"#, + ) + .unwrap(); + let package_id = "android/com.example".parse().unwrap(); + let repository_id = RepositoryId::new("official").unwrap(); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: repository_id.clone(), + name: "Official".to_owned(), + priority: RepositoryPriority::DEFAULT, + api_version: "v1".to_owned(), + }, + Some(&repo), + None, + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id, + enabled: true, + favorite: false, + pin_version: None, + repository_id: Some(repository_id), + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + let inventory_path = temp.path().join("inventory.json"); + std::fs::write( + &inventory_path, + serde_json::to_vec(&json!({ + "format": "upgradeall-installed-inventory", + "version": 1, + "items": [{ + "kind": "android_package", + "package_name": "com.example", + "label": "Example", + "version_name": "2.4.0", + "version_code": 24 + }] + })) + .unwrap(), + ) + .unwrap(); + + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "startup", + "--inventory", + inventory_path.to_str().unwrap(), + ]); + + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let envelope: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!( + envelope["data"]["apps"][0]["installed_version"], "2.4.0", + "{}", + output.stdout + ); + assert_eq!(envelope["data"]["apps"][0]["update_status"], "up_to_date"); +} diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 0d51558..6ce6120 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -22,4 +22,5 @@ thiserror = "1" ureq = { version = "3.3.0", default-features = false, features = ["gzip", "rustls"] } [dev-dependencies] +rusqlite = { version = "0.32", features = ["bundled"] } tempfile = "3" diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index a6fb495..c90e8ed 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -25,3 +25,4 @@ pub mod lua_runtime_hooks; pub mod provider_cache; pub mod read_model; pub mod runtime; +pub mod startup; diff --git a/crates/getter-operations/src/provider_cache.rs b/crates/getter-operations/src/provider_cache.rs index 89179e3..d74e511 100644 --- a/crates/getter-operations/src/provider_cache.rs +++ b/crates/getter-operations/src/provider_cache.rs @@ -10,12 +10,14 @@ use serde_json::{json, Value}; use sha2::{Digest, Sha512}; pub const CACHE_REFRESH_FAILED: &str = "cache.refresh_failed"; +pub const CACHE_ONLY_MISS: &str = "cache.cache_only_miss"; pub const USED_STALE_CACHE: &str = "used_stale_cache"; pub const PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1: &str = "provider-response-provenance-v1"; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ProviderCacheMode { UseCached, + CacheOnly, ForceRefresh, } @@ -62,6 +64,8 @@ pub enum ProviderCacheOperationError { Storage(#[from] StorageError), #[error("provider refresh failed: {0}")] RefreshFailed(String), + #[error("provider cache-only miss for '{cache_key}' ({provider})")] + CacheOnlyMiss { cache_key: String, provider: String }, } pub fn read_or_refresh_provider_response( @@ -90,7 +94,10 @@ where F: FnOnce() -> Result, { let cached = db.provider_response(request.cache_key)?; - if request.mode == ProviderCacheMode::UseCached { + if matches!( + request.mode, + ProviderCacheMode::UseCached | ProviderCacheMode::CacheOnly + ) { if let Some(response) = cached { return Ok(ProviderCacheResult { response, @@ -98,6 +105,12 @@ where diagnostics: Vec::new(), }); } + if request.mode == ProviderCacheMode::CacheOnly { + return Err(ProviderCacheOperationError::CacheOnlyMiss { + cache_key: request.cache_key.to_owned(), + provider: request.provider.to_owned(), + }); + } } match refresh() { @@ -159,7 +172,7 @@ mod tests { use std::cell::Cell; #[test] - fn uses_cached_provider_response_without_refreshing() { + fn cache_only_hit_returns_cached_response_without_refreshing() { let db = CacheDb::open_in_memory().unwrap(); db.upsert_provider_response(&ProviderResponseUpsert { cache_key: "fdroid:official:index".to_owned(), @@ -177,11 +190,11 @@ mod tests { ProviderCacheRequest { cache_key: "fdroid:official:index", provider: "fdroid", - mode: ProviderCacheMode::UseCached, + mode: ProviderCacheMode::CacheOnly, }, || { refreshed.set(true); - Ok(json!({ "revision": "fresh" })) + panic!("cache-only mode must not refresh a hit") }, ) .unwrap(); @@ -192,6 +205,30 @@ mod tests { assert!(result.diagnostics.is_empty()); } + #[test] + fn cache_only_miss_never_invokes_refresh() { + let db = CacheDb::open_in_memory().unwrap(); + let refresh_called = Cell::new(false); + let error = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "github:missing", + provider: "github-releases", + mode: ProviderCacheMode::CacheOnly, + }, + || { + refresh_called.set(true); + panic!("cache-only mode must not invoke refresh") + }, + ) + .unwrap_err(); + assert!(matches!( + error, + ProviderCacheOperationError::CacheOnlyMiss { .. } + )); + assert!(!refresh_called.get()); + } + #[test] fn cache_miss_refreshes_and_stores_provider_response() { let db = CacheDb::open_in_memory().unwrap(); diff --git a/crates/getter-operations/src/startup.rs b/crates/getter-operations/src/startup.rs new file mode 100644 index 0000000..090d7f6 --- /dev/null +++ b/crates/getter-operations/src/startup.rs @@ -0,0 +1,472 @@ +//! Idempotent getter startup and unified app/update read model. + +#[cfg(feature = "lua")] +use crate::lua_provider_host::{evaluate_provider_backed_package, ProviderBackedPackageEvalConfig}; +#[cfg(feature = "lua")] +use crate::provider_cache::{ProviderCacheMode, CACHE_ONLY_MISS}; +use getter_core::autogen::{ + validate_installed_inventory, InstalledInventory, InstalledInventoryItem, +}; +#[cfg(feature = "lua")] +use getter_core::repository::RepositoryPackageDirectoryLayout; +use getter_core::repository::{GetterDataDirLayout, REPOSITORY_ROOT_METADATA_FILE}; +use getter_core::update::{ + check_updates_offline, compare_versions, UpdateCheckStatus, UpdateSelectionPolicy, +}; +#[cfg(feature = "lua")] +use getter_core::InstalledTarget; +#[cfg(feature = "lua")] +use getter_storage::StoredPackageResolution; +use getter_storage::{ + CacheDb, MainDb, StorageError, StoredRepository, StoredTrackedPackage, + CACHE_STORAGE_CONTRACT_VERSION, MAIN_STORAGE_CONTRACT_VERSION, +}; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::{Path, PathBuf}; + +const REPOSITORY_ROOT_METADATA_STARTER: &str = r#"{ + "version": 1, + // Autogen writes to "autogen" by default. Uncomment and change this + // if generated packages should target another existing repository alias. + // "generated_repository": "autogen", + "priority": { + "local": 100, + "official": 0, + "autogen": -1 + } +} +"#; +pub const STARTUP_SNAPSHOT_FORMAT: &str = "getter-startup-snapshot"; +pub const STARTUP_SNAPSHOT_VERSION: u32 = 1; + +#[derive(Debug, thiserror::Error)] +pub enum StartupError { + #[error("startup I/O failed: {0}")] + Io(#[from] std::io::Error), + #[error("startup storage failed: {0}")] + Storage(#[from] StorageError), + #[error("invalid installed inventory: {0}")] + Inventory(String), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupSnapshot { + pub format: String, + pub version: u32, + pub bootstrap: BootstrapStatus, + pub repositories: Vec, + pub apps: Vec, + pub update_count: usize, + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BootstrapStatus { + pub lifecycle: BootstrapLifecycle, + pub data_dir: PathBuf, + pub main_db: DatabaseStatus, + pub cache_db: DatabaseStatus, + pub repo: PathBuf, + pub rc: PathBuf, + pub repo_metadata: PathBuf, + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum BootstrapLifecycle { + Initialized, + AlreadyInitialized, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DatabaseStatus { + pub path: PathBuf, + pub contract_version: u32, + pub created_this_call: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupRepository { + pub id: String, + pub name: String, + pub priority: i32, + pub api_version: String, + pub path: Option, + pub revision: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupAppSummary { + pub package_id: String, + pub repository_id: Option, + pub name: Option, + pub favorite: bool, + pub pin_version: Option, + pub installed_target: Option, + pub installed_version: Option, + pub effective_installed_version: Option, + pub latest_version: Option, + pub update_status: UpdateStatus, + pub warning: StartupWarning, + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupInstalledTarget { + pub kind: String, + pub id: String, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupWarning { + pub free_network: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupDiagnostic { + pub code: String, + pub message: String, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum UpdateStatus { + Available, + UpToDate, + NoCandidates, + NotInstalled, +} + +pub fn bootstrap_data_dir(data_dir: &Path) -> Result { + let layout = GetterDataDirLayout::new(data_dir); + let main_created = !layout.main_db.exists(); + let cache_created = !layout.cache_db.exists(); + let repo_created = !layout.repository_root.is_dir(); + let rc_created = !layout.runtime_config_root.is_dir(); + fs::create_dir_all(&layout.repository_root)?; + fs::create_dir_all(&layout.runtime_config_root)?; + let repo_metadata = layout.repository_root.join(REPOSITORY_ROOT_METADATA_FILE); + let metadata_created = !repo_metadata.is_file(); + if metadata_created { + fs::write(&repo_metadata, REPOSITORY_ROOT_METADATA_STARTER)?; + } + let (_, main_migrated) = MainDb::open_with_migration_status(&layout.main_db)?; + let (_, cache_migrated) = CacheDb::open_with_migration_status(&layout.cache_db)?; + let mut diagnostics = Vec::new(); + if main_migrated { + diagnostics.push(diagnostic( + "storage.main_migrations_applied", + "Main storage schema migrations were applied", + )); + } + if cache_migrated { + diagnostics.push(diagnostic( + "storage.cache_migrations_applied", + "Cache storage schema migrations were applied", + )); + } + Ok(BootstrapStatus { + lifecycle: if !main_created + && !cache_created + && !repo_created + && !rc_created + && !metadata_created + && !main_migrated + && !cache_migrated + { + BootstrapLifecycle::AlreadyInitialized + } else { + BootstrapLifecycle::Initialized + }, + data_dir: layout.root, + main_db: DatabaseStatus { + path: layout.main_db, + contract_version: MAIN_STORAGE_CONTRACT_VERSION, + created_this_call: main_created, + }, + cache_db: DatabaseStatus { + path: layout.cache_db, + contract_version: CACHE_STORAGE_CONTRACT_VERSION, + created_this_call: cache_created, + }, + repo: layout.repository_root, + rc: layout.runtime_config_root, + repo_metadata, + diagnostics, + }) +} + +pub fn startup( + data_dir: &Path, + inventory: InstalledInventory, +) -> Result { + validate_installed_inventory(&inventory) + .map_err(|error| StartupError::Inventory(error.to_string()))?; + let bootstrap = bootstrap_data_dir(data_dir)?; + let db = MainDb::open(&bootstrap.main_db.path)?; + let stored_repositories = db.repositories()?; + let repositories = stored_repositories.iter().map(repository_dto).collect(); + let apps: Vec<_> = db + .tracked_packages()? + .into_iter() + .filter(|p| p.enabled) + .map(|package| app_summary(data_dir, &stored_repositories, &inventory, package)) + .collect(); + let update_count = apps + .iter() + .filter(|app| app.update_status == UpdateStatus::Available) + .count(); + Ok(StartupSnapshot { + format: STARTUP_SNAPSHOT_FORMAT.to_owned(), + version: STARTUP_SNAPSHOT_VERSION, + bootstrap, + repositories, + apps, + update_count, + diagnostics: vec![], + }) +} + +fn repository_dto(repo: &StoredRepository) -> StartupRepository { + StartupRepository { + id: repo.id.to_string(), + name: repo.name.clone(), + priority: repo.priority.value(), + api_version: repo.api_version.clone(), + path: repo.path.clone(), + revision: repo.revision.clone(), + } +} + +fn diagnostic(code: &str, message: impl Into) -> StartupDiagnostic { + StartupDiagnostic { + code: code.into(), + message: message.into(), + } +} + +#[cfg_attr(not(feature = "lua"), allow(unused_mut, unused_variables))] +fn app_summary( + data_dir: &Path, + repositories: &[StoredRepository], + inventory: &InstalledInventory, + tracked: StoredTrackedPackage, +) -> StartupAppSummary { + let mut diagnostics = Vec::new(); + #[cfg(feature = "lua")] + let repository = match tracked.repository_id.as_ref() { + Some(id) => repositories.iter().find(|repo| &repo.id == id), + None if tracked.package_resolution + == StoredPackageResolution::OfficialRepositoryPackage => + { + repositories.iter().find(|repository| { + let root = repository + .path + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| data_dir.join("repo").join(repository.id.as_str())); + RepositoryPackageDirectoryLayout::load(&root) + .ok() + .is_some_and(|layout| layout.package(&tracked.package_id).is_some()) + }) + } + None => None, + }; + #[cfg(not(feature = "lua"))] + let repository = tracked + .repository_id + .as_ref() + .and_then(|id| repositories.iter().find(|repo| &repo.id == id)); + let mut name = None; + let mut installed_target = None; + let mut warning = StartupWarning::default(); + let mut candidates = Vec::new(); + + #[cfg(feature = "lua")] + if tracked.package_resolution == StoredPackageResolution::OfficialRepositoryPackage { + if let Some(repository) = repository { + let root = repository + .path + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| data_dir.join("repo").join(repository.id.as_str())); + let evaluated = (|| { + let layout = + RepositoryPackageDirectoryLayout::load(&root).map_err(|e| e.to_string())?; + let package = layout.package(&tracked.package_id).ok_or_else(|| { + format!("package {} is absent from repository", tracked.package_id) + })?; + let metadata = layout + .package_metadata(package) + .map_err(|e| e.to_string())?; + let script = layout + .unambiguous_version_script(package) + .map_err(|e| e.to_string())?; + evaluate_provider_backed_package( + data_dir, + &repository.id, + package, + &metadata, + script, + ProviderBackedPackageEvalConfig { + mode: ProviderCacheMode::CacheOnly, + fdroid_endpoint_id: None, + fdroid_endpoint_url: None, + fdroid_index_xml: None, + github_endpoint_id: None, + github_api_base_url: None, + github_releases_json: None, + github_release_transport: None, + github_include_prereleases: false, + }, + ) + .map(|result| result.package) + .map_err(|e| e.to_string()) + })(); + match evaluated { + Ok(resolved) => { + name = Some(resolved.name); + warning.free_network = resolved.permissions.free_network; + installed_target = resolved.installed.first().map(target_dto); + candidates = resolved.updates; + } + Err(message) => diagnostics.push(diagnostic( + if message.contains(CACHE_ONLY_MISS) || message.contains("cache-only miss") { + "startup.provider_cache_miss" + } else { + "startup.package_eval_failed" + }, + message, + )), + } + } else { + diagnostics.push(diagnostic( + "startup.repository_missing", + "Tracked official package has no resolvable repository", + )); + } + } else { + diagnostics.push(diagnostic( + match tracked.package_resolution { + StoredPackageResolution::GenerateLocalPackage => { + "startup.package_generation_required" + } + StoredPackageResolution::MissingPackageDefinition => "startup.package_missing", + StoredPackageResolution::OfficialRepositoryPackage => unreachable!(), + }, + format!( + "Tracked package resolution is {}", + tracked.package_resolution.as_str() + ), + )); + } + + let installed_version = installed_target + .as_ref() + .and_then(|target| inventory_version(inventory, target)); + if installed_target.is_none() { + diagnostics.push(diagnostic( + "startup.installed_target_missing", + "Package evaluation returned no installed target", + )); + diagnostics.push(diagnostic( + "startup.installed_inventory_missing", + "No installed target was available to match against inventory", + )); + } else if installed_version.is_none() { + diagnostics.push(diagnostic( + "startup.installed_inventory_missing", + "No matching installed inventory item was supplied", + )); + } + + let policy = UpdateSelectionPolicy { + pin_version: tracked.pin_version.clone(), + }; + let result = check_updates_offline( + tracked.package_id.clone(), + installed_version.clone(), + candidates.clone(), + policy, + ) + .ok(); + let latest_version = candidates + .iter() + .max_by(|a, b| compare_versions(&a.version, &b.version)) + .map(|c| c.version.clone()); + let effective_installed_version = tracked + .pin_version + .clone() + .or_else(|| installed_version.clone()); + let update_status = if installed_version.is_none() || installed_target.is_none() { + UpdateStatus::NotInstalled + } else { + match result.as_ref().map(|r| r.status) { + Some(UpdateCheckStatus::UpdateAvailable) => UpdateStatus::Available, + Some(UpdateCheckStatus::UpToDate) => UpdateStatus::UpToDate, + Some(UpdateCheckStatus::NoCandidates) | None => UpdateStatus::NoCandidates, + } + }; + StartupAppSummary { + package_id: tracked.package_id.to_string(), + repository_id: tracked.repository_id.map(|id| id.to_string()), + name, + favorite: tracked.favorite, + pin_version: tracked.pin_version, + installed_target, + installed_version, + effective_installed_version, + latest_version, + update_status, + warning, + diagnostics, + } +} + +#[cfg(feature = "lua")] +fn target_dto(target: &InstalledTarget) -> StartupInstalledTarget { + match target { + InstalledTarget::AndroidPackage { package_name } => StartupInstalledTarget { + kind: "android_package".into(), + id: package_name.clone(), + }, + InstalledTarget::MagiskModule { module_id } => StartupInstalledTarget { + kind: "magisk_module".into(), + id: module_id.clone(), + }, + InstalledTarget::Generic { id } => StartupInstalledTarget { + kind: "generic".into(), + id: id.clone(), + }, + } +} + +fn inventory_version( + inventory: &InstalledInventory, + target: &StartupInstalledTarget, +) -> Option { + inventory + .items + .iter() + .find_map(|item| match (item, target.kind.as_str()) { + ( + InstalledInventoryItem::AndroidPackage { + package_name, + version_name, + .. + }, + "android_package", + ) if package_name == &target.id => version_name.clone(), + ( + InstalledInventoryItem::MagiskModule { + module_id, + version_name, + .. + }, + "magisk_module", + ) if module_id == &target.id => version_name.clone(), + _ => None, + }) +} diff --git a/crates/getter-operations/tests/startup.rs b/crates/getter-operations/tests/startup.rs new file mode 100644 index 0000000..4c19f93 --- /dev/null +++ b/crates/getter-operations/tests/startup.rs @@ -0,0 +1,343 @@ +#![cfg(feature = "lua")] + +use getter_core::autogen::{InstalledInventory, InstalledInventoryItem}; +use getter_core::repository::RepositoryMetadata; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_operations::startup::{startup, UpdateStatus}; +use getter_storage::{ + CacheDb, MainDb, ProviderResponseUpsert, StoredPackageResolution, TrackedPackageUpsert, +}; +use serde_json::json; +use std::fs; + +#[test] +fn startup_bootstraps_idempotently_and_joins_static_updates() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + + let first = startup(data_dir, InstalledInventory::new(vec![])).unwrap(); + let second = startup(data_dir, InstalledInventory::new(vec![])).unwrap(); + assert_eq!( + first.bootstrap.lifecycle, + getter_operations::startup::BootstrapLifecycle::Initialized + ); + assert_eq!( + second.bootstrap.lifecycle, + getter_operations::startup::BootstrapLifecycle::AlreadyInitialized + ); + assert!(first.bootstrap.main_db.created_this_call); + assert!(!second.bootstrap.main_db.created_this_call); + assert_eq!( + first.bootstrap.main_db.contract_version, + getter_storage::MAIN_STORAGE_CONTRACT_VERSION + ); + assert_eq!( + first.bootstrap.cache_db.contract_version, + getter_storage::CACHE_STORAGE_CONTRACT_VERSION + ); + let metadata = fs::read_to_string(data_dir.join("repo/metadata.jsonc")).unwrap(); + assert!(metadata.contains("// \"generated_repository\": \"autogen\"")); + assert!(metadata.contains("\"official\": 0")); + assert!(first + .bootstrap + .diagnostics + .iter() + .any(|diagnostic| { diagnostic.code == "storage.main_migrations_applied" })); + assert!(first + .bootstrap + .diagnostics + .iter() + .any(|diagnostic| { diagnostic.code == "storage.cache_migrations_applied" })); + assert!(second.bootstrap.diagnostics.is_empty()); + + let repo = data_dir.join("repo/official"); + let package = repo.join("android/org.fdroid.fdroid"); + fs::create_dir_all(&package).unwrap(); + fs::write(package.join("Manifest"), "").unwrap(); + fs::write( + package.join("metadata.jsonc"), + r#"{ + "type":"android:app", "display_name":"F-Droid", + "android":{"package_name":"org.fdroid.fdroid"}, + "lua":{"9999.lua":{"permission":["allow_free_network"]}} + }"#, + ) + .unwrap(); + fs::write( + package.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { updates = { + { version="1.1.0", artifacts={{name="fdroid.apk",url="https://example.invalid/1.1.apk"}} }, + { version="1.2.0", artifacts={{name="fdroid.apk",url="https://example.invalid/1.2.apk"}} }, +} }"#, + ) + .unwrap(); + + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".into(), + priority: RepositoryPriority::DEFAULT, + api_version: "v1".into(), + }, + Some(&repo), + None, + ) + .unwrap(); + let package_id = "android/org.fdroid.fdroid".parse().unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id, + enabled: true, + favorite: true, + pin_version: Some("1.1.0".into()), + repository_id: Some(RepositoryId::new("official").unwrap()), + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + let snapshot = startup( + data_dir, + InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".into(), + label: Some("F-Droid installed".into()), + version_name: Some("1.0.0".into()), + version_code: Some(100), + }]), + ) + .unwrap(); + let app = &snapshot.apps[0]; + assert_eq!(app.name.as_deref(), Some("F-Droid")); + assert_eq!(app.installed_version.as_deref(), Some("1.0.0")); + assert_eq!(app.effective_installed_version.as_deref(), Some("1.1.0")); + assert_eq!(app.latest_version.as_deref(), Some("1.2.0")); + assert_eq!(app.update_status, UpdateStatus::Available); + assert!(app.warning.free_network); + assert_eq!(snapshot.update_count, 1); +} + +#[test] +fn startup_resolves_official_package_without_repository_to_highest_priority_covering_repo() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + startup(data_dir, InstalledInventory::new(vec![])).unwrap(); + + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + for (id, priority, contains_package, latest_version) in [ + ("highest-without-package", 100, false, "9.9.9"), + ("highest-covering", 50, true, "2.0.0"), + ("lower-covering", 10, true, "1.5.0"), + ] { + let repo = data_dir.join("repo").join(id); + fs::create_dir_all(&repo).unwrap(); + if contains_package { + let package = repo.join("android/org.example.app"); + fs::create_dir_all(&package).unwrap(); + fs::write(package.join("Manifest"), "").unwrap(); + fs::write( + package.join("metadata.jsonc"), + r#"{ + "type":"android:app", "display_name":"Example", + "android":{"package_name":"org.example.app"} + }"#, + ) + .unwrap(); + fs::write( + package.join("9999.lua"), + format!( + r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{{{ version="{latest_version}", artifacts={{{{name="app.apk",url="https://example.invalid/app.apk"}}}} }}}} }}"# + ), + ) + .unwrap(); + } + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new(id).unwrap(), + name: id.into(), + priority: RepositoryPriority::new(priority), + api_version: "v1".into(), + }, + Some(&repo), + None, + ) + .unwrap(); + } + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/org.example.app".parse().unwrap(), + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + let snapshot = startup( + data_dir, + InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.example.app".into(), + label: Some("Installed Example".into()), + version_name: Some("1.0.0".into()), + version_code: Some(1), + }]), + ) + .unwrap(); + + assert_eq!(snapshot.apps[0].name.as_deref(), Some("Example")); + assert_eq!(snapshot.apps[0].latest_version.as_deref(), Some("2.0.0")); + assert_eq!(snapshot.apps[0].update_status, UpdateStatus::Available); + assert!(!snapshot + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "startup.repository_missing")); +} + +#[test] +fn startup_reports_initialized_when_an_existing_valid_database_needs_a_migration() { + let temp = tempfile::tempdir().unwrap(); + startup(temp.path(), InstalledInventory::new(vec![])).unwrap(); + + let main_db = rusqlite::Connection::open(temp.path().join("main.db")).unwrap(); + main_db + .execute( + "DELETE FROM schema_migrations WHERE id = 'main-task-v1'", + [], + ) + .unwrap(); + + let migrated = startup(temp.path(), InstalledInventory::new(vec![])).unwrap(); + + assert_eq!( + migrated.bootstrap.lifecycle, + getter_operations::startup::BootstrapLifecycle::Initialized + ); + assert!(!migrated.bootstrap.main_db.created_this_call); + assert!(migrated + .bootstrap + .diagnostics + .iter() + .any(|diagnostic| { diagnostic.code == "storage.main_migrations_applied" })); +} + +#[test] +fn startup_keeps_missing_facts_nullable_with_stable_diagnostics() { + let temp = tempfile::tempdir().unwrap(); + startup(temp.path(), InstalledInventory::new(vec![])).unwrap(); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + let package_id = "android/org.missing.app".parse().unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id, + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::MissingPackageDefinition, + }) + .unwrap(); + + let snapshot = startup(temp.path(), InstalledInventory::new(vec![])).unwrap(); + let app = &snapshot.apps[0]; + assert!(app.repository_id.is_none()); + assert!(app.installed_version.is_none()); + assert!(app.latest_version.is_none()); + let codes: Vec<_> = app.diagnostics.iter().map(|d| d.code.as_str()).collect(); + assert!(codes.contains(&"startup.package_missing")); + assert!(codes.contains(&"startup.installed_inventory_missing")); + assert_eq!(app.update_status, UpdateStatus::NotInstalled); + assert_eq!(snapshot.update_count, 0); +} + +#[test] +fn bootstrap_reports_layout_created_even_when_databases_already_exist() { + let temp = tempfile::tempdir().unwrap(); + MainDb::open(temp.path().join("main.db")).unwrap(); + CacheDb::open(temp.path().join("cache.db")).unwrap(); + + let snapshot = startup(temp.path(), InstalledInventory::new(vec![])).unwrap(); + assert_eq!( + snapshot.bootstrap.lifecycle, + getter_operations::startup::BootstrapLifecycle::Initialized + ); + assert!(!snapshot.bootstrap.main_db.created_this_call); + assert!(!snapshot.bootstrap.cache_db.created_this_call); + assert!(snapshot.bootstrap.repo.join("metadata.jsonc").is_file()); + assert!(snapshot.bootstrap.rc.is_dir()); +} + +#[test] +fn startup_reads_warm_github_cache_without_transport_and_normalizes_updates() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + startup(data_dir, InstalledInventory::new(vec![])).unwrap(); + let repo = data_dir.join("repo/official"); + let package = repo.join("android/com.example.app"); + fs::create_dir_all(&package).unwrap(); + fs::write(package.join("Manifest"), "").unwrap(); + fs::write( + package.join("metadata.jsonc"), + r#"{ + "type":"android:app", "display_name":"Example", + "android":{"package_name":"com.example.app"}, + "lua":{"9999.lua":{"permission":["allow_free_network"]}} + }"#, + ) + .unwrap(); + fs::write( + package.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local result = getter.provider.github.release_candidates { + owner = "example", repo = "app", asset = { include = "app.apk" }, +} +return package_version { name="Example", source_priority={"github"}, updates=result.candidates } +"#, + ) + .unwrap(); + let metadata = RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".into(), + priority: RepositoryPriority::new(0), + api_version: "v1".into(), + }; + MainDb::open(data_dir.join("main.db")) + .unwrap() + .upsert_repository(&metadata, Some(&repo), None) + .unwrap(); + let package_id = "android/com.example.app".parse().unwrap(); + MainDb::open(data_dir.join("main.db")) + .unwrap() + .upsert_tracked_package(&TrackedPackageUpsert { + package_id, + enabled: true, + favorite: false, + pin_version: None, + repository_id: Some(metadata.id), + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + let config = getter_operations::github_releases::GithubReleaseConfig { + api_base_url: getter_operations::github_releases::DEFAULT_GITHUB_API_BASE_URL.into(), + owner: "example".into(), + repo: "app".into(), + }; + CacheDb::open(data_dir.join("cache.db")).unwrap().upsert_provider_response(&ProviderResponseUpsert { + cache_key: config.cache_key(), provider: "github".into(), + response_json: json!([{"tag_name":"2.0.0","name":"2.0.0","draft":false,"prerelease":false, + "published_at":"2026-01-01T00:00:00Z","assets":[{"name":"app.apk","browser_download_url":"https://example/app.apk","size":1}]}]), + source_response_sha512: vec![], provenance_schema_version: None, freshness_json: json!({}), + }).unwrap(); + + let snapshot = startup( + data_dir, + InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "com.example.app".into(), + label: None, + version_name: Some("1.0.0".into()), + version_code: None, + }]), + ) + .unwrap(); + assert_eq!(snapshot.apps[0].latest_version.as_deref(), Some("2.0.0")); + assert_eq!(snapshot.apps[0].update_status, UpdateStatus::Available); + assert_eq!(snapshot.update_count, 1); +} diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index e2b2c7e..ebe139e 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -14,6 +14,11 @@ use serde_json::Value; use std::path::Path; use std::str::FromStr; +pub const MAIN_STORAGE_CONTRACT_VERSION: u32 = 1; +pub const CACHE_STORAGE_CONTRACT_VERSION: u32 = 1; +pub const MAIN_APPLIED_MIGRATION_IDS: &[&str] = &["main-v1", "main-task-v1"]; +pub const CACHE_APPLIED_MIGRATION_IDS: &[&str] = &["cache-v1", "cache-provider-provenance-v1"]; + #[derive(Debug, thiserror::Error)] pub enum StorageError { #[error("sqlite error: {0}")] @@ -52,10 +57,16 @@ pub struct CacheDb { impl MainDb { pub fn open(path: impl AsRef) -> Result { + Self::open_with_migration_status(path).map(|(db, _)| db) + } + + pub fn open_with_migration_status( + path: impl AsRef, + ) -> Result<(Self, bool), StorageError> { let conn = Connection::open(path)?; let db = Self { conn }; - db.migrate()?; - Ok(db) + let migrations_applied = db.migrate()?; + Ok((db, migrations_applied)) } pub fn open_in_memory() -> Result { @@ -65,7 +76,7 @@ impl MainDb { Ok(db) } - fn migrate(&self) -> Result<(), StorageError> { + fn migrate(&self) -> Result { self.conn.execute_batch( r#" PRAGMA foreign_keys = ON; @@ -146,15 +157,14 @@ CREATE TABLE IF NOT EXISTS install_handoffs ( "package_resolution", "TEXT NOT NULL DEFAULT 'missing_package_definition'", )?; - self.conn.execute( - "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('main-v1')", - [], - )?; - self.conn.execute( - "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('main-task-v1')", - [], - )?; - Ok(()) + let mut migrations_applied = false; + for migration_id in MAIN_APPLIED_MIGRATION_IDS { + migrations_applied |= self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES (?1)", + [migration_id], + )? > 0; + } + Ok(migrations_applied) } fn ensure_column( @@ -838,10 +848,16 @@ WHERE task_id = ?4 impl CacheDb { pub fn open(path: impl AsRef) -> Result { + Self::open_with_migration_status(path).map(|(db, _)| db) + } + + pub fn open_with_migration_status( + path: impl AsRef, + ) -> Result<(Self, bool), StorageError> { let conn = Connection::open(path)?; let db = Self { conn }; - db.migrate()?; - Ok(db) + let migrations_applied = db.migrate()?; + Ok((db, migrations_applied)) } pub fn open_in_memory() -> Result { @@ -851,7 +867,7 @@ impl CacheDb { Ok(db) } - fn migrate(&self) -> Result<(), StorageError> { + fn migrate(&self) -> Result { self.conn.execute_batch( r#" PRAGMA foreign_keys = ON; @@ -893,15 +909,14 @@ CREATE TABLE IF NOT EXISTS provider_responses ( "freshness_json", "TEXT NOT NULL DEFAULT '{}'", )?; - self.conn.execute( - "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('cache-v1')", - [], - )?; - self.conn.execute( - "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('cache-provider-provenance-v1')", - [], - )?; - Ok(()) + let mut migrations_applied = false; + for migration_id in CACHE_APPLIED_MIGRATION_IDS { + migrations_applied |= self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES (?1)", + [migration_id], + )? > 0; + } + Ok(migrations_applied) } fn ensure_column( @@ -1408,6 +1423,31 @@ mod tests { use getter_core::repository::REPO_API_VERSION_V1; use getter_core::task::TaskExecutor; + #[test] + fn applied_migration_rows_match_public_constants() { + let main = MainDb::open_in_memory().unwrap(); + let main_ids = main + .conn + .prepare("SELECT id FROM schema_migrations ORDER BY rowid") + .unwrap() + .query_map([], |row| row.get::<_, String>(0)) + .unwrap() + .collect::, _>>() + .unwrap(); + assert_eq!(main_ids, MAIN_APPLIED_MIGRATION_IDS); + + let cache = CacheDb::open_in_memory().unwrap(); + let cache_ids = cache + .conn + .prepare("SELECT id FROM schema_migrations ORDER BY rowid") + .unwrap() + .query_map([], |row| row.get::<_, String>(0)) + .unwrap() + .collect::, _>>() + .unwrap(); + assert_eq!(cache_ids, CACHE_APPLIED_MIGRATION_IDS); + } + #[test] fn main_db_stores_repository_registry_ordered_by_priority() { let db = MainDb::open_in_memory().unwrap(); From 5ba6ccfb147ac468182a5cd433d6e3ecedd189fd Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Wed, 15 Jul 2026 01:50:35 +0800 Subject: [PATCH 67/72] feat(cli): inspect and check tracked apps --- crates/getter-cli/src/lib.rs | 120 +++++++ crates/getter-cli/tests/app_cli.rs | 200 +++++++++++ crates/getter-cli/tests/bdd_cli.rs | 75 +++- .../features/cli/app_inspect_check.feature | 22 ++ crates/getter-operations/src/app.rs | 186 ++++++++++ crates/getter-operations/src/lib.rs | 1 + .../getter-operations/src/provider_cache.rs | 3 +- crates/getter-operations/src/runtime.rs | 105 ++++-- crates/getter-operations/src/startup.rs | 75 +++- crates/getter-operations/tests/app.rs | 338 ++++++++++++++++++ 10 files changed, 1086 insertions(+), 39 deletions(-) create mode 100644 crates/getter-cli/tests/app_cli.rs create mode 100644 crates/getter-cli/tests/features/cli/app_inspect_check.feature create mode 100644 crates/getter-operations/src/app.rs create mode 100644 crates/getter-operations/tests/app.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 5de2e57..391419d 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -58,6 +58,14 @@ pub enum CliCommand { inventory: Option, }, AppList, + AppShow { + package_id: PackageId, + inventory: Option, + }, + AppCheck { + package_id: PackageId, + inventory: Option, + }, HubList, RepoList, RepoAdd { @@ -204,6 +212,16 @@ pub enum CliError { Runtime(String), #[error("autogen error: {0}")] Autogen(String), + #[error("app is not tracked: {0}")] + AppUntracked(String), + #[error("app is disabled: {0}")] + AppDisabled(String), + #[error("{detail}")] + AppOperation { + code: &'static str, + message: &'static str, + detail: String, + }, #[error("provider error: {0}")] Provider(String), #[error("Legacy Room export bundle is invalid")] @@ -226,6 +244,9 @@ impl CliError { | Self::Update(_) | Self::Runtime(_) | Self::Autogen(_) => ExitCode::GenericFailure, + Self::AppUntracked(_) | Self::AppDisabled(_) | Self::AppOperation { .. } => { + ExitCode::GenericFailure + } Self::Provider(_) => ExitCode::Provider, Self::Download(_) => ExitCode::Download, Self::InvalidLegacyBundle { .. } @@ -245,6 +266,9 @@ impl CliError { Self::Download(_) => "download.task_error", Self::Runtime(_) => "runtime.error", Self::Autogen(_) => "autogen.error", + Self::AppUntracked(_) => "app.untracked", + Self::AppDisabled(_) => "app.disabled", + Self::AppOperation { code, .. } => code, Self::Provider(_) => "provider.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", @@ -263,6 +287,9 @@ impl CliError { Self::Download(_) => "Getter download task operation failed", Self::Runtime(_) => "Getter runtime operation failed", Self::Autogen(_) => "Getter autogen operation failed", + Self::AppUntracked(_) => "Getter app is not tracked", + Self::AppDisabled(_) => "Getter app is disabled", + Self::AppOperation { message, .. } => message, Self::Provider(_) => "Getter provider operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { @@ -283,7 +310,10 @@ impl CliError { | Self::Download(detail) | Self::Runtime(detail) | Self::Autogen(detail) + | Self::AppUntracked(detail) + | Self::AppDisabled(detail) | Self::Provider(detail) => Some(detail.as_str()), + Self::AppOperation { detail, .. } => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } | Self::InvalidLegacyDb { .. } @@ -305,6 +335,9 @@ impl CliError { | Self::Download(_) | Self::Runtime(_) | Self::Autogen(_) + | Self::AppUntracked(_) + | Self::AppDisabled(_) + | Self::AppOperation { .. } | Self::Provider(_) => None, } } @@ -443,6 +476,41 @@ where } } [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, + [domain, command, package_id] + if domain == "app" && (command == "show" || command == "check") => + { + let package_id = parse_package_id(package_id)?; + if command == "show" { + CliCommand::AppShow { + package_id, + inventory: None, + } + } else { + CliCommand::AppCheck { + package_id, + inventory: None, + } + } + } + [domain, command, package_id, flag, inventory] + if domain == "app" + && (command == "show" || command == "check") + && flag == "--inventory" => + { + let package_id = parse_package_id(package_id)?; + let inventory = Some(PathBuf::from(inventory)); + if command == "show" { + CliCommand::AppShow { + package_id, + inventory, + } + } else { + CliCommand::AppCheck { + package_id, + inventory, + } + } + } [domain, command] if domain == "hub" && command == "list" => CliCommand::HubList, [domain, command] if domain == "repo" && command == "list" => CliCommand::RepoList, [domain, command, id, path] if domain == "repo" && command == "add" => { @@ -702,6 +770,22 @@ where Ok(CliInvocation { data_dir, command }) } +fn map_app_error(error: getter_operations::app::AppOperationError) -> CliError { + match error { + getter_operations::app::AppOperationError::Untracked(id) => { + CliError::AppUntracked(id.to_string()) + } + getter_operations::app::AppOperationError::Disabled(id) => { + CliError::AppDisabled(id.to_string()) + } + other => CliError::AppOperation { + code: other.code(), + message: other.message(), + detail: other.detail(), + }, + } +} + fn execute(invocation: CliInvocation) -> Result { match invocation.command { CliCommand::Startup { inventory } => { @@ -731,6 +815,40 @@ fn execute(invocation: CliInvocation) -> Result { let db = open_main_db(&invocation.data_dir)?; Ok(json!({ "apps": tracked_packages_json(db.tracked_packages()?) })) } + CliCommand::AppShow { + package_id, + inventory, + } => { + let inventory = match inventory { + Some(path) => read_installed_inventory(&path)?, + None => InstalledInventory::new(Vec::new()), + }; + serde_json::to_value( + getter_operations::app::inspect_app(&invocation.data_dir, inventory, &package_id) + .map_err(map_app_error)?, + ) + .map_err(|error| CliError::Storage(error.to_string())) + } + CliCommand::AppCheck { + package_id, + inventory, + } => { + let inventory = match inventory { + Some(path) => read_installed_inventory(&path)?, + None => InstalledInventory::new(Vec::new()), + }; + let mut runtime = GetterRuntime::new(); + serde_json::to_value( + getter_operations::app::check_app( + &mut runtime, + &invocation.data_dir, + inventory, + &package_id, + ) + .map_err(map_app_error)?, + ) + .map_err(|error| CliError::Storage(error.to_string())) + } CliCommand::HubList => { open_initialized_storage(&invocation.data_dir)?; Ok(json!({ "hubs": [] })) @@ -2281,6 +2399,8 @@ impl CliCommand { Self::Init => "init", Self::Startup { .. } => "startup", Self::AppList => "app list", + Self::AppShow { .. } => "app show", + Self::AppCheck { .. } => "app check", Self::HubList => "hub list", Self::RepoList => "repo list", Self::RepoAdd { .. } => "repo add", diff --git a/crates/getter-cli/tests/app_cli.rs b/crates/getter-cli/tests/app_cli.rs new file mode 100644 index 0000000..a3123de --- /dev/null +++ b/crates/getter-cli/tests/app_cli.rs @@ -0,0 +1,200 @@ +use getter_cli::{run, ExitCode}; +use getter_core::repository::RepositoryMetadata; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; +use serde_json::{json, Value}; +use std::fs; + +fn fixture(latest: &str) -> tempfile::TempDir { + let temp = tempfile::tempdir().unwrap(); + run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "init", + ]); + let package = temp.path().join("repo/official/android/com.example"); + fs::create_dir_all(&package).unwrap(); + fs::write(package.join("Manifest"), "").unwrap(); + fs::write(package.join("metadata.jsonc"), r#"{"type":"android:app","display_name":"Example","android":{"package_name":"com.example"}}"#).unwrap(); + fs::write(package.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{ {{ version="{latest}", artifacts={{{{name="app.apk",url="https://example.invalid/app.apk"}}}} }} }} }}"#)).unwrap(); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".into(), + priority: RepositoryPriority::DEFAULT, + api_version: "v1".into(), + }, + Some(&temp.path().join("repo/official")), + None, + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/com.example".parse().unwrap(), + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + fs::write(temp.path().join("inventory.json"), json!({"format":"upgradeall-installed-inventory","version":1,"items":[{"kind":"android_package","package_name":"com.example","version_name":"1"}]}).to_string()).unwrap(); + temp +} + +#[test] +fn app_show_is_normal_cache_only_detail_command() { + let temp = fixture("2"); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "show", + "android/com.example", + "--inventory", + temp.path().join("inventory.json").to_str().unwrap(), + ]); + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let envelope: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(envelope["command"], "app show"); + assert_eq!(envelope["data"]["package_id"], "android/com.example"); + assert_eq!(envelope["data"]["update_status"], "available"); + assert!(envelope["data"].get("action").is_none()); +} + +#[test] +fn app_check_returns_current_process_action_for_update() { + let temp = fixture("2"); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "check", + "android/com.example", + "--inventory", + temp.path().join("inventory.json").to_str().unwrap(), + ]); + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let envelope: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(envelope["command"], "app check"); + assert!(envelope["data"]["action"]["action_id"].is_string()); +} + +#[test] +fn app_show_without_inventory_reports_not_installed_without_inventing_version() { + let temp = fixture("2"); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "show", + "android/com.example", + ]); + let body: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(output.exit_code, ExitCode::Success); + assert_eq!(body["data"]["update_status"], "not_installed"); + assert_eq!(body["data"]["installed_version"], Value::Null); +} + +#[test] +fn app_commands_preserve_stable_inventory_and_runtime_error_envelopes() { + let temp = fixture("2"); + fs::write( + temp.path().join("invalid-inventory.json"), + json!({"format":"wrong","version":1,"items":[]}).to_string(), + ) + .unwrap(); + let invalid_inventory = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "show", + "android/com.example", + "--inventory", + temp.path().join("invalid-inventory.json").to_str().unwrap(), + ]); + let body: Value = serde_json::from_str(&invalid_inventory.stdout).unwrap(); + assert_eq!(invalid_inventory.exit_code, ExitCode::GenericFailure); + assert_eq!(body["error"]["code"], "inventory.invalid"); + assert_eq!(body["error"]["message"], "Installed inventory is invalid"); + + fs::write( + temp.path().join("repo/official/android/com.example/1.lua"), + "this is not valid lua", + ) + .unwrap(); + let runtime_failure = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "check", + "android/com.example", + "--inventory", + temp.path().join("inventory.json").to_str().unwrap(), + ]); + let body: Value = serde_json::from_str(&runtime_failure.stdout).unwrap(); + assert_eq!(runtime_failure.exit_code, ExitCode::GenericFailure); + assert_eq!(body["error"]["code"], "package.eval_error"); + assert_ne!(body["error"]["code"], "storage.error"); +} + +#[test] +fn app_show_returns_untracked_and_disabled_error_envelopes() { + let temp = fixture("2"); + let untracked = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "show", + "android/not.tracked", + ]); + let body: Value = serde_json::from_str(&untracked.stdout).unwrap(); + assert_eq!(untracked.exit_code, ExitCode::GenericFailure); + assert_eq!(body["error"]["code"], "app.untracked"); + + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/com.example".parse().unwrap(), + enabled: false, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + let disabled = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "show", + "android/com.example", + ]); + let body: Value = serde_json::from_str(&disabled.stdout).unwrap(); + assert_eq!(disabled.exit_code, ExitCode::GenericFailure); + assert_eq!(body["error"]["code"], "app.disabled"); +} + +#[test] +fn app_commands_reject_product_transport_controls() { + let temp = fixture("2"); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "check", + "android/com.example", + "--endpoint", + "https://example.invalid", + ]); + assert_eq!(output.exit_code, ExitCode::Usage); +} diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 6a38e3c..10a762f 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -1,4 +1,5 @@ use cucumber::{given, then, when, World as _}; +use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; use rusqlite::Connection; use serde_json::Value; use std::fs; @@ -541,7 +542,7 @@ fn create_package_directory_repository( .expect("write package metadata"); fs::write( package_dir.join("1.20.0.lua"), - "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } }, updates = { { version = \"1.20.0\", artifacts = { { name = \"app.apk\", url = \"https://example.invalid/app.apk\" } } } } }", ) .expect("write version Lua"); if script_count > 1 { @@ -700,6 +701,78 @@ fn run_getter_repo_validate(world: &mut CliWorld) { world.json = None; } +#[given(expr = "repository {string} is registered with priority {int}")] +fn registered_repository(world: &mut CliWorld, repo_id: String, priority: i32) { + run_getter_repo_add_with_priority(world, &repo_id, priority); +} + +#[given(expr = "package {string} is actively tracked")] +fn actively_tracked_package(world: &mut CliWorld, package_id: String) { + let db = MainDb::open(world.data_dir.as_ref().expect("data dir").join("main.db")).unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: package_id.parse().unwrap(), + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); +} + +#[when(expr = "I run getter app show for {string} with that inventory")] +fn run_app_show(world: &mut CliWorld, package_id: String) { + run_app_command(world, "show", package_id, true); +} + +#[when(expr = "I run getter app check for {string} with that inventory")] +fn run_app_check(world: &mut CliWorld, package_id: String) { + run_app_command(world, "check", package_id, true); +} + +#[when(expr = "I run getter app check for {string} without inventory")] +fn run_app_check_without_inventory(world: &mut CliWorld, package_id: String) { + run_app_command(world, "check", package_id, false); +} + +fn run_app_command(world: &mut CliWorld, command: &str, package_id: String, inventory: bool) { + let mut args = vec!["app".to_owned(), command.to_owned(), package_id]; + if inventory { + args.extend([ + "--inventory".to_owned(), + world + .inventory + .as_ref() + .expect("inventory") + .display() + .to_string(), + ]); + } + world.output = Some(run_getter(world, args)); + world.json = None; +} + +#[then(expr = "the app result names {string} and reports installed version {string}")] +fn app_result(world: &mut CliWorld, name: String, version: String) { + let json = current_json(world); + assert_eq!(json["data"]["name"], name); + assert_eq!(json["data"]["installed_version"], version); +} + +#[then("the app check reports an available update and issues an action")] +fn app_check_available(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["data"]["app"]["update_status"], "available"); + assert!(json["data"]["action"]["action_id"].is_string()); +} + +#[then(expr = "the command fails with stable error {string}")] +fn stable_error(world: &mut CliWorld, code: String) { + let json = current_json(world); + assert_eq!(json["ok"], false); + assert_eq!(json["error"]["code"], code); +} + #[when("I run getter package eval for that fixture package")] fn run_getter_package_eval(world: &mut CliWorld) { let repo_id = world diff --git a/crates/getter-cli/tests/features/cli/app_inspect_check.feature b/crates/getter-cli/tests/features/cli/app_inspect_check.feature new file mode 100644 index 0000000..f40badd --- /dev/null +++ b/crates/getter-cli/tests/features/cli/app_inspect_check.feature @@ -0,0 +1,22 @@ +Feature: Inspect and actively check a tracked app + Getter users can inspect cached app state and explicitly refresh an installed app. + + Background: + Given an initialized getter data directory + And a package-directory repository "official" with package "android/app/fdroid" + And repository "official" is registered with priority 0 + And package "android/app/fdroid" is actively tracked + + Scenario: Show cached app state + Given an installed inventory with Android app "org.fdroid.fdroid" labeled "F-Droid" + When I run getter app show for "android/app/fdroid" with that inventory + Then the app result names "F-Droid" and reports installed version "1.0.0" + + Scenario: Check an installed app for updates + Given an installed inventory with Android app "org.fdroid.fdroid" labeled "F-Droid" + When I run getter app check for "android/app/fdroid" with that inventory + Then the app check reports an available update and issues an action + + Scenario: Check requires installed inventory + When I run getter app check for "android/app/fdroid" without inventory + Then the command fails with stable error "inventory.invalid" diff --git a/crates/getter-operations/src/app.rs b/crates/getter-operations/src/app.rs new file mode 100644 index 0000000..6558150 --- /dev/null +++ b/crates/getter-operations/src/app.rs @@ -0,0 +1,186 @@ +//! Shared app inspection and explicit update-check operations. + +#[cfg(feature = "lua")] +use crate::github_releases::GithubReleaseTransport; +use crate::startup::{self, StartupAppSummary}; +use getter_core::autogen::{validate_installed_inventory, InstalledInventory}; +#[cfg(feature = "lua")] +use getter_core::runtime::GetterRuntime; +use getter_core::runtime::IssuedAction; +use getter_core::PackageId; +use getter_storage::{MainDb, StorageError, StoredTrackedPackage}; +use serde::{Deserialize, Serialize}; +use std::path::Path; +#[cfg(feature = "lua")] +use std::rc::Rc; + +#[derive(Debug, thiserror::Error)] +pub enum AppOperationError { + #[error("app is not tracked: {0}")] + Untracked(PackageId), + #[error("app is disabled: {0}")] + Disabled(PackageId), + #[error("invalid installed inventory: {0}")] + Inventory(String), + #[error("app storage failed: {0}")] + Storage(#[from] StorageError), + #[error("app startup model failed: {0}")] + Startup(#[from] startup::StartupError), + #[cfg(feature = "lua")] + #[error("app update check failed: {0}")] + Runtime(#[from] crate::runtime::RuntimeOperationError), +} + +impl AppOperationError { + pub fn code(&self) -> &'static str { + match self { + Self::Untracked(_) => "app.untracked", + Self::Disabled(_) => "app.disabled", + Self::Inventory(_) => "inventory.invalid", + Self::Storage(_) => "storage.error", + Self::Startup(error) => match error { + startup::StartupError::Io(_) => "startup.io_error", + startup::StartupError::Storage(_) => "storage.error", + startup::StartupError::Inventory(_) => "inventory.invalid", + }, + #[cfg(feature = "lua")] + Self::Runtime(error) => error.code(), + } + } + + pub fn message(&self) -> &'static str { + match self { + Self::Untracked(_) => "Getter app is not tracked", + Self::Disabled(_) => "Getter app is disabled", + Self::Inventory(_) => "Installed inventory is invalid", + Self::Storage(_) => "Getter storage operation failed", + Self::Startup(_) => "Getter startup operation failed", + #[cfg(feature = "lua")] + Self::Runtime(error) => error.message(), + } + } + + pub fn detail(&self) -> String { + match self { + Self::Untracked(id) | Self::Disabled(id) => id.to_string(), + Self::Inventory(detail) => detail.clone(), + Self::Storage(error) => error.to_string(), + Self::Startup(error) => error.to_string(), + #[cfg(feature = "lua")] + Self::Runtime(error) => error.to_string(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct AppCheckResult { + pub app: StartupAppSummary, + /// Getter-issued action valid only in the runtime that performed this check. + pub action: Option, +} + +fn tracked_package( + db: &MainDb, + package_id: &PackageId, +) -> Result { + let tracked = db + .tracked_packages()? + .into_iter() + .find(|package| &package.package_id == package_id) + .ok_or_else(|| AppOperationError::Untracked(package_id.clone()))?; + if !tracked.enabled { + return Err(AppOperationError::Disabled(package_id.clone())); + } + Ok(tracked) +} + +pub fn inspect_app( + data_dir: &Path, + inventory: InstalledInventory, + package_id: &PackageId, +) -> Result { + validate_installed_inventory(&inventory) + .map_err(|error| AppOperationError::Inventory(error.to_string()))?; + startup::bootstrap_data_dir(data_dir)?; + let db = MainDb::open(data_dir.join("main.db"))?; + let tracked = tracked_package(&db, package_id)?; + let repositories = db.repositories()?; + Ok(startup::app_summary( + data_dir, + &repositories, + &inventory, + tracked, + )) +} + +#[cfg(feature = "lua")] +pub fn check_app( + runtime: &mut GetterRuntime, + data_dir: &Path, + inventory: InstalledInventory, + package_id: &PackageId, +) -> Result { + check_app_with_github_transport( + runtime, + data_dir, + inventory, + package_id, + Some(Rc::new( + crate::github_releases::UreqGithubReleaseTransport::new(), + )), + ) +} + +#[cfg(feature = "lua")] +pub fn check_app_with_github_transport( + runtime: &mut GetterRuntime, + data_dir: &Path, + inventory: InstalledInventory, + package_id: &PackageId, + github_release_transport: Option>, +) -> Result { + validate_installed_inventory(&inventory) + .map_err(|error| AppOperationError::Inventory(error.to_string()))?; + let db = MainDb::open(data_dir.join("main.db"))?; + let tracked = tracked_package(&db, package_id)?; + let repository_id = tracked.repository_id.clone(); + let mut request = crate::runtime::RegisteredPackageUpdateActionRequest { + package_id: package_id.clone(), + repository_id, + installed_version: None, + pin_version: tracked.pin_version.clone(), + }; + let evaluated = crate::runtime::evaluate_registered_package( + data_dir, + &db, + &request, + crate::provider_cache::ProviderCacheMode::ForceRefresh, + github_release_transport, + )?; + let mut app = startup::app_summary_from_resolved( + tracked, + Some(evaluated.package.repository.to_string()), + &inventory, + evaluated.package.clone(), + ); + app.diagnostics + .extend( + evaluated + .diagnostics + .iter() + .map(|diagnostic| startup::StartupDiagnostic { + code: diagnostic.code.clone(), + message: diagnostic.message.clone(), + }), + ); + let installed_version = app.installed_version.clone().ok_or_else(|| { + AppOperationError::Inventory(format!("package {package_id} is not installed")) + })?; + request.installed_version = Some(installed_version); + let checked = + crate::runtime::issue_action_from_registered_evaluation(runtime, request, evaluated)?; + Ok(AppCheckResult { + app, + action: checked.action, + }) +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index c90e8ed..d9502d9 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -5,6 +5,7 @@ //! provide raw facts to callers, but the rules for repository coverage, //! generated package files, manifests, and tracked state live here. +pub mod app; pub mod autogen; pub mod download; pub mod fdroid_autogen; diff --git a/crates/getter-operations/src/provider_cache.rs b/crates/getter-operations/src/provider_cache.rs index d74e511..be64a28 100644 --- a/crates/getter-operations/src/provider_cache.rs +++ b/crates/getter-operations/src/provider_cache.rs @@ -6,6 +6,7 @@ //! diagnostics. use getter_storage::{CacheDb, ProviderResponseUpsert, StorageError, StoredProviderResponse}; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use sha2::{Digest, Sha512}; @@ -42,7 +43,7 @@ pub struct ProviderResponseRefresh { pub freshness_json: Value, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ProviderCacheDiagnostic { pub code: String, pub message: String, diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index fd08d33..9c2d6b4 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -18,7 +18,7 @@ use crate::lua_provider_host::{ ProviderBackedPackageEvalConfig, }; #[cfg(feature = "lua")] -use crate::provider_cache::ProviderCacheMode; +use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode}; #[cfg(feature = "lua")] use getter_core::repository::{ package_directory_cache_key, RepositoryLoadError, RepositoryPackageDirectoryLayout, @@ -38,6 +38,8 @@ use getter_providers::StaticPackageUpdatesProvider; #[cfg(feature = "lua")] use getter_storage::{MainDb, StorageError}; use serde::Deserialize; +#[cfg(feature = "lua")] +use serde::Serialize; use serde_json::{json, Value}; #[cfg(feature = "lua")] use std::path::PathBuf; @@ -188,7 +190,7 @@ pub fn issue_action_from_registered_package_json( } #[cfg(feature = "lua")] -fn issue_action_from_registered_package_json_with_github_transport( +pub fn issue_action_from_registered_package_json_with_github_transport( runtime: &mut GetterRuntime, data_dir: &std::path::Path, db: &MainDb, @@ -196,7 +198,51 @@ fn issue_action_from_registered_package_json_with_github_transport( github_release_transport: Option>, ) -> Result { let request: RegisteredPackageUpdateActionRequest = parse_request(request_json)?; - let evaluated = evaluate_registered_package(data_dir, db, &request, github_release_transport)?; + let result = issue_action_from_registered_package_with_github_transport( + runtime, + data_dir, + db, + request, + github_release_transport, + )?; + serde_json::to_value(result) + .map_err(|source| RuntimeOperationError::Serialize(source.to_string())) +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Serialize)] +pub(crate) struct RegisteredPackageActionResult { + pub package: getter_core::ResolvedPackage, + pub update: getter_core::update::OfflineUpdateCheckResult, + pub action: Option, + pub provider_calls: Vec, + pub runtime_hooks: Vec, +} + +#[cfg(feature = "lua")] +pub(crate) fn issue_action_from_registered_package_with_github_transport( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + db: &MainDb, + request: RegisteredPackageUpdateActionRequest, + github_release_transport: Option>, +) -> Result { + let evaluated = evaluate_registered_package( + data_dir, + db, + &request, + ProviderCacheMode::UseCached, + github_release_transport, + )?; + issue_action_from_registered_evaluation(runtime, request, evaluated) +} + +#[cfg(feature = "lua")] +pub(crate) fn issue_action_from_registered_evaluation( + runtime: &mut GetterRuntime, + request: RegisteredPackageUpdateActionRequest, + evaluated: RegisteredPackageEvaluation, +) -> Result { let candidates = StaticPackageUpdatesProvider.check_updates(&evaluated.package); let update = check_updates_offline( evaluated.package.id.clone(), @@ -206,25 +252,23 @@ fn issue_action_from_registered_package_json_with_github_transport( pin_version: request.pin_version, }, )?; - let action = if update.actions.is_empty() { - None - } else { - Some(runtime.issue_action(SealedActionPlan { + let action = (!update.actions.is_empty()).then(|| { + runtime.issue_action(SealedActionPlan { package_id: update.package_id.clone(), actions: update.actions.clone(), lua_object: PackageVersionLuaObject { object_id: format!("package-update:{}", update.package_id), dependency_digest: evaluated.dependency_digest, }, - })) - }; - Ok(json!({ - "package": evaluated.package, - "update": update, - "action": action.map(issued_action_json), - "provider_calls": evaluated.provider_calls, - "runtime_hooks": evaluated.runtime_hooks, - })) + }) + }); + Ok(RegisteredPackageActionResult { + package: evaluated.package, + update, + action, + provider_calls: evaluated.provider_calls, + runtime_hooks: evaluated.runtime_hooks, + }) } pub fn submit_action_json( @@ -438,14 +482,14 @@ struct OfflineUpdateActionRequest { #[cfg(feature = "lua")] #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] -struct RegisteredPackageUpdateActionRequest { - package_id: PackageId, +pub(crate) struct RegisteredPackageUpdateActionRequest { + pub package_id: PackageId, #[serde(default)] - repository_id: Option, + pub repository_id: Option, #[serde(default)] - installed_version: Option, + pub installed_version: Option, #[serde(default, alias = "ignored_version")] - pin_version: Option, + pub pin_version: Option, } #[derive(Debug, Deserialize)] @@ -490,18 +534,20 @@ struct CleanTasksRequest { } #[cfg(feature = "lua")] -struct RegisteredPackageEvaluation { - package: getter_core::ResolvedPackage, +pub(crate) struct RegisteredPackageEvaluation { + pub package: getter_core::ResolvedPackage, + pub diagnostics: Vec, dependency_digest: String, provider_calls: Vec, runtime_hooks: Vec, } #[cfg(feature = "lua")] -fn evaluate_registered_package( +pub(crate) fn evaluate_registered_package( data_dir: &std::path::Path, db: &MainDb, request: &RegisteredPackageUpdateActionRequest, + cache_mode: ProviderCacheMode, github_release_transport: Option>, ) -> Result { let repositories = db.repositories()?; @@ -532,7 +578,7 @@ fn evaluate_registered_package( &metadata, script, ProviderBackedPackageEvalConfig { - mode: ProviderCacheMode::UseCached, + mode: cache_mode, fdroid_endpoint_id: None, fdroid_endpoint_url: None, fdroid_index_xml: None, @@ -548,8 +594,17 @@ fn evaluate_registered_package( "repo:{}:package:{}:hash:{}", cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash ); + let diagnostics = provider_eval + .provider_calls + .iter() + .filter_map(|call| call.get("diagnostics")) + .filter_map(Value::as_array) + .flatten() + .filter_map(|diagnostic| serde_json::from_value(diagnostic.clone()).ok()) + .collect(); return Ok(RegisteredPackageEvaluation { package: provider_eval.package, + diagnostics, dependency_digest, provider_calls: provider_eval.provider_calls, runtime_hooks: provider_eval.runtime_hooks, diff --git a/crates/getter-operations/src/startup.rs b/crates/getter-operations/src/startup.rs index 090d7f6..c2a650a 100644 --- a/crates/getter-operations/src/startup.rs +++ b/crates/getter-operations/src/startup.rs @@ -248,7 +248,7 @@ fn diagnostic(code: &str, message: impl Into) -> StartupDiagnostic { } #[cfg_attr(not(feature = "lua"), allow(unused_mut, unused_variables))] -fn app_summary( +pub(crate) fn app_summary( data_dir: &Path, repositories: &[StoredRepository], inventory: &InstalledInventory, @@ -261,16 +261,23 @@ fn app_summary( None if tracked.package_resolution == StoredPackageResolution::OfficialRepositoryPackage => { - repositories.iter().find(|repository| { - let root = repository - .path - .as_ref() - .map(PathBuf::from) - .unwrap_or_else(|| data_dir.join("repo").join(repository.id.as_str())); - RepositoryPackageDirectoryLayout::load(&root) - .ok() - .is_some_and(|layout| layout.package(&tracked.package_id).is_some()) - }) + repositories + .iter() + .filter(|repository| { + let root = repository + .path + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| data_dir.join("repo").join(repository.id.as_str())); + RepositoryPackageDirectoryLayout::load(&root) + .ok() + .is_some_and(|layout| layout.package(&tracked.package_id).is_some()) + }) + .max_by(|left, right| { + left.priority + .cmp(&right.priority) + .then_with(|| right.id.as_str().cmp(left.id.as_str())) + }) } None => None, }; @@ -279,6 +286,7 @@ fn app_summary( .repository_id .as_ref() .and_then(|id| repositories.iter().find(|repo| &repo.id == id)); + let resolved_repository_id = repository.map(|repository| repository.id.to_string()); let mut name = None; let mut installed_target = None; let mut warning = StartupWarning::default(); @@ -363,6 +371,49 @@ fn app_summary( )); } + finish_app_summary( + tracked, + resolved_repository_id, + name, + warning, + installed_target, + candidates, + diagnostics, + inventory, + ) +} + +#[cfg(feature = "lua")] +pub(crate) fn app_summary_from_resolved( + tracked: StoredTrackedPackage, + repository_id: Option, + inventory: &InstalledInventory, + resolved: getter_core::ResolvedPackage, +) -> StartupAppSummary { + let mut warning = StartupWarning::default(); + warning.free_network = resolved.permissions.free_network; + finish_app_summary( + tracked, + repository_id, + Some(resolved.name), + warning, + resolved.installed.first().map(target_dto), + resolved.updates, + Vec::new(), + inventory, + ) +} + +fn finish_app_summary( + tracked: StoredTrackedPackage, + resolved_repository_id: Option, + name: Option, + warning: StartupWarning, + installed_target: Option, + candidates: Vec, + mut diagnostics: Vec, + inventory: &InstalledInventory, +) -> StartupAppSummary { let installed_version = installed_target .as_ref() .and_then(|target| inventory_version(inventory, target)); @@ -411,7 +462,7 @@ fn app_summary( }; StartupAppSummary { package_id: tracked.package_id.to_string(), - repository_id: tracked.repository_id.map(|id| id.to_string()), + repository_id: resolved_repository_id, name, favorite: tracked.favorite, pin_version: tracked.pin_version, diff --git a/crates/getter-operations/tests/app.rs b/crates/getter-operations/tests/app.rs new file mode 100644 index 0000000..c076862 --- /dev/null +++ b/crates/getter-operations/tests/app.rs @@ -0,0 +1,338 @@ +#![cfg(feature = "lua")] + +use getter_core::autogen::{InstalledInventory, InstalledInventoryItem}; +use getter_core::repository::RepositoryMetadata; +use getter_core::runtime::GetterRuntime; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_operations::app::{ + check_app, check_app_with_github_transport, inspect_app, AppOperationError, +}; +use getter_operations::github_releases::{ + GithubReleaseTransport, GithubReleaseTransportError, GithubReleaseTransportRequest, + GithubReleaseTransportResponse, +}; +use getter_storage::{ + CacheDb, MainDb, ProviderResponseUpsert, StoredPackageResolution, TrackedPackageUpsert, +}; +use serde_json::json; +use std::cell::RefCell; +use std::fs; +use std::rc::Rc; + +fn write_package(root: &std::path::Path, name: &str, latest: &str) { + fs::create_dir_all(root).unwrap(); + fs::write(root.join("Manifest"), "").unwrap(); + fs::write(root.join("metadata.jsonc"), format!(r#"{{"type":"android:app","display_name":"{name}","android":{{"package_name":"com.example"}}}}"#)).unwrap(); + fs::write(root.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{ {{ version="{latest}", artifacts={{{{name="app.apk",url="https://example.invalid/app.apk"}}}} }} }} }}"#)).unwrap(); +} + +fn register(data: &std::path::Path, repo: &str, priority: i32, explicit: bool, enabled: bool) { + let db = MainDb::open(data.join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new(repo).unwrap(), + name: repo.into(), + priority: RepositoryPriority::new(priority), + api_version: "v1".into(), + }, + Some(&data.join("repo").join(repo)), + None, + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/com.example".parse().unwrap(), + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + repository_id: explicit.then(|| RepositoryId::new(repo).unwrap()), + enabled, + favorite: false, + pin_version: None, + }) + .unwrap(); +} + +const GITHUB_RELEASES: &str = r#"[{"tag_name":"2","draft":false,"prerelease":false,"assets":[{"name":"example.apk","browser_download_url":"https://example.invalid/example-v2.apk"}]}]"#; + +struct MockGithubTransport { + requests: RefCell, +} + +struct FailingGithubTransport; + +impl GithubReleaseTransport for FailingGithubTransport { + fn fetch_releases( + &self, + _request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + Err(GithubReleaseTransportError::Transport("offline".into())) + } +} + +impl GithubReleaseTransport for MockGithubTransport { + fn fetch_releases( + &self, + _request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + *self.requests.borrow_mut() += 1; + Ok(GithubReleaseTransportResponse { + body: GITHUB_RELEASES.into(), + etag: None, + last_modified: None, + }) + } +} + +fn write_github_package(root: &std::path::Path) { + fs::create_dir_all(root).unwrap(); + fs::write(root.join("Manifest"), "14be2aa0ad6385271f005345095584d79556562f96cdcb7c25f500f5f35246fccacb3bbfbc38f8df48c512cee853928aad8b4fb7138f4bf9c8b5835b7e46838a github\n").unwrap(); + fs::write( + root.join("metadata.jsonc"), + r#"{"type":"android:app","android":{"package_name":"com.example"}}"#, + ) + .unwrap(); + fs::write( + root.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "Example", + android_package = "com.example", + owner = "owner", + repo = "repo", + asset = { include = "[.]apk$" }, +}"#, + ) + .unwrap(); +} + +fn inventory(version: &str) -> InstalledInventory { + InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "com.example".into(), + label: None, + version_name: Some(version.into()), + version_code: None, + }]) +} + +#[test] +fn inspect_uses_repository_priority_and_is_cache_only() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + write_package( + &temp.path().join("repo/low/android/com.example"), + "Low", + "2", + ); + write_package( + &temp.path().join("repo/high/android/com.example"), + "High", + "3", + ); + register(temp.path(), "low", 0, false, true); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("high").unwrap(), + name: "high".into(), + priority: RepositoryPriority::new(10), + api_version: "v1".into(), + }, + Some(&temp.path().join("repo/high")), + None, + ) + .unwrap(); + + let app = inspect_app( + temp.path(), + inventory("1"), + &"android/com.example".parse().unwrap(), + ) + .unwrap(); + assert_eq!(app.repository_id.as_deref(), Some("high")); + assert_eq!(app.name.as_deref(), Some("High")); + assert_eq!(app.latest_version.as_deref(), Some("3")); +} + +#[test] +fn inspect_honors_explicit_binding_and_reports_missing_inventory() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + write_package( + &temp.path().join("repo/low/android/com.example"), + "Low", + "2", + ); + register(temp.path(), "low", 0, true, true); + let app = inspect_app( + temp.path(), + InstalledInventory::new(vec![]), + &"android/com.example".parse().unwrap(), + ) + .unwrap(); + assert_eq!(app.repository_id.as_deref(), Some("low")); + assert_eq!( + app.update_status, + getter_operations::startup::UpdateStatus::NotInstalled + ); + assert!(app + .diagnostics + .iter() + .any(|d| d.code == "startup.installed_inventory_missing")); +} + +#[test] +fn inspect_rejects_untracked_and_disabled_packages() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + let id = "android/com.example".parse().unwrap(); + assert!(matches!( + inspect_app(temp.path(), InstalledInventory::new(vec![]), &id), + Err(AppOperationError::Untracked(_)) + )); + write_package( + &temp.path().join("repo/official/android/com.example"), + "Example", + "2", + ); + register(temp.path(), "official", 0, true, false); + assert!(matches!( + inspect_app(temp.path(), InstalledInventory::new(vec![]), &id), + Err(AppOperationError::Disabled(_)) + )); +} + +#[test] +fn inspect_reports_missing_package_definition_without_inventing_details() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + register(temp.path(), "official", 0, true, true); + let app = inspect_app( + temp.path(), + InstalledInventory::new(vec![]), + &"android/com.example".parse().unwrap(), + ) + .unwrap(); + assert_eq!(app.name, None); + assert_eq!(app.latest_version, None); + assert_eq!( + app.update_status, + getter_operations::startup::UpdateStatus::NotInstalled + ); + assert!(!app.diagnostics.is_empty()); +} + +#[test] +fn check_preserves_stale_cache_refresh_diagnostics_on_the_app() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + write_github_package(&temp.path().join("repo/official/android/com.example")); + register(temp.path(), "official", 0, false, true); + let config = getter_operations::github_releases::GithubReleaseConfig { + api_base_url: getter_operations::github_releases::DEFAULT_GITHUB_API_BASE_URL.into(), + owner: "owner".into(), + repo: "repo".into(), + }; + CacheDb::open(temp.path().join("cache.db")) + .unwrap() + .upsert_provider_response(&ProviderResponseUpsert { + cache_key: config.cache_key(), + provider: "github".into(), + response_json: serde_json::from_str(GITHUB_RELEASES).unwrap(), + source_response_sha512: vec!["14be2aa0ad6385271f005345095584d79556562f96cdcb7c25f500f5f35246fccacb3bbfbc38f8df48c512cee853928aad8b4fb7138f4bf9c8b5835b7e46838a".into()], + provenance_schema_version: Some("provider-response-provenance-v1".into()), + freshness_json: json!({}), + }) + .unwrap(); + + let checked = check_app_with_github_transport( + &mut GetterRuntime::new(), + temp.path(), + inventory("1"), + &"android/com.example".parse().unwrap(), + Some(Rc::new(FailingGithubTransport)), + ) + .unwrap(); + + let codes = checked + .app + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect::>(); + assert!(codes.contains(&"cache.refresh_failed")); + assert!(codes.contains(&"used_stale_cache")); + assert!(checked + .app + .diagnostics + .iter() + .all(|diagnostic| !diagnostic.message.contains("api.github.com"))); +} + +#[test] +fn check_refreshes_cold_github_cache_before_matching_inventory_and_issuing_action() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + write_github_package(&temp.path().join("repo/official/android/com.example")); + register(temp.path(), "official", 0, false, true); + let transport = Rc::new(MockGithubTransport { + requests: RefCell::new(0), + }); + let mut runtime = GetterRuntime::new(); + + let checked = check_app_with_github_transport( + &mut runtime, + temp.path(), + inventory("1"), + &"android/com.example".parse().unwrap(), + Some(transport.clone()), + ) + .unwrap(); + + assert_eq!(*transport.requests.borrow(), 1); + assert_eq!(checked.app.installed_version.as_deref(), Some("1")); + assert_eq!(checked.app.latest_version.as_deref(), Some("2")); + assert_eq!( + checked.app.update_status, + getter_operations::startup::UpdateStatus::Available + ); + let action = checked.action.expect("update action"); + assert_eq!(action.package_id.to_string(), "android/com.example"); +} + +#[test] +fn check_issues_current_process_action_only_when_update_exists() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + write_package( + &temp.path().join("repo/official/android/com.example"), + "Example", + "2", + ); + register(temp.path(), "official", 0, true, true); + let mut runtime = GetterRuntime::new(); + let updated = check_app( + &mut runtime, + temp.path(), + inventory("1"), + &"android/com.example".parse().unwrap(), + ) + .unwrap(); + assert_eq!( + updated.app.update_status, + getter_operations::startup::UpdateStatus::Available + ); + assert!(updated.action.is_some()); + + let current = check_app( + &mut runtime, + temp.path(), + inventory("2"), + &"android/com.example".parse().unwrap(), + ) + .unwrap(); + assert_eq!( + current.app.update_status, + getter_operations::startup::UpdateStatus::UpToDate + ); + assert!(current.action.is_none()); +} From 3365386ab980a3d24e1a88a8358a6180f32aa5e0 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Wed, 15 Jul 2026 23:06:57 +0800 Subject: [PATCH 68/72] feat(download): stage package artifacts --- crates/getter-cli/src/lib.rs | 24 +- crates/getter-cli/tests/app_cli.rs | 91 +++++ crates/getter-cli/tests/bdd_cli.rs | 93 +++++ .../features/cli/app_inspect_check.feature | 13 + crates/getter-core/src/lib.rs | 1 + crates/getter-core/src/manifest.rs | 108 +++++ crates/getter-core/tests/package_manifest.rs | 62 +++ crates/getter-operations/src/app.rs | 375 ++++++++++++++++- .../getter-operations/src/fdroid_autogen.rs | 71 +++- .../getter-operations/src/github_autogen.rs | 128 +++++- .../getter-operations/src/lua_http_policy.rs | 46 +-- .../src/lua_provider_host.rs | 45 +-- crates/getter-operations/src/runtime.rs | 2 + .../tests/artifact_staging.rs | 381 ++++++++++++++++++ 14 files changed, 1345 insertions(+), 95 deletions(-) create mode 100644 crates/getter-core/src/manifest.rs create mode 100644 crates/getter-core/tests/package_manifest.rs create mode 100644 crates/getter-operations/tests/artifact_staging.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 391419d..470a360 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -66,6 +66,9 @@ pub enum CliCommand { package_id: PackageId, inventory: Option, }, + AppDownload { + package_id: PackageId, + }, HubList, RepoList, RepoAdd { @@ -477,19 +480,20 @@ where } [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, [domain, command, package_id] - if domain == "app" && (command == "show" || command == "check") => + if domain == "app" + && (command == "show" || command == "check" || command == "download") => { let package_id = parse_package_id(package_id)?; - if command == "show" { - CliCommand::AppShow { + match command.as_str() { + "show" => CliCommand::AppShow { package_id, inventory: None, - } - } else { - CliCommand::AppCheck { + }, + "check" => CliCommand::AppCheck { package_id, inventory: None, - } + }, + _ => CliCommand::AppDownload { package_id }, } } [domain, command, package_id, flag, inventory] @@ -829,6 +833,11 @@ fn execute(invocation: CliInvocation) -> Result { ) .map_err(|error| CliError::Storage(error.to_string())) } + CliCommand::AppDownload { package_id } => { + let result = getter_operations::app::download_app(&invocation.data_dir, &package_id) + .map_err(map_app_error)?; + serde_json::to_value(result).map_err(|error| CliError::Storage(error.to_string())) + } CliCommand::AppCheck { package_id, inventory, @@ -2401,6 +2410,7 @@ impl CliCommand { Self::AppList => "app list", Self::AppShow { .. } => "app show", Self::AppCheck { .. } => "app check", + Self::AppDownload { .. } => "app download", Self::HubList => "hub list", Self::RepoList => "repo list", Self::RepoAdd { .. } => "repo add", diff --git a/crates/getter-cli/tests/app_cli.rs b/crates/getter-cli/tests/app_cli.rs index a3123de..aa354d1 100644 --- a/crates/getter-cli/tests/app_cli.rs +++ b/crates/getter-cli/tests/app_cli.rs @@ -4,6 +4,8 @@ use getter_core::{RepositoryId, RepositoryPriority}; use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; use serde_json::{json, Value}; use std::fs; +use std::io::{Read, Write}; +use std::net::TcpListener; fn fixture(latest: &str) -> tempfile::TempDir { let temp = tempfile::tempdir().unwrap(); @@ -44,6 +46,95 @@ return package_version {{ updates = {{ {{ version="{latest}", artifacts={{{{name temp } +#[test] +fn app_download_stages_once_then_reuses_the_verified_artifact() { + let bytes = b"deterministic artifact bytes"; + let digest = "3ac60f26821afbddb63263c8ec6eae881d2d2e77e2674c5c8c84e6404077b3a9"; + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let server = std::thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut request = [0; 1024]; + let _ = stream.read(&mut request).unwrap(); + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + bytes.len() + ) + .unwrap(); + stream.write_all(bytes).unwrap(); + }); + let temp = fixture("2"); + let package = temp.path().join("repo/official/android/com.example"); + fs::write(package.join("Manifest"), format!("{digest} app.apk\n")).unwrap(); + fs::write( + package.join("1.lua"), + format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{ {{ version="2", artifacts={{{{name="app.apk",url="http://{address}/app.apk",file_name="app.apk"}}}} }} }} }}"#), + ) + .unwrap(); + + let first = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "download", + "android/com.example", + ]); + assert_eq!(first.exit_code, ExitCode::Success); + let first_json: Value = serde_json::from_str(&first.stdout).unwrap(); + assert_eq!(first_json["data"]["artifacts"][0]["status"], "downloaded"); + server.join().unwrap(); + + let second = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "download", + "android/com.example", + ]); + assert_eq!(second.exit_code, ExitCode::Success); + let second_json: Value = serde_json::from_str(&second.stdout).unwrap(); + assert_eq!(second_json["data"]["artifacts"][0]["status"], "reused"); + assert_eq!( + second_json["data"]["artifacts"][0]["path"], + first_json["data"]["artifacts"][0]["path"] + ); +} + +#[test] +fn app_download_rejects_transport_controls_and_maps_manifest_errors_stably() { + let temp = fixture("2"); + let controlled = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "download", + "android/com.example", + "--url", + "https://attacker.invalid/app.apk", + ]); + assert_eq!(controlled.exit_code, ExitCode::Usage); + let controlled_body: Value = serde_json::from_str(&controlled.stdout).unwrap(); + assert_eq!(controlled_body["error"]["code"], "cli.usage"); + + let missing = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "download", + "android/com.example", + ]); + assert_eq!(missing.exit_code, ExitCode::GenericFailure); + let missing_body: Value = serde_json::from_str(&missing.stdout).unwrap(); + assert_eq!(missing_body["error"]["code"], "artifact.manifest_missing"); + assert_ne!(missing_body["error"]["code"], "storage.error"); +} + #[test] fn app_show_is_normal_cache_only_detail_command() { let temp = fixture("2"); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 10a762f..2e480bc 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -3,6 +3,8 @@ use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; use rusqlite::Connection; use serde_json::Value; use std::fs; +use std::io::{Read, Write}; +use std::net::TcpListener; use std::path::PathBuf; use std::process::Command; use tempfile::TempDir; @@ -27,6 +29,8 @@ struct CliWorld { fixture_repo_id: Option, fixture_repo_path: Option, fixture_package_id: Option, + local_artifact_server: Option>, + first_download_json: Option, output: Option, json: Option, } @@ -735,6 +739,80 @@ fn run_app_check_without_inventory(world: &mut CliWorld, package_id: String) { run_app_command(world, "check", package_id, false); } +#[given("the package has a deterministic locally served artifact with a Manifest checksum")] +fn deterministic_local_artifact(world: &mut CliWorld) { + let bytes = b"BDD deterministic artifact"; + let digest = "9951fef5dd31f26c142ff2dcb6de2c56a58a3873c0211fdbaaa9e792d22d4e59"; + let listener = TcpListener::bind("127.0.0.1:0").expect("bind local fixture server"); + let address = listener.local_addr().expect("fixture address"); + world.local_artifact_server = Some(std::thread::spawn(move || { + let (mut stream, _) = listener.accept().expect("fixture request"); + let mut request = [0; 1024]; + let _ = stream.read(&mut request).expect("read fixture request"); + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + bytes.len() + ) + .expect("write fixture response headers"); + stream.write_all(bytes).expect("write fixture response"); + })); + let package_dir = world + .fixture_repo_path + .as_ref() + .expect("fixture repository") + .join( + world + .fixture_package_id + .as_ref() + .expect("fixture package") + .replace('/', std::path::MAIN_SEPARATOR_STR), + ); + fs::write(package_dir.join("Manifest"), format!("{digest} app.apk\n")) + .expect("write artifact Manifest"); + fs::write( + package_dir.join("1.20.0.lua"), + format!(r#"#!/bin/upa-lua v1 +return package_version {{ installed = {{ {{ kind = "android_package", package_name = "org.fdroid.fdroid" }} }}, updates = {{ {{ version = "1.20.0", artifacts = {{ {{ name = "app.apk", url = "http://{address}/app.apk", file_name = "app.apk" }} }} }} }} }}"#), + ) + .expect("write local artifact package"); +} + +#[when(expr = "I run getter app download for {string}")] +fn run_app_download(world: &mut CliWorld, package_id: String) { + run_app_command(world, "download", package_id, false); +} + +#[when(expr = "I run getter app download twice for {string}")] +fn run_app_download_twice(world: &mut CliWorld, package_id: String) { + run_app_command(world, "download", package_id.clone(), false); + let first = world.output.as_ref().expect("first output"); + assert_success(first); + world.first_download_json = Some(parse_stdout(first)); + world + .local_artifact_server + .take() + .expect("fixture server") + .join() + .expect("fixture server succeeds"); + run_app_command(world, "download", package_id, false); +} + +#[when(expr = "I run getter app download for {string} with a URL control")] +fn run_app_download_with_url_control(world: &mut CliWorld, package_id: String) { + world.output = Some(run_getter( + world, + [ + "app".to_owned(), + "download".to_owned(), + package_id, + "--url".to_owned(), + "https://attacker.invalid/app.apk".to_owned(), + ], + )); + world.json = None; +} + fn run_app_command(world: &mut CliWorld, command: &str, package_id: String, inventory: bool) { let mut args = vec!["app".to_owned(), command.to_owned(), package_id]; if inventory { @@ -766,6 +844,21 @@ fn app_check_available(world: &mut CliWorld) { assert!(json["data"]["action"]["action_id"].is_string()); } +#[then("the first download is downloaded and the second is reused from the same path")] +fn download_then_reuse(world: &mut CliWorld) { + let first = world + .first_download_json + .clone() + .expect("first download result"); + let second = current_json(world).clone(); + assert_eq!(first["data"]["artifacts"][0]["status"], "downloaded"); + assert_eq!(second["data"]["artifacts"][0]["status"], "reused"); + assert_eq!( + first["data"]["artifacts"][0]["path"], + second["data"]["artifacts"][0]["path"] + ); +} + #[then(expr = "the command fails with stable error {string}")] fn stable_error(world: &mut CliWorld, code: String) { let json = current_json(world); diff --git a/crates/getter-cli/tests/features/cli/app_inspect_check.feature b/crates/getter-cli/tests/features/cli/app_inspect_check.feature index f40badd..841ecf3 100644 --- a/crates/getter-cli/tests/features/cli/app_inspect_check.feature +++ b/crates/getter-cli/tests/features/cli/app_inspect_check.feature @@ -20,3 +20,16 @@ Feature: Inspect and actively check a tracked app Scenario: Check requires installed inventory When I run getter app check for "android/app/fdroid" without inventory Then the command fails with stable error "inventory.invalid" + + Scenario: Download requires an artifact Manifest checksum + When I run getter app download for "android/app/fdroid" + Then the command fails with stable error "artifact.manifest_missing" + + Scenario: Download stages an artifact and reuses it on the next invocation + Given the package has a deterministic locally served artifact with a Manifest checksum + When I run getter app download twice for "android/app/fdroid" + Then the first download is downloaded and the second is reused from the same path + + Scenario: Download rejects transport controls + When I run getter app download for "android/app/fdroid" with a URL control + Then the command fails with stable error "cli.usage" diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 3fce46b..0360c2b 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -14,6 +14,7 @@ pub mod autogen; pub mod diagnostics; #[cfg(feature = "lua")] pub mod lua; +pub mod manifest; pub mod repository; pub mod runtime; pub mod task; diff --git a/crates/getter-core/src/manifest.rs b/crates/getter-core/src/manifest.rs new file mode 100644 index 0000000..6a716c5 --- /dev/null +++ b/crates/getter-core/src/manifest.rs @@ -0,0 +1,108 @@ +use std::collections::HashMap; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ManifestAlgorithm { + Sha256, + Sha512, +} + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct PackageManifest { + entries: HashMap<(ManifestAlgorithm, String), Vec>, +} + +#[derive(Debug, thiserror::Error, PartialEq, Eq)] +pub enum ManifestError { + #[error("malformed Manifest line {line}: {reason}")] + Malformed { line: usize, reason: String }, + #[error("duplicate Manifest entry for '{file_name}'")] + Duplicate { file_name: String }, +} + +impl PackageManifest { + pub fn parse(content: &str) -> Result { + let mut entries: HashMap<(ManifestAlgorithm, String), Vec> = HashMap::new(); + for (index, raw_line) in content.lines().enumerate() { + let line_number = index + 1; + let line = raw_line.trim(); + if line.is_empty() { + continue; + } + let Some(separator) = line.find(char::is_whitespace) else { + return Err(ManifestError::Malformed { + line: line_number, + reason: "expected ' '".to_owned(), + }); + }; + let digest = &line[..separator]; + let file_name = line[separator..].trim_start(); + if file_name.is_empty() || file_name.chars().any(char::is_control) { + return Err(ManifestError::Malformed { + line: line_number, + reason: "file name is empty or contains a control character".to_owned(), + }); + } + let algorithm = match digest.len() { + 64 => ManifestAlgorithm::Sha256, + 128 => ManifestAlgorithm::Sha512, + _ => { + return Err(ManifestError::Malformed { + line: line_number, + reason: "digest must be 64-hex SHA-256 or 128-hex SHA-512".to_owned(), + }) + } + }; + if !digest.bytes().all(|byte| byte.is_ascii_hexdigit()) { + return Err(ManifestError::Malformed { + line: line_number, + reason: "digest contains non-hex characters".to_owned(), + }); + } + let digest = digest.to_ascii_lowercase(); + let key = (algorithm, file_name.to_owned()); + let existing = entries.entry(key).or_default(); + match algorithm { + ManifestAlgorithm::Sha256 if existing.contains(&digest) => { + return Err(ManifestError::Duplicate { + file_name: file_name.to_owned(), + }); + } + ManifestAlgorithm::Sha512 if existing.contains(&digest) => continue, + _ => existing.push(digest), + } + } + Ok(Self { entries }) + } + + pub fn artifact_sha256(&self, file_name: &str) -> Option<&str> { + self.artifact_sha256_members(file_name) + .first() + .map(String::as_str) + } + + pub fn artifact_sha256_members(&self, file_name: &str) -> &[String] { + self.digests(file_name, ManifestAlgorithm::Sha256) + .unwrap_or_default() + } + + pub fn source_sha512(&self, file_name: &str) -> Option<&str> { + self.digests(file_name, ManifestAlgorithm::Sha512) + .and_then(|digests| digests.first()) + .map(String::as_str) + } + + pub fn contains_sha512(&self, digest: &str) -> bool { + self.entries.iter().any(|((algorithm, _), digests)| { + *algorithm == ManifestAlgorithm::Sha512 + && digests + .iter() + .any(|entry| entry.eq_ignore_ascii_case(digest)) + }) + } + + fn digests(&self, file_name: &str, algorithm: ManifestAlgorithm) -> Option<&[String]> { + self.entries + .get(&(algorithm, file_name.to_owned())) + .map(Vec::as_slice) + } +} diff --git a/crates/getter-core/tests/package_manifest.rs b/crates/getter-core/tests/package_manifest.rs new file mode 100644 index 0000000..d890869 --- /dev/null +++ b/crates/getter-core/tests/package_manifest.rs @@ -0,0 +1,62 @@ +use getter_core::manifest::{ManifestError, PackageManifest}; + +#[test] +fn parses_mixed_source_sha512_and_artifact_sha256_entries() { + let source = "a".repeat(128); + let artifact = "B".repeat(64); + let manifest = PackageManifest::parse(&format!( + "{source} github-releases.json\n{artifact} release/path/App.apk\n" + )) + .unwrap(); + + assert_eq!( + manifest.artifact_sha256("release/path/App.apk").unwrap(), + "b".repeat(64) + ); + assert_eq!( + manifest.source_sha512("github-releases.json").unwrap(), + source + ); +} + +#[test] +fn duplicate_artifact_filename_is_rejected_even_when_digest_matches() { + let digest = "a".repeat(64); + let error = + PackageManifest::parse(&format!("{digest} app.apk\n{digest} app.apk\n")).unwrap_err(); + + assert!(matches!(error, ManifestError::Duplicate { .. })); +} + +#[test] +fn sha256_and_multiple_sha512_memberships_can_share_one_filename() { + let artifact = "a".repeat(64); + let first_source = "b".repeat(128); + let second_source = "c".repeat(128); + let manifest = PackageManifest::parse(&format!( + "{artifact} app.apk\n{first_source} app.apk\n{second_source} app.apk\n" + )) + .unwrap(); + + assert_eq!(manifest.artifact_sha256("app.apk"), Some(artifact.as_str())); + assert!(manifest.contains_sha512(&first_source)); + assert!(manifest.contains_sha512(&second_source)); +} + +#[test] +fn preserves_multiple_distinct_sha256_memberships_for_one_filename() { + let first = "a".repeat(64); + let second = "b".repeat(64); + let manifest = PackageManifest::parse(&format!("{first} app.apk\n{second} app.apk\n")).unwrap(); + + assert_eq!( + manifest.artifact_sha256_members("app.apk"), + &[first, second] + ); +} + +#[test] +fn malformed_lines_are_rejected() { + let error = PackageManifest::parse("not-a-digest app.apk\n").unwrap_err(); + assert!(matches!(error, ManifestError::Malformed { line: 1, .. })); +} diff --git a/crates/getter-operations/src/app.rs b/crates/getter-operations/src/app.rs index 6558150..2550d19 100644 --- a/crates/getter-operations/src/app.rs +++ b/crates/getter-operations/src/app.rs @@ -5,14 +5,26 @@ use crate::github_releases::GithubReleaseTransport; use crate::startup::{self, StartupAppSummary}; use getter_core::autogen::{validate_installed_inventory, InstalledInventory}; #[cfg(feature = "lua")] +use getter_core::manifest::{ManifestError, PackageManifest}; +#[cfg(feature = "lua")] +use getter_core::repository::PACKAGE_MANIFEST_FILE; +#[cfg(feature = "lua")] use getter_core::runtime::GetterRuntime; use getter_core::runtime::IssuedAction; +#[cfg(feature = "lua")] +use getter_core::update::compare_versions; use getter_core::PackageId; +#[cfg(feature = "lua")] +use getter_core::UpdateArtifact; use getter_storage::{MainDb, StorageError, StoredTrackedPackage}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "lua")] +use sha2::{Digest, Sha256}; use std::path::Path; #[cfg(feature = "lua")] -use std::rc::Rc; +use std::path::PathBuf; +#[cfg(feature = "lua")] +use std::{fs, io::Write, rc::Rc}; #[derive(Debug, thiserror::Error)] pub enum AppOperationError { @@ -29,6 +41,40 @@ pub enum AppOperationError { #[cfg(feature = "lua")] #[error("app update check failed: {0}")] Runtime(#[from] crate::runtime::RuntimeOperationError), + #[cfg(feature = "lua")] + #[error("selected package has no download candidates")] + NoCandidates, + #[cfg(feature = "lua")] + #[error("package Manifest is missing")] + ManifestMissing, + #[cfg(feature = "lua")] + #[error("package Manifest is invalid: {0}")] + ManifestInvalid(ManifestError), + #[cfg(feature = "lua")] + #[error("artifact '{artifact}' filename '{file_name}' has no matching SHA-256 Manifest entry")] + ManifestArtifactMissing { artifact: String, file_name: String }, + #[cfg(feature = "lua")] + #[error("artifact '{artifact}' filename '{file_name}' has multiple SHA-256 Manifest entries and no digest hint")] + ManifestArtifactAmbiguous { artifact: String, file_name: String }, + #[cfg(feature = "lua")] + #[error("artifact '{artifact}' carries an invalid SHA-256 digest")] + ArtifactSha256Invalid { artifact: String }, + #[cfg(feature = "lua")] + #[error("artifact filenames '{first}' and '{second}' resolve to the same staging path")] + ArtifactPathCollision { first: String, second: String }, + #[cfg(feature = "lua")] + #[error("artifact '{artifact}' SHA-256 mismatch: expected {expected}, got {actual}")] + Sha256Mismatch { + artifact: String, + expected: String, + actual: String, + }, + #[cfg(feature = "lua")] + #[error("artifact staging I/O failed: {0}")] + ArtifactIo(#[from] std::io::Error), + #[cfg(feature = "lua")] + #[error("artifact download failed: {0}")] + ArtifactTransport(#[from] crate::download::RuntimeDownloadTransportError), } impl AppOperationError { @@ -45,6 +91,29 @@ impl AppOperationError { }, #[cfg(feature = "lua")] Self::Runtime(error) => error.code(), + #[cfg(feature = "lua")] + Self::NoCandidates => "app.no_candidates", + #[cfg(feature = "lua")] + Self::ManifestMissing => "artifact.manifest_missing", + #[cfg(feature = "lua")] + Self::ManifestInvalid(source) => match source { + ManifestError::Malformed { .. } => "artifact.manifest_malformed", + ManifestError::Duplicate { .. } => "artifact.manifest_duplicate", + }, + #[cfg(feature = "lua")] + Self::ManifestArtifactMissing { .. } => "artifact.manifest_missing", + #[cfg(feature = "lua")] + Self::ManifestArtifactAmbiguous { .. } => "artifact.manifest_ambiguous", + #[cfg(feature = "lua")] + Self::ArtifactSha256Invalid { .. } => "artifact.sha256_invalid", + #[cfg(feature = "lua")] + Self::ArtifactPathCollision { .. } => "artifact.path_collision", + #[cfg(feature = "lua")] + Self::Sha256Mismatch { .. } => "artifact.sha256_mismatch", + #[cfg(feature = "lua")] + Self::ArtifactIo(_) => "artifact.io_error", + #[cfg(feature = "lua")] + Self::ArtifactTransport(_) => "artifact.transport_error", } } @@ -57,6 +126,26 @@ impl AppOperationError { Self::Startup(_) => "Getter startup operation failed", #[cfg(feature = "lua")] Self::Runtime(error) => error.message(), + #[cfg(feature = "lua")] + Self::NoCandidates => "Getter app has no download candidate", + #[cfg(feature = "lua")] + Self::ManifestMissing => "Getter package Manifest is missing", + #[cfg(feature = "lua")] + Self::ManifestInvalid(_) => "Getter package Manifest is invalid", + #[cfg(feature = "lua")] + Self::ManifestArtifactMissing { .. } => "Getter artifact is missing from Manifest", + #[cfg(feature = "lua")] + Self::ManifestArtifactAmbiguous { .. } => "Getter artifact Manifest entry is ambiguous", + #[cfg(feature = "lua")] + Self::ArtifactSha256Invalid { .. } => "Getter artifact SHA-256 is invalid", + #[cfg(feature = "lua")] + Self::ArtifactPathCollision { .. } => "Getter artifact staging paths collide", + #[cfg(feature = "lua")] + Self::Sha256Mismatch { .. } => "Getter artifact SHA-256 did not match", + #[cfg(feature = "lua")] + Self::ArtifactIo(_) => "Getter artifact staging failed", + #[cfg(feature = "lua")] + Self::ArtifactTransport(_) => "Getter artifact download failed", } } @@ -68,6 +157,18 @@ impl AppOperationError { Self::Startup(error) => error.to_string(), #[cfg(feature = "lua")] Self::Runtime(error) => error.to_string(), + #[cfg(feature = "lua")] + Self::NoCandidates => self.to_string(), + #[cfg(feature = "lua")] + Self::ManifestMissing + | Self::ManifestInvalid(_) + | Self::ManifestArtifactMissing { .. } + | Self::ManifestArtifactAmbiguous { .. } + | Self::ArtifactSha256Invalid { .. } + | Self::ArtifactPathCollision { .. } + | Self::Sha256Mismatch { .. } + | Self::ArtifactIo(_) + | Self::ArtifactTransport(_) => self.to_string(), } } } @@ -79,6 +180,32 @@ pub struct AppCheckResult { pub action: Option, } +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AppDownloadResult { + pub package_id: PackageId, + pub repository_id: String, + pub version: String, + pub artifacts: Vec, +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StagedArtifact { + pub name: String, + pub path: PathBuf, + pub sha256: String, + pub status: String, +} + +#[cfg(feature = "lua")] +struct PreparedArtifact { + artifact: UpdateArtifact, + digest: String, + final_path: PathBuf, + temporary_path: PathBuf, +} + fn tracked_package( db: &MainDb, package_id: &PackageId, @@ -113,6 +240,252 @@ pub fn inspect_app( )) } +#[cfg(feature = "lua")] +pub fn download_app( + data_dir: &Path, + package_id: &PackageId, +) -> Result { + download_app_with_transports( + data_dir, + package_id, + Some(Rc::new( + crate::github_releases::UreqGithubReleaseTransport::new(), + )), + Rc::new(crate::download::UreqRuntimeDownloadTransport::new()), + ) +} + +#[cfg(feature = "lua")] +pub fn download_app_with_transports( + data_dir: &Path, + package_id: &PackageId, + github_release_transport: Option>, + download_transport: Rc, +) -> Result { + startup::bootstrap_data_dir(data_dir)?; + let db = MainDb::open(data_dir.join("main.db"))?; + let tracked = tracked_package(&db, package_id)?; + let evaluation = crate::runtime::evaluate_registered_package( + data_dir, + &db, + &crate::runtime::RegisteredPackageUpdateActionRequest { + package_id: package_id.clone(), + repository_id: tracked.repository_id, + installed_version: None, + pin_version: None, + }, + crate::provider_cache::ProviderCacheMode::ForceRefresh, + github_release_transport, + )?; + let candidate = evaluation + .package + .updates + .iter() + .max_by(|left, right| compare_versions(&left.version, &right.version)) + .cloned() + .ok_or(AppOperationError::NoCandidates)?; + + let manifest_path = evaluation.package_path.join(PACKAGE_MANIFEST_FILE); + let manifest_content = fs::read_to_string(&manifest_path).map_err(|error| { + if error.kind() == std::io::ErrorKind::NotFound { + AppOperationError::ManifestMissing + } else { + AppOperationError::ArtifactIo(error) + } + })?; + let manifest = + PackageManifest::parse(&manifest_content).map_err(AppOperationError::ManifestInvalid)?; + let downloads = data_dir.join("downloads"); + let prepared = candidate + .artifacts + .iter() + .map(|artifact| prepare_artifact(&downloads, &manifest, artifact)) + .collect::, _>>()?; + let mut staging_paths = std::collections::HashMap::new(); + for artifact in &prepared { + let file_name = artifact + .artifact + .file_name + .clone() + .unwrap_or_else(|| artifact.artifact.name.clone()); + for path in [&artifact.final_path, &artifact.temporary_path] { + if let Some(first) = staging_paths.insert(path.clone(), file_name.clone()) { + return Err(AppOperationError::ArtifactPathCollision { + first, + second: file_name, + }); + } + } + } + if prepared + .iter() + .any(|artifact| !artifact.final_path.exists()) + { + fs::create_dir_all(&downloads)?; + } + + let artifacts = prepared + .into_iter() + .map(|artifact| stage_artifact(artifact, download_transport.as_ref())) + .collect::, _>>()?; + Ok(AppDownloadResult { + package_id: package_id.clone(), + repository_id: evaluation.package.repository.to_string(), + version: candidate.version, + artifacts, + }) +} + +#[cfg(feature = "lua")] +fn prepare_artifact( + downloads: &Path, + manifest: &PackageManifest, + artifact: &UpdateArtifact, +) -> Result { + let file_name = artifact + .file_name + .as_deref() + .unwrap_or(artifact.name.as_str()); + let members = manifest.artifact_sha256_members(file_name); + let digest = if let Some(hint) = artifact.sha256.as_deref() { + if hint.len() != 64 || !hint.bytes().all(|byte| byte.is_ascii_hexdigit()) { + return Err(AppOperationError::ArtifactSha256Invalid { + artifact: artifact.name.clone(), + }); + } + let hint = hint.to_ascii_lowercase(); + if !members.iter().any(|member| member == &hint) { + return Err(AppOperationError::ManifestArtifactMissing { + artifact: artifact.name.clone(), + file_name: file_name.to_owned(), + }); + } + hint + } else { + match members { + [] => { + return Err(AppOperationError::ManifestArtifactMissing { + artifact: artifact.name.clone(), + file_name: file_name.to_owned(), + }); + } + [digest] => digest.clone(), + _ => { + return Err(AppOperationError::ManifestArtifactAmbiguous { + artifact: artifact.name.clone(), + file_name: file_name.to_owned(), + }); + } + } + }; + let name = sanitized_artifact_file_name(file_name); + let final_path = downloads.join(format!("{digest}-{name}")); + let temporary_path = PathBuf::from(format!( + "{}.download", + final_path.as_os_str().to_string_lossy() + )); + Ok(PreparedArtifact { + artifact: artifact.clone(), + final_path, + temporary_path, + digest, + }) +} + +#[cfg(feature = "lua")] +fn stage_artifact( + prepared: PreparedArtifact, + transport: &dyn crate::download::RuntimeDownloadTransport, +) -> Result { + if prepared.final_path.exists() { + return Ok(staged_artifact(&prepared, "reused")); + } + let mut sink = StagingSink::new(&prepared.temporary_path)?; + transport.fetch( + &crate::download::RuntimeDownloadTransportRequest { + url: &prepared.artifact.url, + }, + &mut sink, + )?; + let actual = sink.finish()?; + if actual != prepared.digest { + return Err(AppOperationError::Sha256Mismatch { + artifact: prepared.artifact.name, + expected: prepared.digest, + actual, + }); + } + fs::rename(&prepared.temporary_path, &prepared.final_path)?; + Ok(staged_artifact(&prepared, "downloaded")) +} + +#[cfg(feature = "lua")] +fn staged_artifact(prepared: &PreparedArtifact, status: &str) -> StagedArtifact { + StagedArtifact { + name: prepared.artifact.name.clone(), + path: prepared.final_path.clone(), + sha256: prepared.digest.clone(), + status: status.to_owned(), + } +} + +#[cfg(feature = "lua")] +struct StagingSink { + file: fs::File, + hasher: Sha256, +} + +#[cfg(feature = "lua")] +impl StagingSink { + fn new(path: &Path) -> Result { + Ok(Self { + file: fs::File::create(path)?, + hasher: Sha256::new(), + }) + } + + fn finish(mut self) -> Result { + self.file.flush()?; + self.file.sync_all()?; + Ok(format!("{:x}", self.hasher.finalize())) + } +} + +#[cfg(feature = "lua")] +impl crate::download::RuntimeDownloadSink for StagingSink { + fn set_total_bytes(&mut self, _total_bytes: Option) -> Result<(), String> { + Ok(()) + } + + fn write_chunk(&mut self, chunk: &[u8]) -> Result<(), String> { + self.file + .write_all(chunk) + .map_err(|error| error.to_string())?; + self.hasher.update(chunk); + Ok(()) + } +} + +#[cfg(feature = "lua")] +fn sanitized_artifact_file_name(raw: &str) -> String { + let sanitized = raw + .trim() + .chars() + .map(|character| { + if character.is_control() || matches!(character, '/' | '\\') { + '_' + } else { + character + } + }) + .collect::(); + if sanitized.is_empty() || sanitized == "." || sanitized == ".." { + "artifact.bin".to_owned() + } else { + sanitized + } +} + #[cfg(feature = "lua")] pub fn check_app( runtime: &mut GetterRuntime, diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs index 3e3ea1a..a6b3eae 100644 --- a/crates/getter-operations/src/fdroid_autogen.rs +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -22,6 +22,7 @@ use getter_providers::FdroidApp; use getter_storage::{CacheDb, MainDb}; use serde::Deserialize; use serde_json::{json, Value}; +use std::collections::HashSet; use std::path::{Path, PathBuf}; pub fn preview_fdroid_packages_json( @@ -301,7 +302,7 @@ fn fdroid_generated_files( "type": "android:app", "android": { "package_name": app.package_name }, }))?; - let manifest = fdroid_manifest(source_response_sha512); + let manifest = fdroid_manifest(source_response_sha512, app); let version_lua = fdroid_version_lua(app); Ok(vec![ generated_file("metadata.jsonc", metadata), @@ -310,11 +311,26 @@ fn fdroid_generated_files( ]) } -fn fdroid_manifest(source_response_sha512: &[String]) -> String { - source_response_sha512 - .iter() - .map(|digest| format!("{digest} fdroid-index.xml\n")) - .collect() +fn fdroid_manifest(source_response_sha512: &[String], app: &FdroidApp) -> String { + let mut memberships = HashSet::new(); + let mut manifest = String::new(); + for digest in source_response_sha512 { + let membership = (digest.clone(), "fdroid-index.xml".to_owned()); + if memberships.insert(membership.clone()) { + manifest.push_str(&format!("{} {}\n", membership.0, membership.1)); + } + } + for release in &app.packages { + if let Some(digest) = release.sha256.as_deref().filter(|digest| { + digest.len() == 64 && digest.bytes().all(|byte| byte.is_ascii_hexdigit()) + }) { + let membership = (digest.to_ascii_lowercase(), release.apk_name.clone()); + if memberships.insert(membership.clone()) { + manifest.push_str(&format!("{} {}\n", membership.0, membership.1)); + } + } + } + manifest } fn fdroid_version_lua(app: &FdroidApp) -> String { @@ -460,8 +476,10 @@ mod tests { assert_eq!( manifest, format!( - "{} fdroid-index.xml\n", - source_response_sha512(FDROID_FIXTURE) + "{} fdroid-index.xml\n{} org.fdroid.fdroid_1020000.apk\n{} org.fdroid.fdroid_1019000.apk\n", + source_response_sha512(FDROID_FIXTURE), + "a".repeat(64), + "b".repeat(64), ) ); assert_eq!( @@ -475,6 +493,43 @@ mod tests { assert!(!temp.path().join("repo/autogen").exists()); } + #[test] + fn manifest_deduplicates_reused_artifact_membership() { + let digest = "A".repeat(64); + let app = FdroidApp { + package_name: "org.example.app".to_owned(), + name: None, + summary: None, + packages: vec![ + getter_providers::FdroidRelease { + version: "2".to_owned(), + version_code: Some(2), + apk_name: "app.apk".to_owned(), + sha256: Some(digest.clone()), + size: None, + }, + getter_providers::FdroidRelease { + version: "1".to_owned(), + version_code: Some(1), + apk_name: "app.apk".to_owned(), + sha256: Some(digest.clone()), + size: None, + }, + ], + }; + + let manifest = fdroid_manifest(&["b".repeat(128)], &app); + + getter_core::manifest::PackageManifest::parse(&manifest).unwrap(); + assert_eq!( + manifest + .lines() + .filter(|line| *line == format!("{} app.apk", digest.to_ascii_lowercase())) + .count(), + 1 + ); + } + #[test] fn provider_module_generation_rejects_non_default_endpoint() { let app = FdroidApp { diff --git a/crates/getter-operations/src/github_autogen.rs b/crates/getter-operations/src/github_autogen.rs index bd20d0f..554186e 100644 --- a/crates/getter-operations/src/github_autogen.rs +++ b/crates/getter-operations/src/github_autogen.rs @@ -25,6 +25,7 @@ use getter_providers::{ use getter_storage::{CacheDb, MainDb}; use serde::Deserialize; use serde_json::{json, Value}; +use std::collections::HashSet; use std::path::{Path, PathBuf}; pub fn preview_github_android_package_json( @@ -155,6 +156,7 @@ where } else { candidates.push(github_candidate_json( &result.source_response_sha512, + &update_candidates, &package_id, GithubGeneratedPackageInput { owner: &owner, @@ -265,11 +267,12 @@ struct GithubGeneratedPackageInput<'a> { fn github_candidate_json( source_response_sha512: &[String], + update_candidates: &[getter_core::UpdateCandidate], package_id: &PackageId, input: GithubGeneratedPackageInput<'_>, ) -> AutogenOperationResult { let relative_path = package_relative_path(package_id); - let files = github_generated_files(source_response_sha512, &input)?; + let files = github_generated_files(source_response_sha512, update_candidates, &input)?; let record = AutogenRecord { version: AUTOGEN_RECORD_VERSION, generator: GITHUB_AUTOGEN_GENERATOR.to_owned(), @@ -325,6 +328,7 @@ fn github_candidate_json( fn github_generated_files( source_response_sha512: &[String], + update_candidates: &[getter_core::UpdateCandidate], input: &GithubGeneratedPackageInput<'_>, ) -> AutogenOperationResult> { if source_response_sha512.is_empty() { @@ -338,7 +342,7 @@ fn github_generated_files( "display_name": input.display_name, "android": { "package_name": input.android_package }, }))?; - let manifest = github_manifest(source_response_sha512); + let manifest = github_manifest(source_response_sha512, update_candidates); let version_lua = github_version_lua(input); Ok(vec![ generated_file("metadata.jsonc", metadata), @@ -347,11 +351,31 @@ fn github_generated_files( ]) } -fn github_manifest(source_response_sha512: &[String]) -> String { - source_response_sha512 - .iter() - .map(|digest| format!("{digest} github-releases.json\n")) - .collect() +fn github_manifest( + source_response_sha512: &[String], + update_candidates: &[getter_core::UpdateCandidate], +) -> String { + let mut memberships = HashSet::new(); + let mut manifest = String::new(); + for digest in source_response_sha512 { + let membership = (digest.clone(), "github-releases.json".to_owned()); + if memberships.insert(membership.clone()) { + manifest.push_str(&format!("{} {}\n", membership.0, membership.1)); + } + } + for candidate in update_candidates { + for artifact in &candidate.artifacts { + if let (Some(file_name), Some(digest)) = (&artifact.file_name, &artifact.sha256) { + if digest.len() == 64 && digest.bytes().all(|byte| byte.is_ascii_hexdigit()) { + let membership = (digest.to_ascii_lowercase(), file_name.clone()); + if memberships.insert(membership.clone()) { + manifest.push_str(&format!("{} {}\n", membership.0, membership.1)); + } + } + } + } + } + manifest } fn github_version_lua(input: &GithubGeneratedPackageInput<'_>) -> String { @@ -563,6 +587,95 @@ mod tests { assert!(!temp.path().join("repo/autogen").exists()); } + #[test] + fn manifest_covers_valid_digests_for_every_filtered_candidate() { + let first = getter_core::UpdateCandidate { + version: "1".to_owned(), + version_code: None, + channel: Some("stable".to_owned()), + changelog: None, + source: Some("github".to_owned()), + artifacts: vec![getter_core::UpdateArtifact { + name: "first.apk".to_owned(), + url: "https://example.invalid/first.apk".to_owned(), + content_type: None, + file_name: Some("first.apk".to_owned()), + sha256: Some("A".repeat(64)), + size: None, + }], + }; + let second = getter_core::UpdateCandidate { + version: "2".to_owned(), + version_code: None, + channel: Some("stable".to_owned()), + changelog: None, + source: Some("github".to_owned()), + artifacts: vec![ + getter_core::UpdateArtifact { + name: "second.apk".to_owned(), + url: "https://example.invalid/second.apk".to_owned(), + content_type: None, + file_name: Some("second.apk".to_owned()), + sha256: Some("B".repeat(64)), + size: None, + }, + getter_core::UpdateArtifact { + name: "invalid.apk".to_owned(), + url: "https://example.invalid/invalid.apk".to_owned(), + content_type: None, + file_name: Some("invalid.apk".to_owned()), + sha256: Some("not-a-digest".to_owned()), + size: None, + }, + ], + }; + + let repeated = getter_core::UpdateCandidate { + version: "3".to_owned(), + version_code: None, + channel: Some("stable".to_owned()), + changelog: None, + source: Some("github".to_owned()), + artifacts: vec![getter_core::UpdateArtifact { + name: "first.apk".to_owned(), + url: "https://example.invalid/new-first.apk".to_owned(), + content_type: None, + file_name: Some("first.apk".to_owned()), + sha256: Some("A".repeat(64)), + size: None, + }], + }; + let distinct = getter_core::UpdateCandidate { + version: "4".to_owned(), + version_code: None, + channel: Some("stable".to_owned()), + changelog: None, + source: Some("github".to_owned()), + artifacts: vec![getter_core::UpdateArtifact { + name: "first.apk".to_owned(), + url: "https://example.invalid/distinct-first.apk".to_owned(), + content_type: None, + file_name: Some("first.apk".to_owned()), + sha256: Some("D".repeat(64)), + size: None, + }], + }; + + let manifest = github_manifest(&["c".repeat(128)], &[first, second, repeated, distinct]); + + getter_core::manifest::PackageManifest::parse(&manifest).unwrap(); + assert_eq!( + manifest + .lines() + .filter(|line| *line == format!("{} first.apk", "a".repeat(64))) + .count(), + 1 + ); + assert!(manifest.contains(&format!("{} first.apk\n", "d".repeat(64)))); + assert!(manifest.contains(&format!("{} second.apk\n", "b".repeat(64)))); + assert!(!manifest.contains("invalid.apk")); + } + #[test] fn preview_without_fixture_refreshes_github_releases_through_getter_transport() { let temp = tempfile::tempdir().unwrap(); @@ -688,6 +801,7 @@ mod tests { exclude: None, }; let error = github_generated_files( + &[], &[], &GithubGeneratedPackageInput { owner: "DUpdateSystem", diff --git a/crates/getter-operations/src/lua_http_policy.rs b/crates/getter-operations/src/lua_http_policy.rs index da254ce..b3d11a4 100644 --- a/crates/getter-operations/src/lua_http_policy.rs +++ b/crates/getter-operations/src/lua_http_policy.rs @@ -18,7 +18,7 @@ use getter_core::repository::{ use getter_core::{RepositoryId, ResolvedPackage}; use sha2::{Digest, Sha512}; use std::cell::RefCell; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; use std::fs; use std::path::{Path, PathBuf}; use std::rc::Rc; @@ -148,20 +148,14 @@ pub fn evaluate_package_directory_script_with_fixture_http_and_runtime_hooks( } #[derive(Debug, Clone)] -struct PackageManifest { - sha512: BTreeSet, -} +struct PackageManifest(getter_core::manifest::PackageManifest); impl PackageManifest { fn load(path: impl AsRef) -> Result { let path = path.as_ref(); let source = match fs::read_to_string(path) { Ok(source) => source, - Err(source) if source.kind() == std::io::ErrorKind::NotFound => { - return Ok(Self { - sha512: BTreeSet::new(), - }) - } + Err(source) if source.kind() == std::io::ErrorKind::NotFound => String::new(), Err(source) => { return Err(LuaHttpPolicyError::ReadManifest { path: path.to_path_buf(), @@ -169,39 +163,19 @@ impl PackageManifest { }) } }; - let mut sha512 = BTreeSet::new(); - for (line_index, raw_line) in source.lines().enumerate() { - let line = raw_line.trim(); - if line.is_empty() { - continue; - } - let hash = line - .split_whitespace() - .next() - .expect("non-empty line has token"); - if !is_sha512_hex(hash) { - return Err(LuaHttpPolicyError::InvalidManifest { - path: path.to_path_buf(), - reason: format!( - "line {} must start with a 128-character SHA-512 hex digest", - line_index + 1 - ), - }); - } - sha512.insert(hash.to_ascii_lowercase()); - } - Ok(Self { sha512 }) + getter_core::manifest::PackageManifest::parse(&source) + .map(Self) + .map_err(|source| LuaHttpPolicyError::InvalidManifest { + path: path.to_path_buf(), + reason: source.to_string(), + }) } fn contains(&self, digest: &str) -> bool { - self.sha512.contains(digest) + self.0.contains_sha512(digest) } } -fn is_sha512_hex(value: &str) -> bool { - value.len() == 128 && value.bytes().all(|byte| byte.is_ascii_hexdigit()) -} - fn sha512_hex(body: &[u8]) -> String { let mut hasher = Sha512::new(); hasher.update(body); diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 4e0f8da..e68b349 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -45,7 +45,6 @@ use serde::Deserialize; use serde_json::{json, Map, Value}; use sha2::{Digest, Sha512}; use std::cell::RefCell; -use std::collections::BTreeSet; use std::path::{Path, PathBuf}; use std::rc::Rc; @@ -1021,20 +1020,14 @@ fn lua_table_to_json_value(table: Table) -> mlua::Result { } #[derive(Debug, Clone)] -struct PackageManifest { - sha512: BTreeSet, -} +struct PackageManifest(getter_core::manifest::PackageManifest); impl PackageManifest { fn load(path: impl AsRef) -> Result { let path = path.as_ref(); let source = match std::fs::read_to_string(path) { Ok(source) => source, - Err(source) if source.kind() == std::io::ErrorKind::NotFound => { - return Ok(Self { - sha512: BTreeSet::new(), - }) - } + Err(source) if source.kind() == std::io::ErrorKind::NotFound => String::new(), Err(source) => { return Err(LuaProviderHostOperationError::ReadManifest { path: path.to_path_buf(), @@ -1042,28 +1035,12 @@ impl PackageManifest { }) } }; - let mut sha512 = BTreeSet::new(); - for (line_index, raw_line) in source.lines().enumerate() { - let line = raw_line.trim(); - if line.is_empty() { - continue; - } - let hash = line - .split_whitespace() - .next() - .expect("non-empty line has token"); - if !is_sha512_hex(hash) { - return Err(LuaProviderHostOperationError::InvalidManifest { - path: path.to_path_buf(), - reason: format!( - "line {} must start with a 128-character SHA-512 hex digest", - line_index + 1 - ), - }); - } - sha512.insert(hash.to_ascii_lowercase()); - } - Ok(Self { sha512 }) + getter_core::manifest::PackageManifest::parse(&source) + .map(Self) + .map_err(|source| LuaProviderHostOperationError::InvalidManifest { + path: path.to_path_buf(), + reason: source.to_string(), + }) } fn allows_body(&self, body: &str) -> bool { @@ -1071,14 +1048,10 @@ impl PackageManifest { } fn allows_sha512(&self, digest: &str) -> bool { - is_sha512_hex(digest) && self.sha512.contains(&digest.to_ascii_lowercase()) + self.0.contains_sha512(digest) } } -fn is_sha512_hex(value: &str) -> bool { - value.len() == 128 && value.bytes().all(|byte| byte.is_ascii_hexdigit()) -} - fn sha512_hex(body: &[u8]) -> String { let mut hasher = Sha512::new(); hasher.update(body); diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 9c2d6b4..b82cc76 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -536,6 +536,7 @@ struct CleanTasksRequest { #[cfg(feature = "lua")] pub(crate) struct RegisteredPackageEvaluation { pub package: getter_core::ResolvedPackage, + pub package_path: PathBuf, pub diagnostics: Vec, dependency_digest: String, provider_calls: Vec, @@ -604,6 +605,7 @@ pub(crate) fn evaluate_registered_package( .collect(); return Ok(RegisteredPackageEvaluation { package: provider_eval.package, + package_path: package_directory.path.clone(), diagnostics, dependency_digest, provider_calls: provider_eval.provider_calls, diff --git a/crates/getter-operations/tests/artifact_staging.rs b/crates/getter-operations/tests/artifact_staging.rs new file mode 100644 index 0000000..f2fc6fe --- /dev/null +++ b/crates/getter-operations/tests/artifact_staging.rs @@ -0,0 +1,381 @@ +#![cfg(feature = "lua")] + +use getter_core::repository::RepositoryMetadata; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_operations::app::download_app_with_transports; +use getter_operations::download::{ + RuntimeDownloadSink, RuntimeDownloadTransport, RuntimeDownloadTransportError, + RuntimeDownloadTransportRequest, +}; +use getter_operations::github_releases::{ + GithubReleaseTransport, GithubReleaseTransportError, GithubReleaseTransportRequest, + GithubReleaseTransportResponse, +}; +use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; +use sha2::{Digest, Sha256}; +use std::cell::RefCell; +use std::collections::HashMap; +use std::fs; +use std::rc::Rc; + +struct BytesTransport { + bodies: HashMap>, + requests: RefCell>, +} + +impl RuntimeDownloadTransport for BytesTransport { + fn fetch( + &self, + request: &RuntimeDownloadTransportRequest, + sink: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError> { + self.requests.borrow_mut().push(request.url.to_owned()); + let body = self.bodies.get(request.url).unwrap(); + sink.set_total_bytes(Some(body.len() as u64)) + .map_err(RuntimeDownloadTransportError::Sink)?; + sink.write_chunk(body) + .map_err(RuntimeDownloadTransportError::Sink) + } +} + +struct GithubMock { + body: Vec, + requests: RefCell, +} + +impl GithubReleaseTransport for GithubMock { + fn fetch_releases( + &self, + _request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + *self.requests.borrow_mut() += 1; + Ok(GithubReleaseTransportResponse { + body: String::from_utf8(self.body.clone()).unwrap(), + etag: None, + last_modified: None, + }) + } +} + +fn digest(bytes: &[u8]) -> String { + format!("{:x}", Sha256::digest(bytes)) +} + +fn fixture(script: &str, manifest: &str, pin: Option<&str>) -> tempfile::TempDir { + let temp = tempfile::tempdir().unwrap(); + let package = temp.path().join("repo/official/android/com.example"); + fs::create_dir_all(&package).unwrap(); + fs::write(package.join("Manifest"), manifest).unwrap(); + fs::write( + package.join("metadata.jsonc"), + r#"{"type":"android:app","display_name":"Example","android":{"package_name":"com.example"}}"#, + ) + .unwrap(); + fs::write(package.join("1.lua"), script).unwrap(); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".into(), + priority: RepositoryPriority::new(0), + api_version: "1".into(), + }, + Some(&temp.path().join("repo/official")), + None, + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/com.example".parse().unwrap(), + enabled: true, + favorite: false, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + pin_version: pin.map(str::to_owned), + }) + .unwrap(); + temp +} + +#[test] +fn stages_multiple_artifacts_from_manifest_in_declaration_order_and_ignores_baselines() { + let first = b"apk bytes"; + let second = b"symbols bytes"; + let script = r#"#!/bin/upa-lua v1 +return package_version { updates = { + { version="2", artifacts={{name="old",url="mock://old",file_name="old.apk"}} }, + { version="10", artifacts={ + {name="apk",url="mock://apk",file_name="release/path/Example APK.apk"}, + {name="symbols",url="mock://symbols",file_name="symbols.zip"} + } } +} }"#; + let manifest = format!( + "{} release/path/Example APK.apk\n{} symbols.zip\n", + digest(first), + digest(second) + ); + let temp = fixture(script, &manifest, Some("999")); + let transport = Rc::new(BytesTransport { + bodies: HashMap::from([ + ("mock://apk".into(), first.to_vec()), + ("mock://symbols".into(), second.to_vec()), + ]), + requests: RefCell::new(Vec::new()), + }); + + let result = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport.clone(), + ) + .unwrap(); + + assert_eq!(result.version, "10"); + assert_eq!( + result + .artifacts + .iter() + .map(|a| a.name.as_str()) + .collect::>(), + ["apk", "symbols"] + ); + assert!(result.artifacts[0] + .path + .ends_with(format!("{}-release_path_Example APK.apk", digest(first)))); + assert_eq!( + &*transport.requests.borrow(), + &["mock://apk", "mock://symbols"] + ); + assert_eq!(fs::read(&result.artifacts[0].path).unwrap(), first); +} + +#[test] +fn invalid_manifest_preflight_prevents_every_artifact_request() { + for manifest in [ + "", + "not-a-digest app.apk\n", + &format!("{} app.apk\n{} app.apk\n", "a".repeat(64), "a".repeat(64)), + ] { + let script = r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", artifacts={{name="app",url="mock://app",file_name="app.apk"}} }} }"#; + let temp = fixture(script, manifest, None); + let transport = Rc::new(BytesTransport { + bodies: HashMap::new(), + requests: RefCell::new(Vec::new()), + }); + + let error = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport.clone(), + ) + .unwrap_err(); + assert!( + error.code().starts_with("artifact.manifest_") + || error.to_string().contains("package Manifest is invalid") + || format!("{error:?}").contains("InvalidManifest"), + "unexpected error: {error:?}" + ); + assert!(transport.requests.borrow().is_empty()); + assert!(!temp.path().join("downloads").exists()); + } +} + +#[test] +fn manifest_digest_hint_selects_one_of_multiple_filename_members() { + let bytes = b"selected release bytes"; + let selected = digest(bytes); + let script = format!( + r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{ {{ version="2", artifacts={{{{name="app",url="mock://app",file_name="app.apk",sha256="{selected}"}}}} }} }} }}"# + ); + let manifest = format!("{} app.apk\n{selected} app.apk\n", "a".repeat(64)); + let temp = fixture(&script, &manifest, None); + let transport = Rc::new(BytesTransport { + bodies: HashMap::from([("mock://app".into(), bytes.to_vec())]), + requests: RefCell::new(Vec::new()), + }); + + let result = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport, + ) + .unwrap(); + + assert_eq!(result.artifacts[0].sha256, selected); +} + +#[test] +fn multiple_filename_members_without_digest_hint_are_ambiguous() { + let script = r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", artifacts={{name="app",url="mock://app",file_name="app.apk"}} }} }"#; + let manifest = format!("{} app.apk\n{} app.apk\n", "a".repeat(64), "b".repeat(64)); + let temp = fixture(script, &manifest, None); + let transport = Rc::new(BytesTransport { + bodies: HashMap::new(), + requests: RefCell::new(Vec::new()), + }); + + let error = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport.clone(), + ) + .unwrap_err(); + + assert_eq!(error.code(), "artifact.manifest_ambiguous"); + assert!(transport.requests.borrow().is_empty()); +} + +#[test] +fn final_and_temporary_namespace_cross_collision_fails_preflight() { + let first_digest = digest(b"same bytes"); + let second_digest = first_digest.clone(); + let second_name = "app.apk.download"; + let script = format!( + r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{ {{ version="2", artifacts={{ + {{name="first",url="mock://first",file_name="app.apk"}}, + {{name="second",url="mock://second",file_name="{second_name}"}} +}} }} }} }}"# + ); + let manifest = format!("{first_digest} app.apk\n{second_digest} {second_name}\n"); + let temp = fixture(&script, &manifest, None); + let transport = Rc::new(BytesTransport { + bodies: HashMap::new(), + requests: RefCell::new(Vec::new()), + }); + + let error = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport.clone(), + ) + .unwrap_err(); + + assert_eq!(error.code(), "artifact.path_collision"); + assert!(transport.requests.borrow().is_empty()); +} + +#[test] +fn reuses_final_without_transport_and_mismatch_leaves_only_temporary_file() { + let expected = digest(b"good"); + let script = r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", artifacts={{name="app",url="mock://app",file_name="dir/app.apk"}} }} }"#; + let manifest = format!("{expected} dir/app.apk\n"); + let temp = fixture(script, &manifest, None); + let final_path = temp + .path() + .join(format!("downloads/{expected}-dir_app.apk")); + fs::create_dir_all(final_path.parent().unwrap()).unwrap(); + fs::write(&final_path, b"owned marker").unwrap(); + let no_call = Rc::new(BytesTransport { + bodies: HashMap::new(), + requests: RefCell::new(Vec::new()), + }); + let reused = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + no_call.clone(), + ) + .unwrap(); + assert_eq!(reused.artifacts[0].status, "reused"); + assert!(no_call.requests.borrow().is_empty()); + + fs::remove_file(&final_path).unwrap(); + let temporary_path = std::path::PathBuf::from(format!("{}.download", final_path.display())); + fs::write(&temporary_path, b"old partial").unwrap(); + let mismatch = Rc::new(BytesTransport { + bodies: HashMap::from([("mock://app".into(), b"bad".to_vec())]), + requests: RefCell::new(Vec::new()), + }); + let error = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + mismatch, + ) + .unwrap_err(); + assert_eq!(error.code(), "artifact.sha256_mismatch"); + assert!(!final_path.exists()); + assert_eq!(fs::read(temporary_path).unwrap(), b"bad"); +} + +#[test] +fn distinct_artifact_filenames_that_flatten_to_one_staging_path_fail_preflight() { + let bytes = b"same"; + let sha256 = digest(bytes); + let temp = fixture( + r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", artifacts={ + {name="slash",url="https://example.invalid/slash",file_name="dir/app.apk"}, + {name="flat",url="https://example.invalid/flat",file_name="dir_app.apk"}, + } }} }"#, + &format!("{sha256} dir/app.apk\n{sha256} dir_app.apk\n"), + None, + ); + let transport = Rc::new(BytesTransport { + bodies: HashMap::from([ + ("https://example.invalid/slash".to_owned(), bytes.to_vec()), + ("https://example.invalid/flat".to_owned(), bytes.to_vec()), + ]), + requests: RefCell::new(Vec::new()), + }); + + let error = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport.clone(), + ) + .unwrap_err(); + + assert_eq!(error.code(), "artifact.path_collision", "{error:?}"); + assert!(transport.requests.borrow().is_empty()); + assert!(!temp.path().join("downloads").exists()); +} + +#[test] +fn cold_github_refresh_uses_provider_digest_via_manifest() { + let bytes = b"github apk"; + let sha = digest(bytes); + let script = r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name="Example", android_package="com.example", owner="example", repo="app" +}"#; + let releases = format!( + r#"[{{"tag_name":"v2","name":"v2","body":"","draft":false,"prerelease":false,"assets":[{{"id":1,"name":"app.apk","content_type":"application/vnd.android.package-archive","size":10,"browser_download_url":"mock://github-apk","digest":"sha256:{sha}"}}]}}]"# + ); + let source_sha512 = format!("{:x}", sha2::Sha512::digest(releases.as_bytes())); + let temp = fixture( + script, + &format!("{source_sha512} github-releases.json\n{sha} app.apk\n"), + None, + ); + let github = Rc::new(GithubMock { + body: releases.into_bytes(), + requests: RefCell::new(0), + }); + let transport = Rc::new(BytesTransport { + bodies: HashMap::from([("mock://github-apk".into(), bytes.to_vec())]), + requests: RefCell::new(Vec::new()), + }); + + let result = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + Some(github.clone()), + transport, + ) + .unwrap(); + assert_eq!(result.version, "v2"); + assert_eq!(*github.requests.borrow(), 1); + assert_eq!(fs::read(&result.artifacts[0].path).unwrap(), bytes); +} From 2fc69e535e4535a42f6c1de5169da1a732f364f1 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 16 Jul 2026 11:43:53 +0800 Subject: [PATCH 69/72] feat(install): run package-declared installers --- Cargo.lock | 12 +- crates/getter-cli/src/lib.rs | 84 +++- crates/getter-cli/src/main.rs | 2 +- crates/getter-cli/tests/app_cli.rs | 147 +++++++ crates/getter-cli/tests/bdd_cli.rs | 73 ++++ .../features/cli/app_inspect_check.feature | 10 + crates/getter-core/src/lib.rs | 52 +++ crates/getter-core/src/lua.rs | 275 +++++++++++- .../src/luaclass/fdroid_android.lua | 5 + .../src/luaclass/github_android_apk.lua | 5 + crates/getter-core/src/luaclass/installer.lua | 11 + crates/getter-core/src/update.rs | 2 + crates/getter-operations/Cargo.toml | 1 + crates/getter-operations/src/app.rs | 392 ++++++++++++++++- .../getter-operations/src/github_autogen.rs | 4 + crates/getter-operations/src/runtime.rs | 1 + crates/getter-operations/tests/installer.rs | 408 ++++++++++++++++++ crates/getter-providers/src/lib.rs | 3 + 18 files changed, 1466 insertions(+), 21 deletions(-) create mode 100644 crates/getter-core/src/luaclass/installer.lua create mode 100644 crates/getter-operations/tests/installer.rs diff --git a/Cargo.lock b/Cargo.lock index b0add8a..fe86385 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -657,6 +657,7 @@ dependencies = [ "tempfile", "thiserror 1.0.69", "ureq", + "which 8.0.3", ] [[package]] @@ -961,7 +962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3a8e7962a5368d5f264d045a5a255e90f9aa3fc1941ae15a8d2940d42cac671" dependencies = [ "cc", - "which", + "which 7.0.3", ] [[package]] @@ -1917,6 +1918,15 @@ dependencies = [ "winsafe", ] +[[package]] +name = "which" +version = "8.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c789537cf2f7f55be8e6192f92e464174ee55f91af622777f7f1ceb0dbccd03e" +dependencies = [ + "libc", +] + [[package]] name = "winapi-util" version = "0.1.11" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 470a360..8d949c7 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -39,6 +39,7 @@ use getter_storage::{ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::fs; +use std::io::Write; use std::path::{Path, PathBuf}; const MAIN_DB_FILE: &str = "main.db"; @@ -69,6 +70,9 @@ pub enum CliCommand { AppDownload { package_id: PackageId, }, + AppInstall { + package_id: PackageId, + }, HubList, RepoList, RepoAdd { @@ -414,7 +418,20 @@ where I: IntoIterator, S: Into, { - match run_inner(args) { + let mut captured = Vec::new(); + let mut output = run_with_command_sink(args, &mut captured); + if !captured.is_empty() { + output.stderr = String::from_utf8_lossy(&captured).into_owned(); + } + output +} + +pub fn run_with_command_sink(args: I, sink: &mut dyn Write) -> CliOutput +where + I: IntoIterator, + S: Into, +{ + match run_inner(args, sink) { Ok((command_name, data)) => CliOutput { exit_code: ExitCode::Success, stdout: success_envelope(&command_name, data), @@ -435,18 +452,47 @@ where } } -fn run_inner(args: I) -> Result<(String, Value), (String, CliError)> +fn run_inner(args: I, sink: &mut dyn Write) -> Result<(String, Value), (String, CliError)> where I: IntoIterator, S: Into, { let invocation = parse_args(args).map_err(|error| ("unknown".to_owned(), error))?; let command_name = invocation.command.name().to_owned(); - execute(invocation) + execute(invocation, sink) .map(|data| (command_name.clone(), data)) .map_err(|error| (command_name, error)) } +fn format_resolved_command(command: &Value) -> String { + fn quote(value: &str) -> String { + if !value.is_empty() + && value + .bytes() + .all(|byte| byte.is_ascii_alphanumeric() || b"/._-".contains(&byte)) + { + value.to_owned() + } else { + format!("'{}'", value.replace('\'', "'\\''")) + } + } + let executable = command + .get("executable") + .and_then(Value::as_str) + .unwrap_or(""); + let mut words = vec![quote(executable)]; + words.extend( + command + .get("args") + .and_then(Value::as_array) + .into_iter() + .flatten() + .filter_map(Value::as_str) + .map(quote), + ); + format!("installer command: {}\n", words.join(" ")) +} + pub fn parse_args(args: I) -> Result where I: IntoIterator, @@ -481,7 +527,10 @@ where [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, [domain, command, package_id] if domain == "app" - && (command == "show" || command == "check" || command == "download") => + && (command == "show" + || command == "check" + || command == "download" + || command == "install") => { let package_id = parse_package_id(package_id)?; match command.as_str() { @@ -493,7 +542,8 @@ where package_id, inventory: None, }, - _ => CliCommand::AppDownload { package_id }, + "download" => CliCommand::AppDownload { package_id }, + _ => CliCommand::AppInstall { package_id }, } } [domain, command, package_id, flag, inventory] @@ -790,7 +840,7 @@ fn map_app_error(error: getter_operations::app::AppOperationError) -> CliError { } } -fn execute(invocation: CliInvocation) -> Result { +fn execute(invocation: CliInvocation, command_sink: &mut dyn Write) -> Result { match invocation.command { CliCommand::Startup { inventory } => { let inventory = match inventory { @@ -838,6 +888,27 @@ fn execute(invocation: CliInvocation) -> Result { .map_err(map_app_error)?; serde_json::to_value(result).map_err(|error| CliError::Storage(error.to_string())) } + CliCommand::AppInstall { package_id } => { + struct StreamingObserver<'a>(std::cell::RefCell<&'a mut dyn Write>); + impl getter_operations::app::CommandObserver for StreamingObserver<'_> { + fn before_execute( + &self, + command: &getter_operations::app::ResolvedInstallerCommand, + ) -> std::io::Result<()> { + let value = serde_json::to_value(command).expect("command serializes"); + let mut sink = self.0.borrow_mut(); + sink.write_all(format_resolved_command(&value).as_bytes())?; + sink.flush() + } + } + let result = getter_operations::app::install_app_with_observer( + &invocation.data_dir, + &package_id, + &StreamingObserver(std::cell::RefCell::new(command_sink)), + ) + .map_err(map_app_error)?; + serde_json::to_value(result).map_err(|error| CliError::Storage(error.to_string())) + } CliCommand::AppCheck { package_id, inventory, @@ -2411,6 +2482,7 @@ impl CliCommand { Self::AppShow { .. } => "app show", Self::AppCheck { .. } => "app check", Self::AppDownload { .. } => "app download", + Self::AppInstall { .. } => "app install", Self::HubList => "hub list", Self::RepoList => "repo list", Self::RepoAdd { .. } => "repo add", diff --git a/crates/getter-cli/src/main.rs b/crates/getter-cli/src/main.rs index dc49483..53b4fbe 100644 --- a/crates/getter-cli/src/main.rs +++ b/crates/getter-cli/src/main.rs @@ -1,5 +1,5 @@ fn main() { - let output = getter_cli::run(std::env::args()); + let output = getter_cli::run_with_command_sink(std::env::args(), &mut std::io::stderr()); if !output.stdout.is_empty() { print!("{}", output.stdout); } diff --git a/crates/getter-cli/tests/app_cli.rs b/crates/getter-cli/tests/app_cli.rs index aa354d1..9991497 100644 --- a/crates/getter-cli/tests/app_cli.rs +++ b/crates/getter-cli/tests/app_cli.rs @@ -6,6 +6,34 @@ use serde_json::{json, Value}; use std::fs; use std::io::{Read, Write}; use std::net::TcpListener; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn fake_installer(temp: &Path) -> PathBuf { + let source = temp.join("fake_installer.rs"); + let executable = temp.join(format!("fake installer{}", std::env::consts::EXE_SUFFIX)); + fs::write( + &source, + r#"fn main() { + let arguments: Vec<_> = std::env::args().skip(1).collect(); + for argument in &arguments { + println!("{argument}"); + } + if arguments.iter().any(|argument| argument == "--fail") { + std::process::exit(23); + } +}"#, + ) + .unwrap(); + let status = Command::new("rustc") + .arg(&source) + .arg("-o") + .arg(&executable) + .status() + .expect("rustc must be available while running Rust tests"); + assert!(status.success(), "compile platform-native fake installer"); + executable +} fn fixture(latest: &str) -> tempfile::TempDir { let temp = tempfile::tempdir().unwrap(); @@ -104,6 +132,125 @@ return package_version {{ updates = {{ {{ version="2", artifacts={{{{name="app.a ); } +#[test] +fn app_download_ignores_missing_installer() { + let temp = fixture("2"); + let package = temp.path().join("repo/official/android/com.example"); + fs::write(package.join("Manifest"), "").unwrap(); + fs::write( + package.join("1.lua"), + r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", install =nil }} }"#, + ) + .unwrap(); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "download", + "android/com.example", + ]); + assert_eq!(output.exit_code, ExitCode::Success); +} + +#[test] +fn app_install_rejects_product_command_controls() { + let temp = fixture("2"); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "install", + "android/com.example", + "--executable", + "pm", + ]); + assert_eq!(output.exit_code, ExitCode::Usage); +} + +#[test] +fn app_install_executes_declared_command_and_reports_resolved_argv() { + let bytes = b"install artifact"; + let digest = "462c010ad28c00ae578bcfa52d985c8cb00a89accdd51ac9f05fec5872a7ae25"; + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let server = std::thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut request = [0; 1024]; + let _ = stream.read(&mut request).unwrap(); + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + bytes.len() + ) + .unwrap(); + stream.write_all(bytes).unwrap(); + }); + let temp = fixture("2"); + let executable = fake_installer(temp.path()); + let package = temp.path().join("repo/official/android/com.example"); + fs::write(package.join("Manifest"), format!("{digest} app.apk\n")).unwrap(); + fs::write(package.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates={{{{version="2",artifacts={{{{name="app",file_name="app.apk",url="http://{address}/app.apk"}}}},install ={{executable={:?},args={{"install",{{artifact="app"}},"$HOME;literal"}}}}}}}} }}"#, executable.to_string_lossy())).unwrap(); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "install", + "android/com.example", + ]); + server.join().unwrap(); + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["data"]["command"]["args"][0], "install"); + assert_eq!(json["data"]["command"]["args"][2], "$HOME;literal"); + assert!(output.stderr.contains("installer command:")); + assert!(output.stderr.contains("'")); +} + +#[test] +fn app_install_captures_streamed_command_when_child_fails() { + let bytes = b"install artifact"; + let digest = "462c010ad28c00ae578bcfa52d985c8cb00a89accdd51ac9f05fec5872a7ae25"; + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let server = std::thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut request = [0; 1024]; + let _ = stream.read(&mut request).unwrap(); + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + bytes.len() + ) + .unwrap(); + stream.write_all(bytes).unwrap(); + }); + let temp = fixture("2"); + let executable = fake_installer(temp.path()); + let package = temp.path().join("repo/official/android/com.example"); + fs::write(package.join("Manifest"), format!("{digest} app.apk\n")).unwrap(); + fs::write(package.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates={{{{version="2",artifacts={{{{name="app",file_name="app.apk",url="http://{address}/app.apk"}}}},install ={{executable={:?},args={{"--fail",{{artifact="app"}}}}}}}}}} }}"#, executable.to_string_lossy())).unwrap(); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "install", + "android/com.example", + ]); + server.join().unwrap(); + assert_eq!(output.exit_code, ExitCode::GenericFailure); + let body: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(body["error"]["code"], "installer.command_failed"); + assert!(output.stderr.contains("--fail")); + assert!(output.stderr.contains("app.apk")); +} + #[test] fn app_download_rejects_transport_controls_and_maps_manifest_errors_stably() { let temp = fixture("2"); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 2e480bc..6812b32 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -778,6 +778,67 @@ return package_version {{ installed = {{ {{ kind = "android_package", package_na .expect("write local artifact package"); } +#[given("the package installer uses a fake executable")] +fn fake_package_installer(world: &mut CliWorld) { + deterministic_local_artifact(world); + let temp = world.temp.as_ref().unwrap().path(); + let source_path = temp.join("fake_installer.rs"); + let executable = temp.join(format!("fake-installer{}", std::env::consts::EXE_SUFFIX)); + fs::write( + &source_path, + r#"fn main() { + for argument in std::env::args().skip(1) { + println!("{argument}"); + } +}"#, + ) + .unwrap(); + let status = Command::new("rustc") + .arg(&source_path) + .arg("-o") + .arg(&executable) + .status() + .expect("rustc must be available while running Rust tests"); + assert!(status.success(), "compile platform-native fake installer"); + let package_dir = world.fixture_repo_path.as_ref().unwrap().join( + world + .fixture_package_id + .as_ref() + .unwrap() + .replace('/', std::path::MAIN_SEPARATOR_STR), + ); + let source = fs::read_to_string(package_dir.join("1.20.0.lua")).unwrap(); + let source = source.replacen("artifacts = { { name", &format!("install = {{ executable = {:?}, args = {{ \"install\", {{ artifact = \"app.apk\" }}, \"$HOME;literal\" }} }}, artifacts = {{ {{ name", executable.to_string_lossy()), 1); + fs::write(package_dir.join("1.20.0.lua"), source).unwrap(); +} + +#[given("the package installer names a missing executable")] +fn missing_package_installer(world: &mut CliWorld) { + deterministic_local_artifact(world); + let package_dir = world.fixture_repo_path.as_ref().unwrap().join( + world + .fixture_package_id + .as_ref() + .unwrap() + .replace('/', std::path::MAIN_SEPARATOR_STR), + ); + let source = fs::read_to_string(package_dir.join("1.20.0.lua")).unwrap(); + let source = source.replacen( + "artifacts = { { name", + "install = { executable = \"getter-definitely-missing-installer\", args = {} }, artifacts = { { name", + 1, + ); + fs::write(package_dir.join("1.20.0.lua"), source).unwrap(); +} + +#[when(expr = "I run getter app install for {string}")] +fn run_app_install(world: &mut CliWorld, package_id: String) { + run_app_command(world, "install", package_id, false); + if let Some(server) = world.local_artifact_server.take() { + server.join().unwrap(); + } +} + #[when(expr = "I run getter app download for {string}")] fn run_app_download(world: &mut CliWorld, package_id: String) { run_app_command(world, "download", package_id, false); @@ -859,6 +920,18 @@ fn download_then_reuse(world: &mut CliWorld) { ); } +#[then("the install succeeds with exact literal and artifact argv")] +fn install_exact_argv(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["data"]["status"], "succeeded"); + assert_eq!(json["data"]["command"]["args"][0], "install"); + assert!(json["data"]["command"]["args"][1] + .as_str() + .unwrap() + .starts_with('/')); + assert_eq!(json["data"]["command"]["args"][2], "$HOME;literal"); +} + #[then(expr = "the command fails with stable error {string}")] fn stable_error(world: &mut CliWorld, code: String) { let json = current_json(world); diff --git a/crates/getter-cli/tests/features/cli/app_inspect_check.feature b/crates/getter-cli/tests/features/cli/app_inspect_check.feature index 841ecf3..6d5c381 100644 --- a/crates/getter-cli/tests/features/cli/app_inspect_check.feature +++ b/crates/getter-cli/tests/features/cli/app_inspect_check.feature @@ -30,6 +30,16 @@ Feature: Inspect and actively check a tracked app When I run getter app download twice for "android/app/fdroid" Then the first download is downloaded and the second is reused from the same path + Scenario: Install executes exact declared argv + Given the package installer uses a fake executable + When I run getter app install for "android/app/fdroid" + Then the install succeeds with exact literal and artifact argv + + Scenario: Install reports a missing package-declared command + Given the package installer names a missing executable + When I run getter app install for "android/app/fdroid" + Then the command fails with stable error "installer.command_not_found" + Scenario: Download rejects transport controls When I run getter app download for "android/app/fdroid" with a URL control Then the command fails with stable error "cli.usage" diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 0360c2b..ee7d976 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -277,6 +277,56 @@ pub struct PackagePermissions { pub free_network: bool, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct InstallerCommand { + pub executable: String, + #[serde(default)] + pub args: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum InstallerArg { + Literal(String), + Artifact(InstallerArtifactReference), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct InstallerArtifactReference { + pub artifact: String, +} + +/// A getter-owned, deferred installer declaration. Package evaluation preserves +/// this JSON value; install operations validate it as an [`InstallerCommand`]. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(transparent)] +pub struct InstallerDeclaration(serde_json::Value); + +impl InstallerDeclaration { + pub fn new(value: serde_json::Value) -> Self { + Self(value) + } + + pub fn as_json(&self) -> &serde_json::Value { + &self.0 + } + + pub fn parse(&self) -> Result { + let mut value = self.0.clone(); + if let Some(args) = value + .as_object_mut() + .and_then(|object| object.get_mut("args")) + { + if args.as_object().is_some_and(serde_json::Map::is_empty) { + *args = serde_json::Value::Array(Vec::new()); + } + } + serde_json::from_value(value) + } +} + /// Candidate version/update discovered by provider/package logic. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct UpdateCandidate { @@ -291,6 +341,8 @@ pub struct UpdateCandidate { pub source: Option, #[serde(default)] pub artifacts: Vec, + #[serde(default)] + pub install: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index fa6044a..bb54f43 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -5,8 +5,8 @@ use crate::repository::{ PackageVersionScript, RepositoryLoadError, LUA_API_SHEBANG_V1, REPOSITORY_LUACLASS_DIR, }; use crate::{ - InstalledTarget, PackageId, PackagePermissions, RepositoryId, ResolvedPackage, UpdateArtifact, - UpdateCandidate, + InstalledTarget, InstallerDeclaration, PackageId, PackagePermissions, RepositoryId, + ResolvedPackage, UpdateArtifact, UpdateCandidate, }; use mlua::{Function, Lua, Table, Value}; use serde_json::{Map, Number, Value as JsonValue}; @@ -16,6 +16,7 @@ use std::path::{Path, PathBuf}; const BUILTIN_LUACLASS_MODULES: &[(&str, &str)] = &[ ("android", include_str!("luaclass/android.lua")), + ("installer", include_str!("luaclass/installer.lua")), ( "fdroid_android", include_str!("luaclass/fdroid_android.lua"), @@ -715,6 +716,36 @@ fn package_from_version_json( }) } +fn reject_unknown_keys( + path: &Path, + object: &Map, + kind: &str, + allowed: &[&str], +) -> Result<(), LuaPackageError> { + if let Some(key) = object.keys().find(|key| !allowed.contains(&key.as_str())) { + return Err(LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("unknown {kind} field '{key}'"), + }); + } + Ok(()) +} + +fn required_nonempty_string<'a>( + path: &Path, + object: &'a Map, + field: &str, +) -> Result<&'a str, LuaPackageError> { + let value = required_string(path, object, field)?; + if value.is_empty() { + return Err(LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("string field '{field}' must not be empty"), + }); + } + Ok(value) +} + fn required_string<'a>( path: &Path, object: &'a Map, @@ -785,7 +816,25 @@ fn parse_update_candidates( path: path.to_path_buf(), message: "updates entries must be objects".to_owned(), })?; + reject_unknown_keys( + path, + object, + "update candidate", + &[ + "version", + "version_code", + "changelog", + "channel", + "source", + "artifacts", + "install", + ], + )?; let artifacts = parse_update_artifacts(path, object.get("artifacts"))?; + let install = object + .get("install") + .cloned() + .map(InstallerDeclaration::new); Ok(UpdateCandidate { version: required_string(path, object, "version")?.to_owned(), version_code: optional_i64(path, object, "version_code")?, @@ -793,6 +842,7 @@ fn parse_update_candidates( channel: optional_string(object, "channel"), source: optional_string(object, "source"), artifacts, + install, }) }) .collect() @@ -809,15 +859,22 @@ fn parse_update_artifacts( path: path.to_path_buf(), message: "field 'artifacts' must be an array".to_owned(), })?; - array + let artifacts = array .iter() .map(|item| { let object = item.as_object().ok_or_else(|| LuaPackageError::Schema { path: path.to_path_buf(), message: "artifact entries must be objects".to_owned(), })?; + reject_unknown_keys( + path, + object, + "artifact", + &["name", "url", "content_type", "file_name", "sha256", "size"], + )?; + let name = required_nonempty_string(path, object, "name")?.to_owned(); Ok(UpdateArtifact { - name: required_string(path, object, "name")?.to_owned(), + name, url: required_string(path, object, "url")?.to_owned(), content_type: optional_string(object, "content_type"), file_name: optional_string(object, "file_name"), @@ -825,7 +882,17 @@ fn parse_update_artifacts( size: optional_u64(path, object, "size")?, }) }) - .collect() + .collect::, _>>()?; + let mut names = std::collections::HashSet::new(); + for artifact in &artifacts { + if !names.insert(&artifact.name) { + return Err(LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("duplicate artifact name '{}'", artifact.name), + }); + } + } + Ok(artifacts) } fn optional_string(object: &Map, field: &str) -> Option { @@ -949,6 +1016,80 @@ mod tests { fs::write(package_dir.join("9999.lua"), script_source).unwrap(); } + #[test] + fn preserves_chosen_installer_table_declaration_without_eager_validation() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local installer = require("luaclass.installer") +return package_version { + updates = {{ + version = "2", + artifacts = {{ name = "base", url = "https://example.invalid/base.apk" }}, + install = installer.command { + executable = "fake-installer", + args = { "install", installer.artifact("base"), "literal $HOME;not-shell" }, + }, + }}, +}"#, + ); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + assert_eq!( + package.updates[0].install.as_ref().unwrap().as_json(), + &serde_json::json!({ + "executable": "fake-installer", + "args": ["install", {"artifact": "base"}, "literal $HOME;not-shell"] + }) + ); + } + + #[test] + fn rejects_unknown_candidate_and_artifact_keys_and_invalid_artifact_names() { + for (case, source) in [ + ("candidate", r#"updates = {{ version="2", surprise=true }}"#), + ( + "artifact", + r#"updates = {{ version="2", artifacts={{name="a",url="u",surprise=true}} }}"#, + ), + ( + "empty artifact", + r#"updates = {{ version="2", artifacts={{name="",url="u"}} }}"#, + ), + ( + "duplicate artifact", + r#"updates = {{ version="2", artifacts={{name="a",url="u"},{name="a",url="v"}} }}"#, + ), + ] { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + &format!("#!/bin/upa-lua v1\nreturn package_version {{ {source} }}"), + ); + let error = evaluate_single_package_directory(temp.path(), "official").unwrap_err(); + assert!( + matches!(error, LuaPackageError::Schema { .. }), + "{case}: {error}" + ); + } + } + + #[test] + fn rejects_legacy_installer_candidate_field() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version = "2", installer = {} }} }"#, + ); + let error = evaluate_single_package_directory(temp.path(), "official").unwrap_err(); + assert!(matches!(error, LuaPackageError::Schema { .. })); + assert!(error + .to_string() + .contains("unknown update candidate field 'installer'")); + } + #[test] fn evaluates_json_like_lua_package_table() { let temp = tempfile::tempdir().unwrap(); @@ -1729,6 +1870,130 @@ return package_version { name = first .. "|" .. second } ); } + #[test] + fn fdroid_luaclass_propagates_declared_install_to_every_candidate_without_defaulting() { + for declared in [false, true] { + let temp = tempfile::tempdir().unwrap(); + let install = if declared { + r#", install = { executable = "pm", args = { "install" } }"# + } else { + "" + }; + write_simple_android_package( + temp.path(), + &format!( + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package {{ package_name = "com.example"{install} }} +"#, + ), + ); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("official").unwrap(), + package_dir, + &metadata, + script, + |lua| { + let candidates = lua.create_table()?; + for index in 1..=2 { + let candidate = lua.create_table()?; + candidate.set("version", index.to_string())?; + candidates.raw_set(index, candidate)?; + } + let fdroid = lua.create_table()?; + fdroid.set( + "update_candidates", + lua.create_function(move |lua, _: Table| { + let result = lua.create_table()?; + result.set("candidates", candidates.clone())?; + Ok(result) + })?, + )?; + let provider = lua.create_table()?; + provider.set("fdroid", fdroid)?; + let getter = lua.create_table()?; + getter.set("provider", provider)?; + lua.globals().set("getter", getter) + }, + ) + .unwrap(); + assert_eq!(package.updates.len(), 2); + assert!(package.updates.iter().all(|candidate| { + candidate + .install + .as_ref() + .map(|install| install.as_json().clone()) + == declared.then(|| serde_json::json!({"executable":"pm","args":["install"]})) + })); + } + } + + #[test] + fn github_luaclass_propagates_declared_install_to_every_candidate_without_defaulting() { + for declared in [false, true] { + let temp = tempfile::tempdir().unwrap(); + let install = if declared { + r#", install = { executable = "pm", args = { "install" } }"# + } else { + "" + }; + write_simple_android_package( + temp.path(), + &format!( + r#"#!/bin/upa-lua v1 +local github = require("luaclass.github_android_apk") +return github.package {{ owner = "owner", repo = "repo"{install} }} +"#, + ), + ); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("official").unwrap(), + package_dir, + &metadata, + script, + |lua| { + let candidates = lua.create_table()?; + for index in 1..=2 { + let candidate = lua.create_table()?; + candidate.set("version", index.to_string())?; + candidates.raw_set(index, candidate)?; + } + let github = lua.create_table()?; + github.set( + "release_candidates", + lua.create_function(move |lua, _: Table| { + let result = lua.create_table()?; + result.set("candidates", candidates.clone())?; + Ok(result) + })?, + )?; + let provider = lua.create_table()?; + provider.set("github", github)?; + let getter = lua.create_table()?; + getter.set("provider", provider)?; + lua.globals().set("getter", getter) + }, + ) + .unwrap(); + assert_eq!(package.updates.len(), 2); + assert!(package.updates.iter().all(|candidate| { + candidate + .install + .as_ref() + .map(|install| install.as_json().clone()) + == declared.then(|| serde_json::json!({"executable":"pm","args":["install"]})) + })); + } + } + #[test] fn package_directory_http_get_rejects_unsupported_options() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/luaclass/fdroid_android.lua b/crates/getter-core/src/luaclass/fdroid_android.lua index 7029ffe..77b4994 100644 --- a/crates/getter-core/src/luaclass/fdroid_android.lua +++ b/crates/getter-core/src/luaclass/fdroid_android.lua @@ -41,6 +41,11 @@ function fdroid.package(spec) endpoint_id = optional_string(spec, "endpoint_id"), } local result = fdroid_update_candidates()(host_spec) + if spec.install ~= nil then + for _, candidate in ipairs(result.candidates) do + candidate.install = spec.install + end + end return package_version { source_priority = { "fdroid" }, updates = result.candidates, diff --git a/crates/getter-core/src/luaclass/github_android_apk.lua b/crates/getter-core/src/luaclass/github_android_apk.lua index 753f7f0..799204d 100644 --- a/crates/getter-core/src/luaclass/github_android_apk.lua +++ b/crates/getter-core/src/luaclass/github_android_apk.lua @@ -66,6 +66,11 @@ function github_android.package(spec) endpoint_id = optional_string(spec, "endpoint_id"), } local result = github_release_candidates()(host_spec) + if spec.install ~= nil then + for _, candidate in ipairs(result.candidates) do + candidate.install = spec.install + end + end local package = { name = spec.name, source_priority = { "github" }, diff --git a/crates/getter-core/src/luaclass/installer.lua b/crates/getter-core/src/luaclass/installer.lua new file mode 100644 index 0000000..7f3aff9 --- /dev/null +++ b/crates/getter-core/src/luaclass/installer.lua @@ -0,0 +1,11 @@ +local installer = {} + +function installer.artifact(name) + return { artifact = name } +end + +function installer.command(spec) + return spec +end + +return installer diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs index 4315365..d07ee50 100644 --- a/crates/getter-core/src/update.rs +++ b/crates/getter-core/src/update.rs @@ -509,6 +509,7 @@ mod tests { channel: None, source: None, artifacts: Vec::new(), + install: None, }], UpdateSelectionPolicy::default(), ) @@ -553,6 +554,7 @@ mod tests { sha256: None, size: None, }], + install: None, } } } diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 6ce6120..0ec16b8 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -19,6 +19,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" sha2 = "0.10" thiserror = "1" +which = "=8.0.3" ureq = { version = "3.3.0", default-features = false, features = ["gzip", "rustls"] } [dev-dependencies] diff --git a/crates/getter-operations/src/app.rs b/crates/getter-operations/src/app.rs index 2550d19..0a18e60 100644 --- a/crates/getter-operations/src/app.rs +++ b/crates/getter-operations/src/app.rs @@ -15,7 +15,7 @@ use getter_core::runtime::IssuedAction; use getter_core::update::compare_versions; use getter_core::PackageId; #[cfg(feature = "lua")] -use getter_core::UpdateArtifact; +use getter_core::{InstallerArg, InstallerCommand, UpdateArtifact}; use getter_storage::{MainDb, StorageError, StoredTrackedPackage}; use serde::{Deserialize, Serialize}; #[cfg(feature = "lua")] @@ -75,6 +75,27 @@ pub enum AppOperationError { #[cfg(feature = "lua")] #[error("artifact download failed: {0}")] ArtifactTransport(#[from] crate::download::RuntimeDownloadTransportError), + #[cfg(feature = "lua")] + #[error("selected candidate has no installer command")] + InstallerMissing, + #[cfg(feature = "lua")] + #[error("installer declaration schema is invalid: {0}")] + InstallerSchema(String), + #[cfg(feature = "lua")] + #[error("installer artifact reference '{0}' is unknown")] + InstallerArtifactUnknown(String), + #[cfg(feature = "lua")] + #[error("installer artifact name '{0}' is duplicated")] + InstallerArtifactDuplicate(String), + #[cfg(feature = "lua")] + #[error("installer executable '{0}' was not found")] + InstallerCommandNotFound(String), + #[cfg(feature = "lua")] + #[error("installer command could not be spawned: {0}")] + InstallerCommandSpawnFailed(std::io::Error), + #[cfg(feature = "lua")] + #[error("installer command failed with exit code {0}")] + InstallerCommandFailed(i32), } impl AppOperationError { @@ -114,6 +135,20 @@ impl AppOperationError { Self::ArtifactIo(_) => "artifact.io_error", #[cfg(feature = "lua")] Self::ArtifactTransport(_) => "artifact.transport_error", + #[cfg(feature = "lua")] + Self::InstallerMissing => "installer.missing", + #[cfg(feature = "lua")] + Self::InstallerSchema(_) => "installer.schema_invalid", + #[cfg(feature = "lua")] + Self::InstallerArtifactUnknown(_) => "installer.artifact_unknown", + #[cfg(feature = "lua")] + Self::InstallerArtifactDuplicate(_) => "installer.artifact_duplicate", + #[cfg(feature = "lua")] + Self::InstallerCommandNotFound(_) => "installer.command_not_found", + #[cfg(feature = "lua")] + Self::InstallerCommandSpawnFailed(_) => "installer.command_spawn_failed", + #[cfg(feature = "lua")] + Self::InstallerCommandFailed(_) => "installer.command_failed", } } @@ -146,6 +181,20 @@ impl AppOperationError { Self::ArtifactIo(_) => "Getter artifact staging failed", #[cfg(feature = "lua")] Self::ArtifactTransport(_) => "Getter artifact download failed", + #[cfg(feature = "lua")] + Self::InstallerMissing => "Getter package installer is missing", + #[cfg(feature = "lua")] + Self::InstallerSchema(_) => "Getter package installer schema is invalid", + #[cfg(feature = "lua")] + Self::InstallerArtifactUnknown(_) => "Getter installer references an unknown artifact", + #[cfg(feature = "lua")] + Self::InstallerArtifactDuplicate(_) => "Getter installer artifact name is duplicated", + #[cfg(feature = "lua")] + Self::InstallerCommandNotFound(_) => "Getter installer command was not found", + #[cfg(feature = "lua")] + Self::InstallerCommandSpawnFailed(_) => "Getter installer command could not be started", + #[cfg(feature = "lua")] + Self::InstallerCommandFailed(_) => "Getter installer command failed", } } @@ -168,7 +217,14 @@ impl AppOperationError { | Self::ArtifactPathCollision { .. } | Self::Sha256Mismatch { .. } | Self::ArtifactIo(_) - | Self::ArtifactTransport(_) => self.to_string(), + | Self::ArtifactTransport(_) + | Self::InstallerMissing + | Self::InstallerSchema(_) + | Self::InstallerArtifactUnknown(_) + | Self::InstallerArtifactDuplicate(_) + | Self::InstallerCommandNotFound(_) + | Self::InstallerCommandSpawnFailed(_) + | Self::InstallerCommandFailed(_) => self.to_string(), } } } @@ -189,6 +245,124 @@ pub struct AppDownloadResult { pub artifacts: Vec, } +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct ResolvedInstallerCommand { + pub executable: PathBuf, + pub args: Vec, +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RunnerOutput { + pub status: i32, + pub stdout: Vec, + pub stderr: Vec, +} + +#[cfg(feature = "lua")] +pub trait CommandResolver { + fn resolve(&self, executable: &str) -> Option; +} +#[cfg(feature = "lua")] +pub trait CommandRunner { + fn run(&self, command: &ResolvedInstallerCommand) -> std::io::Result; +} + +#[cfg(feature = "lua")] +pub trait CommandObserver { + fn before_execute(&self, command: &ResolvedInstallerCommand) -> std::io::Result<()>; +} + +#[cfg(feature = "lua")] +pub struct NoopCommandObserver; +#[cfg(feature = "lua")] +impl CommandObserver for NoopCommandObserver { + fn before_execute(&self, _: &ResolvedInstallerCommand) -> std::io::Result<()> { + Ok(()) + } +} + +#[cfg(feature = "lua")] +pub struct PathCommandResolver; +#[cfg(feature = "lua")] +impl CommandResolver for PathCommandResolver { + fn resolve(&self, executable: &str) -> Option { + which::which(executable).ok() + } +} + +#[cfg(feature = "lua")] +pub struct ProcessCommandRunner; +#[cfg(feature = "lua")] +impl CommandRunner for ProcessCommandRunner { + fn run(&self, command: &ResolvedInstallerCommand) -> std::io::Result { + let mut child = std::process::Command::new(&command.executable) + .args(&command.args) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn()?; + let stdout = child.stdout.take().ok_or_else(|| { + std::io::Error::other("installer runner failed to capture child stdout") + })?; + let stderr = child.stderr.take().ok_or_else(|| { + std::io::Error::other("installer runner failed to capture child stderr") + })?; + + let stdout_reader = std::thread::spawn(move || drain_bounded(stdout)); + let stderr_reader = std::thread::spawn(move || drain_bounded(stderr)); + let status = child.wait(); + let stdout = join_output_reader(stdout_reader, "stdout")?; + let stderr = join_output_reader(stderr_reader, "stderr")?; + let status = status?; + + Ok(RunnerOutput { + status: status.code().unwrap_or(-1), + stdout, + stderr, + }) + } +} + +#[cfg(feature = "lua")] +fn drain_bounded(mut stream: impl std::io::Read) -> std::io::Result> { + const LIMIT: usize = 64 * 1024; + let mut captured = Vec::with_capacity(LIMIT); + let mut buffer = [0_u8; 8 * 1024]; + loop { + let read = stream.read(&mut buffer)?; + if read == 0 { + return Ok(captured); + } + let remaining = LIMIT.saturating_sub(captured.len()); + captured.extend_from_slice(&buffer[..read.min(remaining)]); + } +} + +#[cfg(feature = "lua")] +fn join_output_reader( + reader: std::thread::JoinHandle>>, + stream: &str, +) -> std::io::Result> { + reader + .join() + .map_err(|_| std::io::Error::other(format!("installer runner {stream} reader panicked")))? +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AppInstallResult { + pub package_id: PackageId, + pub repository_id: String, + pub version: String, + pub artifacts: Vec, + pub command: ResolvedInstallerCommand, + pub status: String, + pub exit_code: i32, + pub stdout: String, + pub stderr: String, +} + #[cfg(feature = "lua")] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct StagedArtifact { @@ -262,6 +436,22 @@ pub fn download_app_with_transports( github_release_transport: Option>, download_transport: Rc, ) -> Result { + prepare_app_with_transports( + data_dir, + package_id, + github_release_transport, + download_transport, + ) + .map(|(result, _)| result) +} + +#[cfg(feature = "lua")] +fn prepare_app_with_transports( + data_dir: &Path, + package_id: &PackageId, + github_release_transport: Option>, + download_transport: Rc, +) -> Result<(AppDownloadResult, getter_core::UpdateCandidate), AppOperationError> { startup::bootstrap_data_dir(data_dir)?; let db = MainDb::open(data_dir.join("main.db"))?; let tracked = tracked_package(&db, package_id)?; @@ -295,7 +485,7 @@ pub fn download_app_with_transports( })?; let manifest = PackageManifest::parse(&manifest_content).map_err(AppOperationError::ManifestInvalid)?; - let downloads = data_dir.join("downloads"); + let downloads = absolute_staging_root(data_dir)?; let prepared = candidate .artifacts .iter() @@ -328,14 +518,200 @@ pub fn download_app_with_transports( .into_iter() .map(|artifact| stage_artifact(artifact, download_transport.as_ref())) .collect::, _>>()?; - Ok(AppDownloadResult { - package_id: package_id.clone(), - repository_id: evaluation.package.repository.to_string(), - version: candidate.version, - artifacts, + Ok(( + AppDownloadResult { + package_id: package_id.clone(), + repository_id: evaluation.package.repository.to_string(), + version: candidate.version.clone(), + artifacts, + }, + candidate, + )) +} + +#[cfg(feature = "lua")] +pub fn install_app( + data_dir: &Path, + package_id: &PackageId, +) -> Result { + install_app_with_observer(data_dir, package_id, &NoopCommandObserver) +} + +#[cfg(feature = "lua")] +pub fn install_app_with_observer( + data_dir: &Path, + package_id: &PackageId, + observer: &dyn CommandObserver, +) -> Result { + install_app_with_dependencies_and_observer( + data_dir, + package_id, + Some(Rc::new( + crate::github_releases::UreqGithubReleaseTransport::new(), + )), + Rc::new(crate::download::UreqRuntimeDownloadTransport::new()), + &PathCommandResolver, + &ProcessCommandRunner, + observer, + ) +} + +#[cfg(feature = "lua")] +pub fn install_app_with_dependencies( + data_dir: &Path, + package_id: &PackageId, + github_release_transport: Option>, + download_transport: Rc, + resolver: &dyn CommandResolver, + runner: &dyn CommandRunner, +) -> Result { + install_app_with_dependencies_and_observer( + data_dir, + package_id, + github_release_transport, + download_transport, + resolver, + runner, + &NoopCommandObserver, + ) +} + +#[cfg(feature = "lua")] +pub fn install_app_with_dependencies_and_observer( + data_dir: &Path, + package_id: &PackageId, + github_release_transport: Option>, + download_transport: Rc, + resolver: &dyn CommandResolver, + runner: &dyn CommandRunner, + observer: &dyn CommandObserver, +) -> Result { + let (download, candidate) = prepare_app_with_transports( + data_dir, + package_id, + github_release_transport, + download_transport, + )?; + let installer = candidate + .install + .ok_or(AppOperationError::InstallerMissing)? + .parse() + .map_err(|error| AppOperationError::InstallerSchema(error.to_string()))?; + validate_installer_command(&installer)?; + let command = resolve_installer(&installer, &download.artifacts, resolver)?; + observer + .before_execute(&command) + .map_err(AppOperationError::InstallerCommandSpawnFailed)?; + let output = runner + .run(&command) + .map_err(AppOperationError::InstallerCommandSpawnFailed)?; + let stdout = bounded_output(&output.stdout); + let stderr = bounded_output(&output.stderr); + if output.status != 0 { + return Err(AppOperationError::InstallerCommandFailed(output.status)); + } + Ok(AppInstallResult { + package_id: download.package_id, + repository_id: download.repository_id, + version: download.version, + artifacts: download.artifacts, + command, + status: "succeeded".into(), + exit_code: output.status, + stdout, + stderr, }) } +#[cfg(feature = "lua")] +fn resolve_installer( + installer: &InstallerCommand, + artifacts: &[StagedArtifact], + resolver: &dyn CommandResolver, +) -> Result { + let executable = resolver + .resolve(&installer.executable) + .ok_or_else(|| AppOperationError::InstallerCommandNotFound(installer.executable.clone()))?; + let mut by_name = std::collections::HashMap::new(); + for artifact in artifacts { + if by_name + .insert(artifact.name.as_str(), &artifact.path) + .is_some() + { + return Err(AppOperationError::InstallerArtifactDuplicate( + artifact.name.clone(), + )); + } + } + let args = installer + .args + .iter() + .map(|arg| match arg { + InstallerArg::Literal(value) => Ok(value.clone()), + InstallerArg::Artifact(reference) => by_name + .get(reference.artifact.as_str()) + .map(|path| path.to_string_lossy().into_owned()) + .ok_or_else(|| { + AppOperationError::InstallerArtifactUnknown(reference.artifact.clone()) + }), + }) + .collect::, _>>()?; + Ok(ResolvedInstallerCommand { executable, args }) +} + +#[cfg(feature = "lua")] +fn validate_installer_command(installer: &InstallerCommand) -> Result<(), AppOperationError> { + if installer.executable.is_empty() { + return Err(AppOperationError::InstallerSchema( + "installer executable must not be empty".to_owned(), + )); + } + if installer.args.iter().any( + |arg| matches!(arg, InstallerArg::Artifact(reference) if reference.artifact.is_empty()), + ) { + return Err(AppOperationError::InstallerSchema( + "installer artifact reference must not be empty".to_owned(), + )); + } + Ok(()) +} + +#[cfg(feature = "lua")] +fn bounded_output(bytes: &[u8]) -> String { + const LIMIT: usize = 64 * 1024; + String::from_utf8_lossy(&bytes[..bytes.len().min(LIMIT)]).into_owned() +} + +#[cfg(all(test, feature = "lua"))] +mod installer_staging_tests { + use super::absolute_staging_root; + + #[test] + fn relative_nonexistent_data_dir_produces_absolute_canonical_staging_root() { + let name = format!("target/relative-staging-test-{}", std::process::id()); + let data_dir = std::path::Path::new(&name); + let _ = std::fs::remove_dir_all(data_dir); + let root = absolute_staging_root(data_dir).unwrap(); + assert!(root.is_absolute()); + assert_eq!(root.file_name().unwrap(), "downloads"); + assert!(root.parent().unwrap().is_dir()); + std::fs::remove_dir_all(data_dir).unwrap(); + } +} + +#[cfg(feature = "lua")] +fn absolute_staging_root(data_dir: &Path) -> Result { + let absolute_data_dir = if data_dir.is_absolute() { + data_dir.to_path_buf() + } else { + std::env::current_dir() + .map_err(AppOperationError::ArtifactIo)? + .join(data_dir) + }; + fs::create_dir_all(&absolute_data_dir)?; + Ok(fs::canonicalize(absolute_data_dir)?.join("downloads")) +} + #[cfg(feature = "lua")] fn prepare_artifact( downloads: &Path, diff --git a/crates/getter-operations/src/github_autogen.rs b/crates/getter-operations/src/github_autogen.rs index 554186e..f552253 100644 --- a/crates/getter-operations/src/github_autogen.rs +++ b/crates/getter-operations/src/github_autogen.rs @@ -603,6 +603,7 @@ mod tests { sha256: Some("A".repeat(64)), size: None, }], + install: None, }; let second = getter_core::UpdateCandidate { version: "2".to_owned(), @@ -628,6 +629,7 @@ mod tests { size: None, }, ], + install: None, }; let repeated = getter_core::UpdateCandidate { @@ -644,6 +646,7 @@ mod tests { sha256: Some("A".repeat(64)), size: None, }], + install: None, }; let distinct = getter_core::UpdateCandidate { version: "4".to_owned(), @@ -659,6 +662,7 @@ mod tests { sha256: Some("D".repeat(64)), size: None, }], + install: None, }; let manifest = github_manifest(&["c".repeat(128)], &[first, second, repeated, distinct]); diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index b82cc76..8bee8e2 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -1703,6 +1703,7 @@ return package_version { sha256: None, size: None, }], + install: None, }) .collect(), } diff --git a/crates/getter-operations/tests/installer.rs b/crates/getter-operations/tests/installer.rs new file mode 100644 index 0000000..7a48659 --- /dev/null +++ b/crates/getter-operations/tests/installer.rs @@ -0,0 +1,408 @@ +#![cfg(feature = "lua")] + +use getter_core::repository::RepositoryMetadata; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_operations::app::{ + download_app_with_transports, install_app_with_dependencies, + install_app_with_dependencies_and_observer, CommandObserver, CommandResolver, CommandRunner, + ResolvedInstallerCommand, RunnerOutput, +}; +use getter_operations::download::{ + RuntimeDownloadSink, RuntimeDownloadTransport, RuntimeDownloadTransportError, + RuntimeDownloadTransportRequest, +}; +use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; +use sha2::{Digest, Sha256}; +use std::cell::RefCell; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::rc::Rc; + +struct Bytes(Vec); +impl RuntimeDownloadTransport for Bytes { + fn fetch( + &self, + _: &RuntimeDownloadTransportRequest, + sink: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError> { + sink.write_chunk(&self.0) + .map_err(RuntimeDownloadTransportError::Sink) + } +} +struct Resolver(PathBuf); +impl CommandResolver for Resolver { + fn resolve(&self, executable: &str) -> Option { + (executable == "fake-installer").then(|| self.0.clone()) + } +} +#[derive(Default)] +struct Runner { + seen: RefCell>, +} +impl CommandRunner for Runner { + fn run(&self, command: &ResolvedInstallerCommand) -> std::io::Result { + *self.seen.borrow_mut() = Some(command.clone()); + Ok(RunnerOutput { + status: 0, + stdout: b"installed".to_vec(), + stderr: Vec::new(), + }) + } +} +fn digest(bytes: &[u8]) -> String { + format!("{:x}", Sha256::digest(bytes)) +} + +fn native_fixture(temp: &Path, name: &str, source_code: &str) -> PathBuf { + let source = temp.join(format!("{name}.rs")); + let executable = temp.join(format!("{name}{}", std::env::consts::EXE_SUFFIX)); + fs::write(&source, source_code).unwrap(); + let status = Command::new("rustc") + .arg(&source) + .arg("-o") + .arg(&executable) + .status() + .expect("rustc must be available while running Rust tests"); + assert!(status.success(), "compile platform-native fixture {name}"); + executable +} + +fn fake_installer(temp: &Path) -> PathBuf { + native_fixture( + temp, + "fake-installer", + r#"fn main() { + for argument in std::env::args().skip(1) { + println!("{argument}"); + } +}"#, + ) +} +fn fixture(install: &str, bytes: &[u8]) -> tempfile::TempDir { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + let package = temp.path().join("repo/official/android/com.example"); + fs::create_dir_all(&package).unwrap(); + fs::write( + package.join("metadata.jsonc"), + r#"{"type":"android:app","android":{"package_name":"com.example"}}"#, + ) + .unwrap(); + fs::write( + package.join("Manifest"), + format!("{} app.apk\n", digest(bytes)), + ) + .unwrap(); + fs::write(package.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{{{ version="2", artifacts={{{{name="app",file_name="app.apk",url="mock://app"}}}}, install={install} }}}} }}"#)).unwrap(); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".into(), + priority: RepositoryPriority::DEFAULT, + api_version: "v1".into(), + }, + Some(&temp.path().join("repo/official")), + None, + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/com.example".parse().unwrap(), + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + temp +} + +#[test] +fn malformed_installer_does_not_block_download_but_fails_install_after_staging() { + let bytes = b"apk"; + let temp = fixture( + r#"{executable="fake-installer", args={}, surprise=true}"#, + bytes, + ); + let download = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + ) + .unwrap(); + assert!(download.artifacts[0].path.exists()); + + let error = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &Runner::default(), + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.schema_invalid"); + assert!(download.artifacts[0].path.exists()); +} + +#[test] +fn resolves_artifact_to_staged_absolute_path_and_preserves_literal_argv() { + let bytes = b"apk"; + let temp = fixture( + r#"{executable="fake-installer",args={"install",{artifact="app"},"$HOME;literal"}}"#, + bytes, + ); + let runner = Runner::default(); + let result = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &runner, + ) + .unwrap(); + let command = runner.seen.borrow().clone().unwrap(); + assert_eq!(command.executable, PathBuf::from("/fake/bin/installer")); + assert_eq!(command.args[0], "install"); + assert!(Path::new(&command.args[1]).is_absolute()); + assert_eq!(command.args[1], result.artifacts[0].path.to_string_lossy()); + assert_eq!(command.args[2], "$HOME;literal"); + assert_eq!(result.exit_code, 0); + assert_eq!(result.stdout, "installed"); +} + +#[test] +fn observer_runs_immediately_before_runner() { + struct Ordered { + events: Rc>>, + } + impl CommandObserver for Ordered { + fn before_execute(&self, _: &ResolvedInstallerCommand) -> std::io::Result<()> { + self.events.borrow_mut().push("observed"); + Ok(()) + } + } + impl CommandRunner for Ordered { + fn run(&self, _: &ResolvedInstallerCommand) -> std::io::Result { + self.events.borrow_mut().push("ran"); + Ok(RunnerOutput { + status: 0, + stdout: vec![], + stderr: vec![], + }) + } + } + let bytes = b"apk"; + let temp = fixture(r#"{executable="fake-installer",args={}}"#, bytes); + let events = Rc::new(RefCell::new(Vec::new())); + let ordered = Ordered { + events: events.clone(), + }; + install_app_with_dependencies_and_observer( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &ordered, + &ordered, + ) + .unwrap(); + assert_eq!(&*events.borrow(), &["observed", "ran"]); +} + +#[test] +fn rejects_unknown_reference_after_preserving_staged_file() { + let bytes = b"apk"; + let temp = fixture( + r#"{executable="fake-installer",args={{artifact="missing"}}}"#, + bytes, + ); + let error = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &Runner::default(), + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.artifact_unknown"); + assert!(temp + .path() + .join("downloads") + .read_dir() + .unwrap() + .next() + .is_some()); +} + +#[test] +fn process_runner_executes_fake_file_with_exact_argv_without_shell_expansion() { + let bytes = b"apk"; + let temp = fixture( + r#"{executable="fake-installer",args={"install",{artifact="app"},"$HOME;literal"}}"#, + bytes, + ); + let executable = fake_installer(temp.path()); + let result = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(executable), + &getter_operations::app::ProcessCommandRunner, + ) + .unwrap(); + let lines: Vec<_> = result.stdout.lines().collect(); + assert_eq!(lines[0], "install"); + assert_eq!(lines[1], result.artifacts[0].path.to_string_lossy()); + assert_eq!(lines[2], "$HOME;literal"); +} + +#[test] +fn process_runner_bounds_and_concurrently_drains_both_output_streams() { + const CAPTURE_LIMIT: usize = 64 * 1024; + + let temp = tempfile::tempdir().unwrap(); + let executable = native_fixture( + temp.path(), + "large-output", + r#"use std::io::{self, Write}; +fn main() { + let stdout = vec![b'o'; 2 * 1024 * 1024]; + let stderr = vec![b'e'; 2 * 1024 * 1024]; + let stdout_thread = std::thread::spawn(move || io::stdout().write_all(&stdout).unwrap()); + io::stderr().write_all(&stderr).unwrap(); + stdout_thread.join().unwrap(); +}"#, + ); + let output = getter_operations::app::ProcessCommandRunner + .run(&ResolvedInstallerCommand { + executable, + args: Vec::new(), + }) + .unwrap(); + + assert_eq!(output.status, 0); + assert_eq!(output.stdout.len(), CAPTURE_LIMIT); + assert_eq!(output.stderr.len(), CAPTURE_LIMIT); + assert!(output.stdout.iter().all(|byte| *byte == b'o')); + assert!(output.stderr.iter().all(|byte| *byte == b'e')); +} + +#[test] +fn command_not_found_is_stable_and_preserves_staged_file() { + let bytes = b"apk"; + let temp = fixture(r#"{executable="absent",args={}}"#, bytes); + let error = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/unused")), + &Runner::default(), + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.command_not_found", "{error:?}"); + assert!(temp + .path() + .join("downloads") + .read_dir() + .unwrap() + .next() + .is_some()); +} + +#[test] +fn runner_io_error_is_stable_and_observer_precedes_attempt() { + struct Ordered { + events: Rc>>, + } + impl CommandObserver for Ordered { + fn before_execute(&self, _: &ResolvedInstallerCommand) -> std::io::Result<()> { + self.events.borrow_mut().push("observed"); + Ok(()) + } + } + impl CommandRunner for Ordered { + fn run(&self, _: &ResolvedInstallerCommand) -> std::io::Result { + self.events.borrow_mut().push("ran"); + Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "fixture spawn failed", + )) + } + } + let bytes = b"apk"; + let temp = fixture(r#"{executable="fake-installer",args={}}"#, bytes); + let events = Rc::new(RefCell::new(Vec::new())); + let ordered = Ordered { + events: events.clone(), + }; + let error = install_app_with_dependencies_and_observer( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &ordered, + &ordered, + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.command_spawn_failed"); + assert_eq!(&*events.borrow(), &["observed", "ran"]); +} + +#[test] +fn nonzero_child_is_stable_and_observer_precedes_failure() { + struct Failed { + events: Rc>>, + } + impl CommandObserver for Failed { + fn before_execute(&self, _: &ResolvedInstallerCommand) -> std::io::Result<()> { + self.events.borrow_mut().push("observed"); + Ok(()) + } + } + impl CommandRunner for Failed { + fn run(&self, _: &ResolvedInstallerCommand) -> std::io::Result { + self.events.borrow_mut().push("ran"); + Ok(RunnerOutput { + status: 23, + stdout: Vec::new(), + stderr: b"failed".to_vec(), + }) + } + } + let bytes = b"apk"; + let temp = fixture(r#"{executable="fake-installer",args={}}"#, bytes); + let events = Rc::new(RefCell::new(Vec::new())); + let failed = Failed { + events: events.clone(), + }; + let error = install_app_with_dependencies_and_observer( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &failed, + &failed, + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.command_failed"); + assert_eq!(&*events.borrow(), &["observed", "ran"]); + assert!(temp + .path() + .join("downloads") + .read_dir() + .unwrap() + .next() + .is_some()); +} diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index 001eae5..362882d 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -92,6 +92,7 @@ impl FdroidRelease { sha256: self.sha256.clone(), size: self.size, }], + install: None, } } } @@ -405,6 +406,7 @@ fn github_release_update_candidate( channel: release.prerelease.then(|| "prerelease".to_owned()), source: Some("github".to_owned()), artifacts, + install: None, }) } @@ -513,6 +515,7 @@ mod tests { channel: Some("stable".to_owned()), source: Some("fixture".to_owned()), artifacts: Vec::new(), + install: None, }], }; From 989448975d891d5e5043ea0f42cf92e1fc1200a9 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Fri, 17 Jul 2026 14:37:47 +0800 Subject: [PATCH 70/72] feat(setup): onboard installed packages --- Cargo.lock | 415 ++++++++- crates/getter-cli/src/lib.rs | 82 +- crates/getter-cli/tests/bdd_cli.rs | 68 ++ .../features/cli/fresh_install_setup.feature | 15 + crates/getter-cli/tests/setup_cli.rs | 109 +++ crates/getter-operations/Cargo.toml | 12 + crates/getter-operations/src/autogen.rs | 820 ++++++++++++++++-- .../src/fdroid_signed_index.rs | 582 +++++++++++++ crates/getter-operations/src/lib.rs | 2 + crates/getter-operations/src/onboarding.rs | 595 +++++++++++++ crates/getter-operations/src/startup.rs | 12 +- .../tests/fdroid_signed_index.rs | 190 ++++ .../tests/fixtures/fdroid-official-index.jar | Bin 0 -> 3375358 bytes .../tests/fixtures/wrong-cert-index.jar | Bin 0 -> 2078 bytes crates/getter-operations/tests/onboarding.rs | 290 +++++++ crates/getter-operations/tests/startup.rs | 32 + crates/getter-storage/src/lib.rs | 95 +- 17 files changed, 3235 insertions(+), 84 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/fresh_install_setup.feature create mode 100644 crates/getter-cli/tests/setup_cli.rs create mode 100644 crates/getter-operations/src/fdroid_signed_index.rs create mode 100644 crates/getter-operations/src/onboarding.rs create mode 100644 crates/getter-operations/tests/fdroid_signed_index.rs create mode 100644 crates/getter-operations/tests/fixtures/fdroid-official-index.jar create mode 100644 crates/getter-operations/tests/fixtures/wrong-cert-index.jar create mode 100644 crates/getter-operations/tests/onboarding.rs diff --git a/Cargo.lock b/Cargo.lock index fe86385..9e8e34f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,18 +85,39 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "autocfg" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bitflags" version = "2.13.0" @@ -112,6 +133,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bstr" version = "1.12.1" @@ -122,6 +152,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + [[package]] name = "bytecount" version = "0.6.9" @@ -197,6 +233,24 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + +[[package]] +name = "cms" +version = "0.3.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986e7713348fbe5322df18082d73a0ad53a2946dbb7a02452cf3622b83b91e7c" +dependencies = [ + "const-oid", + "der", + "spki", + "x509-cert", +] + [[package]] name = "colorchoice" version = "1.0.5" @@ -225,6 +279,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "convert_case" version = "0.10.0" @@ -250,6 +310,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -259,6 +325,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -293,6 +368,20 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-bigint" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271" +dependencies = [ + "cpubits", + "ctutils", + "num-traits", + "rand_core", + "serdect", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.7" @@ -303,6 +392,34 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "crypto-primes" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3633a51a39c69ebbaa4feaa694bd83d241e4093901c84a0963b19d9bb3f0cf8f" +dependencies = [ + "crypto-bigint", + "rand_core", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "cucumber" version = "0.23.0" @@ -360,6 +477,41 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "der" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" +dependencies = [ + "const-oid", + "der_derive", + "flagset", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59600e2c2d636fde9b65e99cc6445ac770c63d3628195ff39932b8d6d7409903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -389,8 +541,30 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -451,6 +625,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "flagset" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" + [[package]] name = "flate2" version = "1.1.9" @@ -461,6 +641,16 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "futures" version = "0.3.32" @@ -619,7 +809,7 @@ dependencies = [ "mlua", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "tempfile", "thiserror 1.0.69", "toml", @@ -645,19 +835,31 @@ dependencies = [ name = "getter-operations" version = "0.1.0" dependencies = [ + "base64", + "cms", + "der", + "fs2", + "getrandom 0.4.3", "getter-core", "getter-providers", "getter-storage", "json_comments", "mlua", + "rsa", "rusqlite", "serde", "serde_json", - "sha2", + "sha2 0.10.9", + "sha2 0.11.0", + "signature", + "spki", + "subtle", "tempfile", "thiserror 1.0.69", "ureq", "which 8.0.3", + "x509-cert", + "zip", ] [[package]] @@ -790,6 +992,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +[[package]] +name = "hybrid-array" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +dependencies = [ + "typenum", +] + [[package]] name = "ignore" version = "0.4.26" @@ -1117,6 +1328,15 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1149,6 +1369,26 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkcs1" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.33" @@ -1179,6 +1419,12 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -1260,6 +1506,24 @@ dependencies = [ "memchr", ] +[[package]] +name = "rsa" +version = "0.10.0-rc.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b2aa4ba0d89f73d1e332df05be0eeab8840351c36ca5654341dfdb57bb3caf" +dependencies = [ + "const-oid", + "crypto-bigint", + "crypto-primes", + "digest 0.11.3", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "zeroize", +] + [[package]] name = "rusqlite" version = "0.32.1" @@ -1498,6 +1762,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serdect" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha2" version = "0.10.9" @@ -1505,8 +1779,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -1515,6 +1800,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" +dependencies = [ + "digest 0.11.3", + "rand_core", +] + [[package]] name = "simd-adler32" version = "0.3.9" @@ -1560,6 +1855,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "spki" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "strsim" version = "0.11.1" @@ -1690,6 +1995,27 @@ dependencies = [ "syn", ] +[[package]] +name = "tls_codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio" version = "1.52.3" @@ -1927,6 +2253,22 @@ dependencies = [ "libc", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -1936,6 +2278,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-link" version = "0.2.1" @@ -2105,6 +2453,18 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "x509-cert" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "105ef4642d9cb137ef83d623d0e4bf08b8adf69e9918ca904a174adb6d3d038b" +dependencies = [ + "const-oid", + "der", + "spki", + "tls_codec", +] + [[package]] name = "zerocopy" version = "0.8.52" @@ -2130,9 +2490,52 @@ name = "zeroize" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "flate2", + "indexmap", + "memchr", + "thiserror 2.0.18", + "zopfli", +] [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 8d949c7..ea30aa2 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -27,6 +27,7 @@ use getter_operations::github_autogen; use getter_operations::github_latest_commit::{self, GithubLatestCommitOperationError}; use getter_operations::github_releases::{self, GithubReleaseOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; +use getter_operations::onboarding::{self, SetupAcceptance, SetupPreview}; use getter_operations::runtime as runtime_operations; use getter_operations::startup as startup_operations; use getter_storage::legacy_room::{ @@ -58,6 +59,13 @@ pub enum CliCommand { Startup { inventory: Option, }, + SetupPreview { + inventory: PathBuf, + }, + SetupApply { + preview: PathBuf, + acceptance: SetupAcceptance, + }, AppList, AppShow { package_id: PackageId, @@ -524,6 +532,21 @@ where inventory: Some(PathBuf::from(inventory)), } } + [domain, command, flag, inventory] + if domain == "setup" && command == "preview" && flag == "--inventory" => + { + CliCommand::SetupPreview { + inventory: PathBuf::from(inventory), + } + } + [domain, command, flag, preview, rest @ ..] + if domain == "setup" && command == "apply" && flag == "--preview" => + { + CliCommand::SetupApply { + preview: PathBuf::from(preview), + acceptance: parse_setup_acceptance(rest)?, + } + } [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, [domain, command, package_id] if domain == "app" @@ -1046,6 +1069,33 @@ fn execute(invocation: CliInvocation, command_sink: &mut dyn Write) -> Result run_runtime_script(&invocation.data_dir, &script), + CliCommand::SetupPreview { inventory } => { + let db = open_main_db(&invocation.data_dir)?; + let cache = open_cache_db(&invocation.data_dir)?; + let inventory = read_installed_inventory(&inventory)?; + serde_json::to_value(onboarding::preview_setup_with_transport( + &invocation.data_dir, + &db, + &cache, + inventory, + &FixturelessSetupTransport, + )?) + .map_err(|error| CliError::Autogen(error.to_string())) + } + CliCommand::SetupApply { + preview, + acceptance, + } => { + let db = open_main_db(&invocation.data_dir)?; + let preview = read_setup_preview(&preview)?; + serde_json::to_value(onboarding::apply_setup_preview( + &invocation.data_dir, + &db, + &preview, + &acceptance, + )?) + .map_err(|error| CliError::Autogen(error.to_string())) + } CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; @@ -1742,6 +1792,13 @@ fn parse_provider_github_latest_commit_args( Ok(parsed) } +fn parse_setup_acceptance(args: &[String]) -> Result { + match parse_autogen_acceptance(args)? { + AutogenAcceptance::AcceptAll => Ok(SetupAcceptance::AcceptAll), + AutogenAcceptance::Accept(ids) => Ok(SetupAcceptance::Accept(ids)), + } +} + fn parse_autogen_acceptance(args: &[String]) -> Result { match args { [flag] if flag == "--accept-all" => Ok(AutogenAcceptance::AcceptAll), @@ -1890,6 +1947,27 @@ fn read_github_commit_fixture(path: &Path) -> Result { }) } +struct FixturelessSetupTransport; + +impl onboarding::FdroidCatalogTransport for FixturelessSetupTransport { + fn fetch_official_index_xml(&self) -> Result { + Err("CLI setup preview does not perform live network access".to_owned()) + } +} + +fn read_setup_preview(path: &Path) -> Result { + let bytes = fs::read(path) + .map_err(|error| CliError::Autogen(format!("failed to read setup preview: {error}")))?; + let value: Value = serde_json::from_slice(&bytes) + .map_err(|error| CliError::Autogen(format!("invalid setup preview: {error}")))?; + let payload = value + .get("data") + .filter(|_| value.get("ok").and_then(Value::as_bool) == Some(true)) + .unwrap_or(&value); + serde_json::from_value(payload.clone()) + .map_err(|error| CliError::Autogen(format!("invalid setup preview: {error}"))) +} + fn read_installed_inventory(path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; @@ -2432,7 +2510,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir ]|app list|repo list|repo add [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo [--releases ] [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir ]|setup preview --inventory |setup apply --preview (--accept-all|--accept ...)|app list|repo list|repo add [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo [--releases ] [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -2478,6 +2556,8 @@ impl CliCommand { match self { Self::Init => "init", Self::Startup { .. } => "startup", + Self::SetupPreview { .. } => "setup preview", + Self::SetupApply { .. } => "setup apply", Self::AppList => "app list", Self::AppShow { .. } => "app show", Self::AppCheck { .. } => "app check", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 6812b32..bc18451 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -1184,6 +1184,57 @@ fn run_getter_runtime_script(world: &mut CliWorld) { world.json = None; } +#[when("I run getter setup preview for that inventory")] +fn run_getter_setup_preview(world: &mut CliWorld) { + let inventory = world.inventory.as_ref().expect("inventory exists"); + let output = run_getter( + world, + [ + "setup".to_owned(), + "preview".to_owned(), + "--inventory".to_owned(), + inventory.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter setup apply for that preview with accept-all")] +fn run_getter_setup_apply_accept_all(world: &mut CliWorld) { + let preview = world + .autogen_preview + .as_ref() + .expect("setup preview exists"); + let output = run_getter( + world, + [ + "setup".to_owned(), + "apply".to_owned(), + "--preview".to_owned(), + preview.to_string_lossy().to_string(), + "--accept-all".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter startup for that inventory")] +fn run_getter_startup_for_inventory(world: &mut CliWorld) { + let inventory = world.inventory.as_ref().expect("inventory exists"); + let output = run_getter( + world, + [ + "startup".to_owned(), + "--inventory".to_owned(), + inventory.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen installed preview for that inventory")] fn run_getter_autogen_installed_preview(world: &mut CliWorld) { let inventory = world.inventory.as_ref().expect("inventory exists"); @@ -2015,6 +2066,23 @@ fn autogen_repository_has_not_been_written(world: &mut CliWorld) { ); } +#[then(expr = "setup state is {string}")] +fn setup_state_is(world: &mut CliWorld, state: String) { + assert_eq!(current_json(world)["data"]["setup"]["state"], state); +} + +#[then("I save the setup preview envelope to a file")] +fn save_setup_preview_envelope_to_file(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let preview = temp.path().join("setup-preview.json"); + fs::write( + &preview, + serde_json::to_vec_pretty(current_json(world)).expect("preview serializes"), + ) + .expect("write setup preview"); + world.autogen_preview = Some(preview); +} + #[then("I save the autogen preview to a file")] fn save_autogen_preview_to_file(world: &mut CliWorld) { let data = current_json(world) diff --git a/crates/getter-cli/tests/features/cli/fresh_install_setup.feature b/crates/getter-cli/tests/features/cli/fresh_install_setup.feature new file mode 100644 index 0000000..b2e052c --- /dev/null +++ b/crates/getter-cli/tests/features/cli/fresh_install_setup.feature @@ -0,0 +1,15 @@ +Feature: Fresh-install package setup + Scenario: User tracks an installed app through the unified setup flow + Given an initialized getter data directory + And an installed inventory with Android app "com.example.fallback" labeled "Fallback App" + When I run getter startup for that inventory + Then the command succeeds + And setup state is "needs_package_setup" + When I run getter setup preview for that inventory + Then the command succeeds + And I save the setup preview envelope to a file + When I run getter setup apply for that preview with accept-all + Then the command succeeds + When I run getter startup for that inventory + Then the command succeeds + And setup state is "ready" diff --git a/crates/getter-cli/tests/setup_cli.rs b/crates/getter-cli/tests/setup_cli.rs new file mode 100644 index 0000000..1042354 --- /dev/null +++ b/crates/getter-cli/tests/setup_cli.rs @@ -0,0 +1,109 @@ +use getter_cli::{run, ExitCode}; +use serde_json::{json, Value}; +use std::fs; + +#[test] +fn setup_preview_uses_product_safe_inventory_input_and_offline_fallback() { + let temp = tempfile::tempdir().unwrap(); + let inventory = temp.path().join("installed.json"); + fs::write(&inventory, json!({ + "format": "upgradeall-installed-inventory", "version": 1, + "items": [{"kind":"android_package","package_name":"com.example.app","label":"Example","version_name":"1.0","version_code":1}] + }).to_string()).unwrap(); + + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "setup", + "preview", + "--inventory", + inventory.to_str().unwrap(), + ]); + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let envelope: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(envelope["command"], "setup preview"); + assert_eq!( + envelope["data"]["candidates"][0]["category"], + "installed_fallback" + ); + assert!(envelope["data"]["preview_id"] + .as_str() + .is_some_and(|value| value.len() == 64)); +} + +#[test] +fn setup_preview_envelope_can_be_applied_without_manual_json_extraction() { + let temp = tempfile::tempdir().unwrap(); + let inventory = temp.path().join("installed.json"); + let preview = temp.path().join("preview.json"); + fs::write( + &inventory, + json!({ + "format": "upgradeall-installed-inventory", + "version": 1, + "items": [{ + "kind": "android_package", + "package_name": "com.example.app", + "label": "Example", + "version_name": "1.0", + "version_code": 1 + }] + }) + .to_string(), + ) + .unwrap(); + + let preview_output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "setup", + "preview", + "--inventory", + inventory.to_str().unwrap(), + ]); + assert_eq!(preview_output.exit_code, ExitCode::Success); + fs::write(&preview, &preview_output.stdout).unwrap(); + + let apply_output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "setup", + "apply", + "--preview", + preview.to_str().unwrap(), + "--accept-all", + ]); + assert_eq!( + apply_output.exit_code, + ExitCode::Success, + "{}", + apply_output.stdout + ); + let envelope: Value = serde_json::from_str(&apply_output.stdout).unwrap(); + assert_eq!( + envelope["data"]["applied_package_ids"][0], + "android/app/com.example.app" + ); + assert!(temp + .path() + .join("repo/autogen/android/app/com.example.app/.autogen.jsonc") + .is_file()); +} + +#[test] +fn setup_request_rejects_provider_controls() { + let temp = tempfile::tempdir().unwrap(); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "setup", + "preview", + "--endpoint", + "https://evil.invalid/index.xml", + ]); + assert_eq!(output.exit_code, ExitCode::Usage); +} diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 0ec16b8..520a9d6 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -10,17 +10,29 @@ lua-provider-host-dev = ["lua"] rustls-platform-verifier = ["ureq/platform-verifier"] [dependencies] +base64 = "0.22" +cms = "=0.3.0-pre.2" +der = "0.8" +fs2 = "0.4" json_comments = "0.2" getter-core = { path = "../getter-core", default-features = false } getter-providers = { path = "../getter-providers" } getter-storage = { path = "../getter-storage" } +getrandom = "0.4" mlua = { version = "0.10", features = ["luajit", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } +rsa = "0.10.0-rc" serde_json = "1" sha2 = "0.10" +sha2-11 = { package = "sha2", version = "0.11.0-rc" } +signature = "3.0.0-rc" +spki = "0.8" +subtle = "2.6" thiserror = "1" which = "=8.0.3" ureq = { version = "3.3.0", default-features = false, features = ["gzip", "rustls"] } +x509-cert = "0.3" +zip = { version = "2.4.2", default-features = false, features = ["deflate"] } [dev-dependencies] rusqlite = { version = "0.32", features = ["bundled"] } diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs index 770d3bf..826e45f 100644 --- a/crates/getter-operations/src/autogen.rs +++ b/crates/getter-operations/src/autogen.rs @@ -6,6 +6,7 @@ //! repository coverage, package-local `.autogen.jsonc` ownership, file writes, //! cleanup, and tracked state updates. +use fs2::FileExt; use getter_core::autogen::{ content_hash, content_hash_bytes, plan_installed_autogen, record_file_key, render_autogen_record, AutogenCandidate, AutogenPlan, AutogenRecord, AutogenSkipReason, @@ -19,9 +20,11 @@ use getter_core::repository::{ }; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_storage::{MainDb, StorageError}; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::collections::{BTreeMap, BTreeSet, HashMap}; -use std::fs; +use std::fs::{self, File}; +use std::io::Write; use std::path::{Component, Path, PathBuf}; #[derive(Debug, Clone, PartialEq, Eq)] @@ -60,6 +63,7 @@ pub fn build_installed_autogen_plan( db: &MainDb, inventory: &InstalledInventory, ) -> AutogenOperationResult { + recover_autogen_transactions(data_dir, db)?; let (target_alias, _target_path, target_priority) = generated_repository_config(data_dir)?; let covered = higher_priority_package_coverage(db, &target_alias, target_priority)?; let mut plan = plan_installed_autogen(inventory, &covered) @@ -106,6 +110,7 @@ pub fn cleanup_preview_json( db: &MainDb, inventory: &InstalledInventory, ) -> AutogenOperationResult { + recover_autogen_transactions(data_dir, db)?; let (target_alias, repo_path, _target_priority) = generated_repository_config(data_dir)?; if !repo_path.is_dir() { return Ok(cleanup_preview_response( @@ -172,6 +177,313 @@ pub fn cleanup_preview_json( )) } +pub(crate) struct AutogenApplySpec<'a> { + pub(crate) preview: &'a Value, + pub(crate) acceptance: &'a AutogenAcceptance, + pub(crate) expected_operation: &'static str, + pub(crate) expected_generator: &'static str, +} + +struct PreparedCandidate { + package_id: PackageId, + target_dir: PathBuf, + payload: PreviewCandidatePayload, +} + +const AUTOGEN_JOURNAL_FILE: &str = "journal.json"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +enum AutogenTransactionPhase { + Prepared, + BackupsDone, + SwapsDone, + DbCommitted, +} + +#[derive(Debug, Serialize, Deserialize)] +struct AutogenTransactionEntry { + package_id: String, + target: PathBuf, + backup: PathBuf, + staged: PathBuf, + existed: bool, +} + +#[derive(Debug, Serialize, Deserialize)] +struct AutogenTransactionJournal { + transaction_id: String, + phase: AutogenTransactionPhase, + package_ids: Vec, + entries: Vec, +} + +fn sync_directory(path: &Path) -> std::io::Result<()> { + File::open(path)?.sync_all() +} + +fn sync_tree(path: &Path) -> std::io::Result<()> { + for entry in fs::read_dir(path)? { + let entry = entry?; + if entry.file_type()?.is_dir() { + sync_tree(&entry.path())?; + } else { + File::open(entry.path())?.sync_all()?; + } + } + sync_directory(path) +} + +#[cfg(test)] +thread_local! { + static CRASH_AFTER_PHASE: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + +#[cfg(test)] +fn simulate_crash_after(phase: AutogenTransactionPhase) -> Result<(), String> { + let value = match phase { + AutogenTransactionPhase::BackupsDone => 1, + AutogenTransactionPhase::SwapsDone => 2, + AutogenTransactionPhase::DbCommitted => 3, + AutogenTransactionPhase::Prepared => 0, + }; + if CRASH_AFTER_PHASE.get() == value { + return Err(format!("simulated process crash after {phase:?}")); + } + Ok(()) +} + +#[cfg(not(test))] +fn simulate_crash_after(_phase: AutogenTransactionPhase) -> Result<(), String> { + Ok(()) +} + +fn write_journal( + transaction_dir: &Path, + journal: &AutogenTransactionJournal, +) -> Result<(), String> { + let temporary = transaction_dir.join("journal.json.tmp"); + let destination = transaction_dir.join(AUTOGEN_JOURNAL_FILE); + let bytes = serde_json::to_vec_pretty(journal) + .map_err(|error| format!("failed to serialize autogen transaction journal: {error}"))?; + let mut file = File::create(&temporary) + .map_err(|error| format!("failed to create autogen transaction journal: {error}"))?; + file.write_all(&bytes) + .and_then(|()| file.sync_all()) + .map_err(|error| format!("failed to persist autogen transaction journal: {error}"))?; + fs::rename(&temporary, &destination) + .map_err(|error| format!("failed to publish autogen transaction journal: {error}"))?; + sync_directory(transaction_dir) + .map_err(|error| format!("failed to sync autogen transaction directory: {error}")) +} + +fn rollback_journal(journal: &AutogenTransactionJournal) -> Result<(), String> { + let mut failures = Vec::new(); + for entry in journal.entries.iter().rev() { + if entry.backup.exists() { + if entry.target.exists() { + if let Err(error) = fs::remove_dir_all(&entry.target) { + failures.push(format!( + "remove replacement '{}': {error}", + entry.target.display() + )); + continue; + } + } + if let Err(error) = fs::rename(&entry.backup, &entry.target) { + failures.push(format!( + "restore backup '{}' to '{}': {error}", + entry.backup.display(), + entry.target.display() + )); + } + } else if !entry.existed && entry.target.exists() { + if let Err(error) = fs::remove_dir_all(&entry.target) { + failures.push(format!( + "remove new target '{}': {error}", + entry.target.display() + )); + } + } else if entry.existed && !entry.target.exists() { + failures.push(format!( + "original target '{}' is missing and backup '{}' is unavailable", + entry.target.display(), + entry.backup.display() + )); + } + } + if failures.is_empty() { + Ok(()) + } else { + Err(failures.join("; ")) + } +} + +struct AutogenApplyLock { + file: File, +} + +impl Drop for AutogenApplyLock { + fn drop(&mut self) { + let _ = self.file.unlock(); + } +} + +fn lock_autogen_apply(data_dir: &Path) -> AutogenOperationResult { + fs::create_dir_all(data_dir).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to access Getter data directory: {error}")) + })?; + let file = File::options() + .read(true) + .write(true) + .create(true) + .open(data_dir.join(".autogen-apply.lock")) + .map_err(|error| { + AutogenOperationError::Autogen(format!("failed to open autogen lock: {error}")) + })?; + file.try_lock_exclusive().map_err(|error| { + AutogenOperationError::Autogen(format!( + "another autogen apply or recovery is active: {error}" + )) + })?; + Ok(AutogenApplyLock { file }) +} + +fn path_is_symlink(path: &Path) -> bool { + fs::symlink_metadata(path).is_ok_and(|metadata| metadata.file_type().is_symlink()) +} + +fn journal_target_is_contained(repo_path: &Path, target: &Path) -> bool { + if path_is_symlink(repo_path) { + return false; + } + let Ok(relative) = target.strip_prefix(repo_path) else { + return false; + }; + if relative.as_os_str().is_empty() + || relative + .components() + .any(|component| !matches!(component, Component::Normal(_))) + { + return false; + } + let mut current = repo_path.to_path_buf(); + for component in relative.components() { + current.push(component.as_os_str()); + if current.exists() && path_is_symlink(¤t) { + return false; + } + } + true +} + +fn validate_autogen_journal( + repo_path: &Path, + transaction_dir: &Path, + journal: &AutogenTransactionJournal, +) -> AutogenOperationResult<()> { + if transaction_dir.file_name().and_then(|name| name.to_str()) + != Some(journal.transaction_id.as_str()) + { + return Err(AutogenOperationError::Autogen( + "autogen journal transaction id does not match its directory".into(), + )); + } + let staged_root = transaction_dir.join("staged"); + let backup_root = transaction_dir.join("backups"); + for (index, entry) in journal.entries.iter().enumerate() { + if !journal_target_is_contained(repo_path, &entry.target) + || entry.backup != backup_root.join(index.to_string()) + || entry.staged != staged_root.join(index.to_string()) + { + return Err(AutogenOperationError::Autogen(format!( + "autogen journal contains unsafe paths for package '{}'", + entry.package_id + ))); + } + } + Ok(()) +} + +pub fn recover_autogen_transactions(data_dir: &Path, db: &MainDb) -> AutogenOperationResult<()> { + let _lock = lock_autogen_apply(data_dir)?; + recover_autogen_transactions_locked(data_dir, db) +} + +fn recover_autogen_transactions_locked(data_dir: &Path, db: &MainDb) -> AutogenOperationResult<()> { + let (_, repo_path, _) = generated_repository_config(data_dir)?; + if path_is_symlink(&repo_path) { + return Err(AutogenOperationError::Autogen( + "configured generated repository must not be a symbolic link".into(), + )); + } + if !repo_path.is_dir() { + return Ok(()); + } + let entries = fs::read_dir(&repo_path).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to scan autogen transactions: {error}")) + })?; + for entry in entries { + let entry = entry.map_err(|error| AutogenOperationError::Autogen(error.to_string()))?; + let name = entry.file_name(); + if !name.to_string_lossy().starts_with(".autogen-transaction-") + || !entry + .file_type() + .map_err(|error| AutogenOperationError::Autogen(error.to_string()))? + .is_dir() + { + continue; + } + let transaction_dir = entry.path(); + if path_is_symlink(&transaction_dir) { + return Err(AutogenOperationError::Autogen(format!( + "autogen transaction directory must not be a symbolic link: '{}'", + transaction_dir.display() + ))); + } + let journal_path = transaction_dir.join(AUTOGEN_JOURNAL_FILE); + if !journal_path.is_file() { + return Err(AutogenOperationError::Autogen(format!( + "autogen transaction '{}' has no durable journal", + transaction_dir.display() + ))); + } + let journal: AutogenTransactionJournal = + serde_json::from_slice(&fs::read(&journal_path).map_err(|error| { + AutogenOperationError::Autogen(format!( + "failed to read '{}': {error}", + journal_path.display() + )) + })?) + .map_err(|error| { + AutogenOperationError::Autogen(format!( + "invalid autogen journal '{}': {error}", + journal_path.display() + )) + })?; + validate_autogen_journal(&repo_path, &transaction_dir, &journal)?; + let committed = db.has_autogen_transaction_commit(&journal.transaction_id)?; + if !committed { + rollback_journal(&journal).map_err(|error| { + AutogenOperationError::Autogen(format!( + "failed to roll back autogen transaction '{}': {error}; transaction retained at '{}'", + journal.transaction_id, transaction_dir.display() + )) + })?; + } + fs::remove_dir_all(&transaction_dir).map_err(|error| { + AutogenOperationError::Autogen(format!( + "failed to clean recovered autogen transaction '{}': {error}", + transaction_dir.display() + )) + })?; + if committed { + db.delete_autogen_transaction_commit(&journal.transaction_id)?; + } + } + Ok(()) +} + pub fn apply_installed_preview( data_dir: &Path, db: &MainDb, @@ -193,56 +505,293 @@ pub(crate) fn apply_preview( db: &MainDb, preview: &Value, acceptance: &AutogenAcceptance, - expected_operation: &str, - expected_generator: &str, + expected_operation: &'static str, + expected_generator: &'static str, ) -> AutogenOperationResult { - if preview.get("operation").and_then(Value::as_str) != Some(expected_operation) { - return Err(AutogenOperationError::Autogen(format!( - "autogen preview operation must be '{expected_operation}'" - ))); + apply_preview_batch( + data_dir, + db, + &[AutogenApplySpec { + preview, + acceptance, + expected_operation, + expected_generator, + }], + ) +} + +pub(crate) fn apply_preview_batch( + data_dir: &Path, + db: &MainDb, + specs: &[AutogenApplySpec<'_>], +) -> AutogenOperationResult { + let _lock = lock_autogen_apply(data_dir)?; + apply_preview_batch_locked(data_dir, db, specs) +} + +fn apply_preview_batch_locked( + data_dir: &Path, + db: &MainDb, + specs: &[AutogenApplySpec<'_>], +) -> AutogenOperationResult { + if specs.is_empty() { + return Err(AutogenOperationError::Autogen( + "autogen apply batch must contain at least one preview".to_owned(), + )); } + recover_autogen_transactions_locked(data_dir, db)?; let (target_alias, repo_path, target_priority) = generated_repository_config(data_dir)?; - if preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) { - return Err(AutogenOperationError::Autogen(format!( - "{expected_operation} target_repo_id must be '{}'", - target_alias.as_str() - ))); + if path_is_symlink(&repo_path) { + return Err(AutogenOperationError::Autogen( + "configured generated repository must not be a symbolic link".into(), + )); } - ensure_generated_repository(&repo_path, db, &target_alias, target_priority)?; - let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut applied = Vec::new(); + let mut prepared = Vec::new(); + let mut package_ids = BTreeSet::new(); + let mut target_paths = BTreeSet::new(); - for candidate in accepted { - let package_id = preview_package_id(candidate)?; - let relative_path = preview_relative_path(candidate)?; - let payload = - preview_candidate_payload(candidate, &package_id, &relative_path, expected_generator)?; - let target_dir = safe_join(&repo_path, &relative_path)?; - if target_dir.exists() { - read_owned_record(&target_dir, &package_id, &relative_path, expected_generator)?; - clear_directory_contents(&target_dir)?; - } else { - fs::create_dir_all(&target_dir).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to create generated package directory '{}': {source}", + for spec in specs { + if spec.preview.get("operation").and_then(Value::as_str) != Some(spec.expected_operation) { + return Err(AutogenOperationError::Autogen(format!( + "autogen preview operation must be '{}'", + spec.expected_operation + ))); + } + if spec.preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) + { + return Err(AutogenOperationError::Autogen(format!( + "{} target_repo_id must be '{}'", + spec.expected_operation, + target_alias.as_str() + ))); + } + for candidate in accepted_preview_candidates(spec.preview, spec.acceptance)? { + let package_id = preview_package_id(candidate)?; + let relative_path = preview_relative_path(candidate)?; + let payload = preview_candidate_payload( + candidate, + &package_id, + &relative_path, + spec.expected_generator, + )?; + let target_dir = safe_join(&repo_path, &relative_path)?; + if !package_ids.insert(package_id.to_string()) { + return Err(AutogenOperationError::Autogen(format!( + "duplicate accepted package id '{package_id}' in autogen apply batch" + ))); + } + if !target_paths.insert(target_dir.clone()) { + return Err(AutogenOperationError::Autogen(format!( + "duplicate accepted target path '{}' in autogen apply batch", target_dir.display() + ))); + } + if target_dir.exists() { + read_owned_record( + &target_dir, + &package_id, + &relative_path, + spec.expected_generator, + )?; + } + prepared.push(PreparedCandidate { + package_id, + target_dir, + payload, + }); + } + } + + let repo_existed = repo_path.exists(); + fs::create_dir_all(&repo_path).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create generated repository '{}': {source}", + repo_path.display() + )) + })?; + let transaction_dir = repo_path.join(format!( + ".autogen-transaction-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_nanos() + )); + let staged_root = transaction_dir.join("staged"); + let backup_root = transaction_dir.join("backups"); + fs::create_dir_all(&backup_root).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create autogen backup directory: {source}" + )) + })?; + for (index, candidate) in prepared.iter().enumerate() { + let staged = staged_root.join(index.to_string()); + fs::create_dir_all(&staged).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create autogen staging directory '{}': {source}", + staged.display() + )) + })?; + write_generated_package( + &staged, + &candidate.payload.files, + &candidate.payload.record_content, + )?; + } + sync_tree(&transaction_dir).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to sync staged autogen transaction: {source}" + )) + })?; + let transaction_id = transaction_dir + .file_name() + .unwrap() + .to_string_lossy() + .into_owned(); + let mut journal = AutogenTransactionJournal { + transaction_id: transaction_id.clone(), + phase: AutogenTransactionPhase::Prepared, + package_ids: prepared + .iter() + .map(|candidate| candidate.package_id.to_string()) + .collect(), + entries: prepared + .iter() + .enumerate() + .map(|(index, candidate)| AutogenTransactionEntry { + package_id: candidate.package_id.to_string(), + target: candidate.target_dir.clone(), + backup: backup_root.join(index.to_string()), + staged: staged_root.join(index.to_string()), + existed: candidate.target_dir.exists(), + }) + .collect(), + }; + write_journal(&transaction_dir, &journal).map_err(AutogenOperationError::Autogen)?; + sync_directory(&repo_path).map_err(|error| { + AutogenOperationError::Autogen(format!( + "failed to persist autogen transaction directory entry: {error}" + )) + })?; + + let result_package_ids = journal.package_ids.clone(); + let committed_result = || { + json!({ + "operation": "autogen.apply", + "target_repo_id": target_alias.as_str(), + "accepted_package_ids": result_package_ids, + "applied": result_package_ids.iter().map(|id| json!({ "package_id": id })).collect::>(), + "written": result_package_ids.len(), + "applied_count": result_package_ids.len(), + }) + }; + let apply_result = (|| -> AutogenOperationResult { + for entry in &journal.entries { + if let Some(parent) = entry.target.parent() { + fs::create_dir_all(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create generated package parent: {source}" + )) + })?; + } + if entry.existed { + fs::rename(&entry.target, &entry.backup).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to retain generated package backup '{}': {source}", + entry.target.display() + )) + })?; + sync_directory(&backup_root).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to sync autogen backup directory: {source}" + )) + })?; + if let Some(parent) = entry.target.parent() { + sync_directory(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to sync generated package parent: {source}" + )) + })?; + } + } + } + journal.phase = AutogenTransactionPhase::BackupsDone; + write_journal(&transaction_dir, &journal).map_err(AutogenOperationError::Autogen)?; + simulate_crash_after(journal.phase).map_err(AutogenOperationError::Autogen)?; + + for entry in &journal.entries { + fs::rename(&entry.staged, &entry.target).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to atomically replace generated package '{}': {source}", + entry.target.display() )) })?; + if let Some(parent) = entry.target.parent() { + sync_directory(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to sync generated package swap: {source}" + )) + })?; + } } - write_generated_package(&target_dir, &payload.files, &payload.record_content)?; - db.upsert_generated_tracked_package_preserving_user_state(&package_id, &target_alias)?; - applied.push(json!({ - "package_id": package_id.to_string(), - "output_relative_path": relative_path, - })); + journal.phase = AutogenTransactionPhase::SwapsDone; + write_journal(&transaction_dir, &journal).map_err(AutogenOperationError::Autogen)?; + simulate_crash_after(journal.phase).map_err(AutogenOperationError::Autogen)?; + + let metadata = RepositoryMetadata { + id: target_alias.clone(), + name: DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned(), + priority: target_priority, + api_version: REPO_API_VERSION_V1.to_owned(), + }; + let ids = prepared + .iter() + .map(|candidate| candidate.package_id.clone()) + .collect::>(); + db.apply_generated_repository_batch_with_marker( + &metadata, + &repo_path, + &ids, + Some(&transaction_id), + )?; + journal.phase = AutogenTransactionPhase::DbCommitted; + write_journal(&transaction_dir, &journal).map_err(AutogenOperationError::Autogen)?; + simulate_crash_after(journal.phase).map_err(AutogenOperationError::Autogen)?; + Ok(committed_result()) + })(); + + if let Err(error) = apply_result { + if error.to_string().contains("simulated process crash") { + return Err(error); + } + if db.has_autogen_transaction_commit(&transaction_id)? { + // The database is the commit point. Leave journal/marker recovery + // artifacts for the next startup rather than reporting a false failure. + return Ok(committed_result()); + } + if let Err(rollback_error) = rollback_journal(&journal) { + return Err(AutogenOperationError::Autogen(format!( + "{error}; rollback also failed: {rollback_error}; transaction retained at '{}'", + transaction_dir.display() + ))); + } + fs::remove_dir_all(&transaction_dir).map_err(|cleanup_error| { + AutogenOperationError::Autogen(format!( + "{error}; rollback succeeded but cleanup failed: {cleanup_error}" + )) + })?; + if !repo_existed { + let _ = fs::remove_dir(&repo_path); + } + return Err(error); } - Ok(json!({ - "target_repo_id": target_alias.as_str(), - "target_repo_path": repo_path, - "applied_count": applied.len(), - "applied": applied, - })) + if fs::remove_dir_all(&transaction_dir).is_ok() { + // A stale commit marker is harmless and recoverable; cleanup failure + // must not turn an already committed apply into a reported failure. + let _ = db.delete_autogen_transaction_commit(&transaction_id); + } + apply_result } pub fn apply_cleanup_preview( @@ -251,6 +800,8 @@ pub fn apply_cleanup_preview( preview: &Value, acceptance: &AutogenAcceptance, ) -> AutogenOperationResult { + let _lock = lock_autogen_apply(data_dir)?; + recover_autogen_transactions_locked(data_dir, db)?; let (target_alias, repo_path, _target_priority) = generated_repository_config(data_dir)?; if preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) { return Err(AutogenOperationError::Autogen(format!( @@ -442,6 +993,18 @@ fn accepted_preview_candidates<'a>( AutogenAcceptance::AcceptAll => Ok(candidates.iter().collect()), AutogenAcceptance::Accept(ids) => { let accepted: BTreeSet = ids.iter().map(ToString::to_string).collect(); + let available: BTreeSet<&str> = candidates + .iter() + .filter_map(|candidate| candidate.get("package_id").and_then(Value::as_str)) + .collect(); + if let Some(unknown) = accepted + .iter() + .find(|package_id| !available.contains(package_id.as_str())) + { + return Err(AutogenOperationError::Autogen(format!( + "accepted package id '{unknown}' is not present in the autogen preview" + ))); + } Ok(candidates .iter() .filter(|candidate| { @@ -580,39 +1143,6 @@ fn preview_generated_files(candidate: &Value) -> AutogenOperationResult AutogenOperationResult<()> { - fs::create_dir_all(repo_path).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to create generated repository '{}': {source}", - repo_path.display() - )) - })?; - db.upsert_repository( - &RepositoryMetadata { - id: alias.clone(), - name: generated_repository_name(alias), - priority, - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(repo_path), - None, - )?; - Ok(()) -} - -fn generated_repository_name(alias: &RepositoryId) -> String { - if alias.as_str() == DEFAULT_AUTOGEN_REPOSITORY_ID { - DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned() - } else { - alias.to_string() - } -} - struct LoadedAutogenRecord { content_hash: String, } @@ -947,6 +1477,150 @@ mod tests { assert_eq!(repos[0].priority.value(), -1); } + #[test] + fn batch_preflight_rejects_later_invalid_spec_without_mutation() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + let mut invalid_preview = preview.clone(); + invalid_preview["candidates"][0]["relative_path"] = json!("../escape"); + let acceptance = AutogenAcceptance::AcceptAll; + + let error = apply_preview_batch( + data_dir, + &db, + &[ + AutogenApplySpec { + preview: &preview, + acceptance: &acceptance, + expected_operation: "installed.preview", + expected_generator: INSTALLED_AUTOGEN_GENERATOR, + }, + AutogenApplySpec { + preview: &invalid_preview, + acceptance: &acceptance, + expected_operation: "installed.preview", + expected_generator: INSTALLED_AUTOGEN_GENERATOR, + }, + ], + ) + .unwrap_err(); + + assert!(matches!(error, AutogenOperationError::Autogen(_))); + assert!(!data_dir.join("repo/autogen").exists()); + assert!(db.repositories().unwrap().is_empty()); + assert!(db.tracked_packages().unwrap().is_empty()); + } + + #[test] + fn crash_phases_recover_old_or_committed_state() { + for (crash_phase, expect_new) in [(1, false), (2, false), (3, true)] { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db_path = data_dir.join("main.db"); + let db = MainDb::open(&db_path).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll) + .unwrap(); + let package_dir = data_dir.join("repo/autogen/android/app/com.example.autogen"); + let old_lua = fs::read(package_dir.join("9999.lua")).unwrap(); + + let changed_inventory = InstalledInventory::new(vec![ + getter_core::autogen::InstalledInventoryItem::AndroidPackage { + package_name: "com.example.autogen".to_owned(), + label: Some("Changed label".to_owned()), + version_name: Some("999".to_owned()), + version_code: Some(999), + }, + ]); + let changed_plan = + build_installed_autogen_plan(data_dir, &db, &changed_inventory).unwrap(); + let changed_preview = installed_preview_json(data_dir, &changed_plan).unwrap(); + CRASH_AFTER_PHASE.set(crash_phase); + let error = apply_installed_preview( + data_dir, + &db, + &changed_preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap_err(); + CRASH_AFTER_PHASE.set(0); + assert!(error.to_string().contains("simulated process crash")); + drop(db); + + let reopened = MainDb::open(&db_path).unwrap(); + recover_autogen_transactions(data_dir, &reopened).unwrap(); + if expect_new { + let metadata = fs::read_to_string(package_dir.join("metadata.jsonc")).unwrap(); + assert!(metadata.contains("Changed label")); + } else { + assert_eq!(fs::read(package_dir.join("9999.lua")).unwrap(), old_lua); + } + assert_eq!(reopened.repositories().unwrap().len(), 1); + assert_eq!(reopened.tracked_packages().unwrap().len(), 1); + assert!(!fs::read_dir(data_dir.join("repo/autogen")) + .unwrap() + .any(|entry| entry + .unwrap() + .file_name() + .to_string_lossy() + .starts_with(".autogen-transaction-"))); + } + } + + #[test] + fn database_failure_after_swaps_restores_previous_package_bytes() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db_path = data_dir.join("main.db"); + let db = MainDb::open(&db_path).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll).unwrap(); + let package_dir = data_dir.join("repo/autogen/android/app/com.example.autogen"); + let old_lua = fs::read(package_dir.join("9999.lua")).unwrap(); + + let changed_inventory = InstalledInventory::new(vec![ + getter_core::autogen::InstalledInventoryItem::AndroidPackage { + package_name: "com.example.autogen".to_owned(), + label: Some("Changed label".to_owned()), + version_name: Some("999".to_owned()), + version_code: Some(999), + }, + ]); + let changed_plan = build_installed_autogen_plan(data_dir, &db, &changed_inventory).unwrap(); + let changed_preview = installed_preview_json(data_dir, &changed_plan).unwrap(); + let injector = rusqlite::Connection::open(&db_path).unwrap(); + injector + .execute_batch( + "CREATE TRIGGER fail_generated_tracking BEFORE INSERT ON tracked_packages BEGIN SELECT RAISE(ABORT, 'injected'); END;", + ) + .unwrap(); + + let error = apply_installed_preview( + data_dir, + &db, + &changed_preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap_err(); + + assert!(matches!(error, AutogenOperationError::Storage(_))); + assert_eq!(fs::read(package_dir.join("9999.lua")).unwrap(), old_lua); + assert!(!data_dir + .join("repo/autogen") + .read_dir() + .unwrap() + .any(|entry| entry + .unwrap() + .file_name() + .to_string_lossy() + .starts_with(".autogen-transaction-"))); + } + #[test] fn apply_rejects_existing_package_directory_without_ownership_record() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-operations/src/fdroid_signed_index.rs b/crates/getter-operations/src/fdroid_signed_index.rs new file mode 100644 index 0000000..b8c4b32 --- /dev/null +++ b/crates/getter-operations/src/fdroid_signed_index.rs @@ -0,0 +1,582 @@ +//! Verification of the official F-Droid v1 signed `index.jar`. + +use std::collections::{HashMap, HashSet}; +use std::io::{Cursor, Read}; + +use base64::Engine as _; +use cms::cert::CertificateChoices; +use cms::content_info::ContentInfo; +use cms::signed_data::{SignedData, SignerIdentifier}; +use der::asn1::{ObjectIdentifier, OctetString}; +use der::{Decode, Encode}; +use rsa::pkcs1v15::{Signature as RsaSignature, VerifyingKey}; +use rsa::RsaPublicKey; +use sha2::{Digest, Sha256}; +use sha2_11::Sha256 as RsaSha256; +use signature::Verifier; +use spki::DecodePublicKey; +use subtle::ConstantTimeEq; +use thiserror::Error; +use zip::ZipArchive; + +pub const OFFICIAL_INDEX_JAR_URL: &str = "https://f-droid.org/repo/index.jar"; +pub const MAX_ARCHIVE_BYTES: usize = 64 * 1024 * 1024; +const MAX_ENTRY_BYTES: u64 = 48 * 1024 * 1024; +const MAX_TOTAL_UNCOMPRESSED_BYTES: u64 = 52 * 1024 * 1024; +const MAX_MANIFEST_BYTES: u64 = 64 * 1024; +const MAX_SIGNATURE_FILE_BYTES: u64 = 64 * 1024; +const MAX_SIGNATURE_BLOCK_BYTES: u64 = 256 * 1024; +const MAX_ENTRIES: usize = 4; +const OFFICIAL_CERT_SHA256: [u8; 32] = [ + 0x43, 0x23, 0x8d, 0x51, 0x2c, 0x1e, 0x5e, 0xb2, 0xd6, 0x56, 0x9f, 0x4a, 0x3a, 0xfb, 0xf5, 0x52, + 0x34, 0x18, 0xb8, 0x2e, 0x0a, 0x3e, 0xd1, 0x55, 0x27, 0x70, 0xab, 0xb9, 0xa9, 0xc9, 0xcc, 0xab, +]; + +#[derive(Debug, Error)] +pub enum VerifyError { + #[error("F-Droid index.jar exceeds the {MAX_ARCHIVE_BYTES}-byte limit")] + ArchiveTooLarge, + #[error("invalid ZIP archive: {0}")] + InvalidZip(String), + #[error("unsafe ZIP entry name: {0}")] + UnsafeEntry(String), + #[error("duplicate ZIP entry: {0}")] + DuplicateEntry(String), + #[error("unexpected ZIP entry: {0}")] + UnexpectedEntry(String), + #[error("missing required ZIP entry: {0}")] + MissingEntry(&'static str), + #[error("ZIP entry exceeds its size limit: {0}")] + EntryTooLarge(String), + #[error("invalid JAR manifest: {0}")] + InvalidManifest(String), + #[error("invalid PKCS#7 signature block: {0}")] + InvalidSignature(String), + #[error("JAR v1 signature was not verified")] + SignatureNotVerified, + #[error("the signing certificate is not the pinned official F-Droid certificate")] + WrongCertificate, + #[error("digest mismatch for {0}")] + DigestMismatch(&'static str), + #[error("index.xml is not UTF-8")] + InvalidXmlUtf8, +} + +struct JarParts { + xml: Vec, + manifest: Vec, + sf: Vec, + rsa: Vec, +} + +/// Verifies an already bounded official F-Droid `index.jar` and returns its XML. +pub fn verify_fdroid_index_jar(jar: &[u8]) -> Result { + if jar.len() > MAX_ARCHIVE_BYTES { + return Err(VerifyError::ArchiveTooLarge); + } + let parts = read_strict_jar(jar)?; + + verify_cms_signature(&parts.rsa, &parts.sf)?; + + let sf = ParsedManifest::parse(&parts.sf)?; + sf.require_exact("Signature-Version", "1.0")?; + sf.require_sha256("SHA-256-Digest-Manifest", &parts.manifest, "MANIFEST.MF")?; + let manifest_section = find_named_section_bytes(&parts.manifest, "index.xml")?; + sf.section("index.xml")?.require_sha256( + "SHA-256-Digest", + manifest_section, + "index.xml manifest section", + )?; + + let manifest = ParsedManifest::parse(&parts.manifest)?; + manifest.require_exact("Manifest-Version", "1.0")?; + manifest + .section("index.xml")? + .require_sha256("SHA-256-Digest", &parts.xml, "index.xml")?; + + String::from_utf8(parts.xml).map_err(|_| VerifyError::InvalidXmlUtf8) +} + +fn read_strict_jar(jar: &[u8]) -> Result { + let declared_entries = eocd_entry_count(jar)?; + let mut archive = ZipArchive::new(Cursor::new(jar)) + .map_err(|error| VerifyError::InvalidZip(error.to_string()))?; + if declared_entries != archive.len() { + return Err(VerifyError::DuplicateEntry( + "central directory contains duplicate names".to_owned(), + )); + } + let mut seen = HashSet::new(); + let mut total = 0_u64; + let mut values = HashMap::new(); + let mut signer_stem: Option = None; + for index in 0..archive.len() { + let entry = archive + .by_index(index) + .map_err(|error| VerifyError::InvalidZip(error.to_string()))?; + let name = entry.name().to_owned(); + if !safe_entry_name(&name) { + return Err(VerifyError::UnsafeEntry(name)); + } + if !seen.insert(name.clone()) { + return Err(VerifyError::DuplicateEntry(name)); + } + let kind_limit = match name.as_str() { + "index.xml" => MAX_ENTRY_BYTES, + "META-INF/MANIFEST.MF" => MAX_MANIFEST_BYTES, + _ if signature_name(&name, ".SF").is_some() => MAX_SIGNATURE_FILE_BYTES, + _ if signature_name(&name, ".RSA").is_some() => MAX_SIGNATURE_BLOCK_BYTES, + _ => return Err(VerifyError::UnexpectedEntry(name)), + }; + if entry.is_dir() || entry.size() > kind_limit { + return Err(VerifyError::EntryTooLarge(name)); + } + total = total + .checked_add(entry.size()) + .ok_or_else(|| VerifyError::EntryTooLarge(name.clone()))?; + if total > MAX_TOTAL_UNCOMPRESSED_BYTES { + return Err(VerifyError::EntryTooLarge(name)); + } + if let Some(stem) = signature_name(&name, ".SF").or_else(|| signature_name(&name, ".RSA")) { + match &signer_stem { + Some(existing) if existing != stem => { + return Err(VerifyError::UnexpectedEntry(name)); + } + None => signer_stem = Some(stem.to_owned()), + _ => {} + } + } + let capacity = + usize::try_from(entry.size()).map_err(|_| VerifyError::EntryTooLarge(name.clone()))?; + let mut bytes = Vec::with_capacity(capacity); + entry + .take(kind_limit + 1) + .read_to_end(&mut bytes) + .map_err(|error| VerifyError::InvalidZip(error.to_string()))?; + if bytes.len() as u64 > kind_limit { + return Err(VerifyError::EntryTooLarge(name)); + } + values.insert(name, bytes); + } + + if seen.len() != MAX_ENTRIES { + return Err(VerifyError::UnexpectedEntry(format!( + "archive has {} entries", + seen.len() + ))); + } + let stem = signer_stem.ok_or(VerifyError::MissingEntry("META-INF/.SF/.RSA"))?; + Ok(JarParts { + xml: take_required(&mut values, "index.xml")?, + manifest: take_required(&mut values, "META-INF/MANIFEST.MF")?, + sf: take_required(&mut values, &format!("META-INF/{stem}.SF"))?, + rsa: take_required(&mut values, &format!("META-INF/{stem}.RSA"))?, + }) +} + +fn eocd_entry_count(jar: &[u8]) -> Result { + const EOCD_SIGNATURE: &[u8; 4] = b"PK\x05\x06"; + const MIN_EOCD_SIZE: usize = 22; + let search_start = jar.len().saturating_sub(65_535 + MIN_EOCD_SIZE); + let relative = jar[search_start..] + .windows(EOCD_SIGNATURE.len()) + .rposition(|window| window == EOCD_SIGNATURE) + .ok_or_else(|| { + VerifyError::InvalidZip("missing end-of-central-directory record".to_owned()) + })?; + let offset = search_start + relative; + let eocd = jar.get(offset..offset + MIN_EOCD_SIZE).ok_or_else(|| { + VerifyError::InvalidZip("truncated end-of-central-directory record".to_owned()) + })?; + let disk_entries = u16::from_le_bytes([eocd[8], eocd[9]]); + let total_entries = u16::from_le_bytes([eocd[10], eocd[11]]); + let comment_len = usize::from(u16::from_le_bytes([eocd[20], eocd[21]])); + if offset + MIN_EOCD_SIZE + comment_len != jar.len() { + return Err(VerifyError::InvalidZip( + "trailing bytes or malformed ZIP comment".to_owned(), + )); + } + if disk_entries == u16::MAX || total_entries == u16::MAX || disk_entries != total_entries { + return Err(VerifyError::InvalidZip( + "multi-disk and ZIP64 archives are not accepted".to_owned(), + )); + } + Ok(usize::from(total_entries)) +} + +fn take_required( + values: &mut HashMap>, + name: &str, +) -> Result, VerifyError> { + values + .remove(name) + .ok_or(VerifyError::MissingEntry(match name { + "index.xml" => "index.xml", + "META-INF/MANIFEST.MF" => "META-INF/MANIFEST.MF", + _ if name.ends_with(".SF") => "META-INF/.SF", + _ => "META-INF/.RSA", + })) +} + +fn safe_entry_name(name: &str) -> bool { + !name.is_empty() + && !name.starts_with('/') + && !name.starts_with('\\') + && !name.contains('\\') + && !name.contains('\0') + && !name + .split('/') + .any(|part| part.is_empty() || part == "." || part == "..") + && !name.as_bytes().get(1).is_some_and(|byte| *byte == b':') +} + +fn signature_name<'a>(name: &'a str, suffix: &str) -> Option<&'a str> { + let stem = name.strip_prefix("META-INF/")?.strip_suffix(suffix)?; + (!stem.is_empty() + && stem + .bytes() + .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-'))) + .then_some(stem) +} + +#[derive(Debug)] +struct ParsedManifest { + main: Attributes, + named: HashMap, +} + +type Attributes = HashMap; + +impl ParsedManifest { + fn parse(bytes: &[u8]) -> Result { + let sections = split_sections(bytes)?; + let mut iter = sections.into_iter(); + let main = parse_attributes(iter.next().ok_or_else(|| { + VerifyError::InvalidManifest("manifest has no main section".to_owned()) + })?)?; + let mut named = HashMap::new(); + for section in iter { + let mut attributes = parse_attributes(section)?; + let name = attributes.remove("Name").ok_or_else(|| { + VerifyError::InvalidManifest("named section has no Name attribute".to_owned()) + })?; + if named.insert(name.clone(), attributes).is_some() { + return Err(VerifyError::InvalidManifest(format!( + "duplicate section for {name}" + ))); + } + } + Ok(Self { main, named }) + } + + fn section(&self, name: &str) -> Result<&Attributes, VerifyError> { + self.named + .get(name) + .ok_or_else(|| VerifyError::InvalidManifest(format!("missing section for {name}"))) + } + + fn require_sha256( + &self, + key: &str, + bytes: &[u8], + subject: &'static str, + ) -> Result<(), VerifyError> { + self.main.require_sha256(key, bytes, subject) + } + + fn require_exact(&self, key: &str, expected: &str) -> Result<(), VerifyError> { + if self.main.get(key).is_some_and(|value| value == expected) { + Ok(()) + } else { + Err(VerifyError::InvalidManifest(format!( + "missing or unsupported {key}" + ))) + } + } +} + +trait DigestAttribute { + fn require_sha256( + &self, + key: &str, + bytes: &[u8], + subject: &'static str, + ) -> Result<(), VerifyError>; +} + +impl DigestAttribute for Attributes { + fn require_sha256( + &self, + key: &str, + bytes: &[u8], + subject: &'static str, + ) -> Result<(), VerifyError> { + let encoded = self + .get(key) + .ok_or_else(|| VerifyError::InvalidManifest(format!("missing {key}")))?; + let expected = base64::engine::general_purpose::STANDARD + .decode(encoded) + .map_err(|_| VerifyError::InvalidManifest(format!("invalid base64 in {key}")))?; + if expected.len() != 32 { + return Err(VerifyError::InvalidManifest(format!( + "{key} is not a SHA-256 digest" + ))); + } + let actual = Sha256::digest(bytes); + if !bool::from(expected.as_slice().ct_eq(actual.as_slice())) { + return Err(VerifyError::DigestMismatch(subject)); + } + Ok(()) + } +} + +fn split_sections(bytes: &[u8]) -> Result, VerifyError> { + if bytes.is_empty() { + return Err(VerifyError::InvalidManifest("empty manifest".to_owned())); + } + let mut sections = Vec::new(); + let mut start = 0; + let mut position = 0; + while position < bytes.len() { + let line_end = bytes[position..] + .iter() + .position(|byte| *byte == b'\n') + .map(|relative| position + relative + 1) + .unwrap_or(bytes.len()); + let line = &bytes[position..line_end]; + let content = line + .strip_suffix(b"\r\n") + .or_else(|| line.strip_suffix(b"\n")) + .unwrap_or(line); + if content.is_empty() { + sections.push(&bytes[start..line_end]); + start = line_end; + } + position = line_end; + } + if start < bytes.len() { + sections.push(&bytes[start..]); + } + Ok(sections) +} + +fn parse_attributes(section: &[u8]) -> Result { + let text = std::str::from_utf8(section) + .map_err(|_| VerifyError::InvalidManifest("attributes are not UTF-8".to_owned()))?; + let mut unfolded: Vec = Vec::new(); + for raw in text.split_terminator('\n') { + let line = raw.strip_suffix('\r').unwrap_or(raw); + if line.is_empty() { + continue; + } + if let Some(continuation) = line.strip_prefix(' ') { + let previous = unfolded.last_mut().ok_or_else(|| { + VerifyError::InvalidManifest("orphan continuation line".to_owned()) + })?; + previous.push_str(continuation); + } else { + unfolded.push(line.to_owned()); + } + } + let mut attributes = HashMap::new(); + for line in unfolded { + let (key, value) = line + .split_once(": ") + .ok_or_else(|| VerifyError::InvalidManifest(format!("malformed attribute: {line}")))?; + if key.is_empty() + || attributes + .insert(key.to_owned(), value.to_owned()) + .is_some() + { + return Err(VerifyError::InvalidManifest(format!( + "empty or duplicate attribute: {key}" + ))); + } + } + Ok(attributes) +} + +fn find_named_section_bytes<'a>(manifest: &'a [u8], wanted: &str) -> Result<&'a [u8], VerifyError> { + let mut found = None; + for section in split_sections(manifest)?.into_iter().skip(1) { + let attributes = parse_attributes(section)?; + if attributes.get("Name").is_some_and(|name| name == wanted) + && found.replace(section).is_some() + { + return Err(VerifyError::InvalidManifest(format!( + "duplicate section for {wanted}" + ))); + } + } + found.ok_or_else(|| VerifyError::InvalidManifest(format!("missing section for {wanted}"))) +} + +fn verify_cms_signature(signature_block: &[u8], signature_file: &[u8]) -> Result<(), VerifyError> { + const SIGNED_DATA: &str = "1.2.840.113549.1.7.2"; + const DATA: &str = "1.2.840.113549.1.7.1"; + const SHA256: &str = "2.16.840.1.101.3.4.2.1"; + const RSA_ENCRYPTION: &str = "1.2.840.113549.1.1.1"; + const SHA256_WITH_RSA: &str = "1.2.840.113549.1.1.11"; + const CONTENT_TYPE: &str = "1.2.840.113549.1.9.3"; + const MESSAGE_DIGEST: &str = "1.2.840.113549.1.9.4"; + + let content_info = ContentInfo::from_der(signature_block).map_err(invalid_der)?; + if content_info.content_type != oid(SIGNED_DATA)? { + return invalid_signature("CMS ContentInfo is not SignedData"); + } + let signed_data_der = content_info.content.to_der().map_err(invalid_der)?; + let signed_data = SignedData::from_der(&signed_data_der).map_err(invalid_der)?; + if signed_data.signer_infos.0.len() != 1 { + return invalid_signature("CMS SignedData must contain exactly one signer"); + } + let signer = signed_data + .signer_infos + .0 + .get(0) + .ok_or(VerifyError::SignatureNotVerified)?; + let signature_oid = signer.signature_algorithm.oid; + if signer.digest_alg.oid != oid(SHA256)? + || (signature_oid != oid(RSA_ENCRYPTION)? && signature_oid != oid(SHA256_WITH_RSA)?) + { + return invalid_signature("unsupported CMS signature algorithm; expected SHA256withRSA"); + } + + let certificates = signed_data.certificates.as_ref().ok_or_else(|| { + VerifyError::InvalidSignature("signature block has no certificate".into()) + })?; + let matching: Vec<_> = certificates + .0 + .iter() + .filter_map(|choice| match choice { + CertificateChoices::Certificate(cert) if signer_matches_certificate(signer, cert) => { + Some(cert) + } + _ => None, + }) + .collect(); + if matching.len() != 1 { + return invalid_signature("CMS signer must match exactly one certificate"); + } + let certificate = matching[0]; + let certificate_der = certificate.to_der().map_err(invalid_der)?; + if !bool::from( + Sha256::digest(&certificate_der) + .as_slice() + .ct_eq(&OFFICIAL_CERT_SHA256), + ) { + return Err(VerifyError::WrongCertificate); + } + + let signed_content = if let Some(attrs) = signer.signed_attrs.as_ref() { + let content_type_der = single_attribute_value(attrs, CONTENT_TYPE)? + .to_der() + .map_err(invalid_der)?; + if ObjectIdentifier::from_der(&content_type_der).map_err(invalid_der)? != oid(DATA)? { + return Err(VerifyError::SignatureNotVerified); + } + let message_digest_der = single_attribute_value(attrs, MESSAGE_DIGEST)? + .to_der() + .map_err(invalid_der)?; + let message_digest = OctetString::from_der(&message_digest_der).map_err(invalid_der)?; + if !bool::from( + Sha256::digest(signature_file) + .as_slice() + .ct_eq(message_digest.as_bytes()), + ) { + return Err(VerifyError::SignatureNotVerified); + } + + // RFC 5652 section 5.4 signs the DER SET OF SignedAttrs, not the + // context-specific [0] IMPLICIT encoding used inside SignerInfo. + attrs.to_der().map_err(invalid_der)? + } else { + // The official F-Droid JAR signature is a detached CMS signature + // without signed attributes, so its signature covers the exact .SF. + signature_file.to_vec() + }; + + let public_key_der = certificate + .tbs_certificate() + .subject_public_key_info() + .to_der() + .map_err(invalid_der)?; + let public_key = RsaPublicKey::from_public_key_der(&public_key_der) + .map_err(|error| VerifyError::InvalidSignature(error.to_string()))?; + let signature = RsaSignature::try_from(signer.signature.as_bytes()) + .map_err(|_| VerifyError::SignatureNotVerified)?; + VerifyingKey::::new(public_key) + .verify(&signed_content, &signature) + .map_err(|_| VerifyError::SignatureNotVerified) +} + +fn signer_matches_certificate( + signer: &cms::signed_data::SignerInfo, + certificate: &x509_cert::Certificate, +) -> bool { + match &signer.sid { + SignerIdentifier::IssuerAndSerialNumber(id) => { + id.issuer == *certificate.tbs_certificate().issuer() + && id.serial_number == *certificate.tbs_certificate().serial_number() + } + SignerIdentifier::SubjectKeyIdentifier(_) => false, + } +} + +fn single_attribute_value<'a>( + attrs: &'a cms::signed_data::SignedAttributes, + wanted: &str, +) -> Result<&'a der::Any, VerifyError> { + let wanted = oid(wanted)?; + let mut matches = attrs.iter().filter(|attribute| attribute.oid == wanted); + let attribute = matches.next().ok_or(VerifyError::SignatureNotVerified)?; + if matches.next().is_some() || attribute.values.len() != 1 { + return Err(VerifyError::SignatureNotVerified); + } + attribute + .values + .get(0) + .ok_or(VerifyError::SignatureNotVerified) +} + +fn oid(value: &str) -> Result { + ObjectIdentifier::new(value).map_err(|error| VerifyError::InvalidSignature(error.to_string())) +} + +fn invalid_der(error: der::Error) -> VerifyError { + VerifyError::InvalidSignature(error.to_string()) +} + +fn invalid_signature(message: &str) -> Result { + Err(VerifyError::InvalidSignature(message.to_owned())) +} + +#[cfg(test)] +mod tests { + use super::{find_named_section_bytes, ParsedManifest}; + + #[test] + fn parses_crlf_and_continuation_lines() { + let manifest = ParsedManifest::parse( + b"Manifest-Version: 1.0\r\nLong: first\r\n second\r\n\r\nName: index.xml\r\nSHA-256-Digest: abc\r\n def\r\n\r\n", + ) + .unwrap(); + assert_eq!(manifest.main.get("Long").unwrap(), "firstsecond"); + assert_eq!( + manifest + .section("index.xml") + .unwrap() + .get("SHA-256-Digest") + .unwrap(), + "abcdef" + ); + } + + #[test] + fn retains_exact_named_section_bytes_with_lf_endings() { + let bytes = b"Manifest-Version: 1.0\n\nName: index.xml\nSHA-256-Digest: abc\n def\n\n"; + assert_eq!( + find_named_section_bytes(bytes, "index.xml").unwrap(), + b"Name: index.xml\nSHA-256-Digest: abc\n def\n\n" + ); + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index d9502d9..63462e6 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -10,6 +10,7 @@ pub mod autogen; pub mod download; pub mod fdroid_autogen; pub mod fdroid_catalog; +pub mod fdroid_signed_index; pub mod github_autogen; pub mod github_latest_commit; pub mod github_releases; @@ -23,6 +24,7 @@ mod lua_provider_host; #[cfg(feature = "lua")] #[doc(hidden)] pub mod lua_runtime_hooks; +pub mod onboarding; pub mod provider_cache; pub mod read_model; pub mod runtime; diff --git a/crates/getter-operations/src/onboarding.rs b/crates/getter-operations/src/onboarding.rs new file mode 100644 index 0000000..8ba3504 --- /dev/null +++ b/crates/getter-operations/src/onboarding.rs @@ -0,0 +1,595 @@ +//! Getter-owned fresh-install setup preview and apply orchestration. + +use crate::autogen::{self, AutogenAcceptance, AutogenApplySpec, AutogenOperationError}; +use crate::fdroid_autogen; +use crate::fdroid_signed_index::{ + verify_fdroid_index_jar, MAX_ARCHIVE_BYTES, OFFICIAL_INDEX_JAR_URL, +}; +use getter_core::autogen::{ + validate_installed_inventory, InstalledInventory, InstalledInventoryItem, +}; +use getter_core::PackageId; +use getter_storage::{CacheDb, MainDb}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use sha2::{Digest, Sha512}; +use std::collections::{BTreeSet, HashSet}; +use std::fs; +use std::io::Read; +use std::path::{Path, PathBuf}; +use std::time::{SystemTime, UNIX_EPOCH}; + +pub trait FdroidCatalogTransport { + fn fetch_official_index_xml(&self) -> Result; +} + +pub struct UreqFdroidCatalogTransport; + +impl FdroidCatalogTransport for UreqFdroidCatalogTransport { + fn fetch_official_index_xml(&self) -> Result { + let response = ureq::get(OFFICIAL_INDEX_JAR_URL) + .call() + .map_err(|error| format!("official F-Droid index.jar request failed: {error}"))?; + let mut body = response.into_body().into_reader(); + let mut jar = Vec::new(); + body.by_ref() + .take((MAX_ARCHIVE_BYTES + 1) as u64) + .read_to_end(&mut jar) + .map_err(|error| format!("official F-Droid index.jar read failed: {error}"))?; + if jar.len() > MAX_ARCHIVE_BYTES { + return Err(format!( + "official F-Droid index.jar exceeds the {MAX_ARCHIVE_BYTES}-byte limit" + )); + } + verify_fdroid_index_jar(&jar) + .map_err(|error| format!("official F-Droid index.jar verification failed: {error}")) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum SetupReadiness { + NeedsPackageSetup, + Ready, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SetupStatus { + pub state: SetupReadiness, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum SetupCandidateCategory { + Fdroid, + InstalledFallback, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SetupCandidate { + pub package_id: String, + pub display_name: Option, + pub category: SetupCandidateCategory, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SetupDiagnostic { + pub code: String, + pub message: String, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SetupPreview { + pub format: String, + pub version: u32, + pub preview_id: String, + pub candidates: Vec, + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +struct StoredSetupPreview { + format: String, + version: u32, + preview_id: String, + candidates: Vec, + diagnostics: Vec, + fdroid_preview: Value, + installed_preview: Value, + inventory: InstalledInventory, + context_fingerprint: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SetupAcceptance { + AcceptAll, + Accept(Vec), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SetupApplyResult { + pub readiness: SetupReadiness, + pub applied_package_ids: Vec, +} + +pub fn derive_setup_status( + has_enabled_tracked_package: bool, + inventory: &InstalledInventory, +) -> SetupStatus { + SetupStatus { + state: if !has_enabled_tracked_package && !inventory.items.is_empty() { + SetupReadiness::NeedsPackageSetup + } else { + SetupReadiness::Ready + }, + } +} + +pub fn derive_setup_status_cache_only( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + inventory: &InstalledInventory, +) -> Result { + if main_db + .tracked_packages()? + .iter() + .any(|package| package.enabled) + { + return Ok(SetupStatus { + state: SetupReadiness::Ready, + }); + } + let request = json!({ + "mode": "use_cached", + "installed_inventory": inventory, + }); + let fdroid_preview = fdroid_autogen::preview_fdroid_packages_json( + data_dir, + main_db, + cache_db, + &request.to_string(), + ) + .unwrap_or_else(|_| json!({ "candidates": [] })); + let matched = fdroid_handled_android_ids(&fdroid_preview); + let fallback = InstalledInventory::new( + inventory + .items + .iter() + .filter(|item| match item { + InstalledInventoryItem::AndroidPackage { package_name, .. } => { + !matched.contains(package_name) + } + InstalledInventoryItem::MagiskModule { .. } => true, + }) + .cloned() + .collect(), + ); + let fallback_plan = autogen::build_installed_autogen_plan(data_dir, main_db, &fallback)?; + let actionable = + !candidate_ids(&fdroid_preview).is_empty() || !fallback_plan.candidates.is_empty(); + Ok(SetupStatus { + state: if actionable { + SetupReadiness::NeedsPackageSetup + } else { + SetupReadiness::Ready + }, + }) +} + +pub fn preview_setup_with_transport( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + inventory: InstalledInventory, + transport: &dyn FdroidCatalogTransport, +) -> Result { + validate_installed_inventory(&inventory) + .map_err(|error| AutogenOperationError::Autogen(error.to_string()))?; + let android_names: Vec<_> = inventory + .items + .iter() + .filter_map(|item| match item { + InstalledInventoryItem::AndroidPackage { package_name, .. } => { + Some(package_name.clone()) + } + InstalledInventoryItem::MagiskModule { .. } => None, + }) + .collect(); + let refresh = transport.fetch_official_index_xml(); + let refresh_failed = refresh.is_err(); + let request = match refresh { + Ok(xml) => { + json!({"index_xml": xml, "mode": "force_refresh", "package_names": android_names, "installed_inventory": inventory}) + } + Err(_) => { + json!({"mode": "force_refresh", "package_names": android_names, "installed_inventory": inventory}) + } + }; + let mut diagnostics = Vec::new(); + let fdroid_preview = match fdroid_autogen::preview_fdroid_packages_json( + data_dir, + main_db, + cache_db, + &request.to_string(), + ) { + Ok(preview) => preview, + Err(_) if refresh_failed => { + diagnostics.push(SetupDiagnostic { code: "setup.fdroid_unavailable".into(), message: "F-Droid catalog is unavailable; installed fallback candidates are still available".into() }); + let (target_alias, target_path, _) = autogen::generated_repository_config(data_dir)?; + json!({ + "operation": "fdroid.autogen.preview", + "target_repo_id": target_alias.as_str(), + "target_repo_path": target_path, + "candidates": [], + "diagnostics": [], + }) + } + Err(error) => return Err(error), + }; + for diagnostic in fdroid_preview + .get("diagnostics") + .and_then(Value::as_array) + .into_iter() + .flatten() + { + diagnostics.push(SetupDiagnostic { + code: diagnostic + .get("code") + .and_then(Value::as_str) + .unwrap_or("setup.fdroid_stale") + .to_owned(), + message: diagnostic + .get("message") + .and_then(Value::as_str) + .unwrap_or("F-Droid catalog cache is stale") + .to_owned(), + }); + } + let matched_android_ids = fdroid_handled_android_ids(&fdroid_preview); + let fallback_inventory = InstalledInventory::new( + inventory + .items + .iter() + .filter(|item| match item { + InstalledInventoryItem::AndroidPackage { package_name, .. } => { + !matched_android_ids.contains(package_name) + } + InstalledInventoryItem::MagiskModule { .. } => true, + }) + .cloned() + .collect(), + ); + let installed_plan = + autogen::build_installed_autogen_plan(data_dir, main_db, &fallback_inventory)?; + let installed_preview = autogen::installed_preview_json(data_dir, &installed_plan)?; + let mut candidates = Vec::new(); + append_candidates( + &mut candidates, + &fdroid_preview, + SetupCandidateCategory::Fdroid, + ); + append_candidates( + &mut candidates, + &installed_preview, + SetupCandidateCategory::InstalledFallback, + ); + candidates.sort_by(|a, b| a.package_id.cmp(&b.package_id)); + candidates.dedup_by(|a, b| a.package_id == b.package_id); + let context_fingerprint = current_context_fingerprint(main_db)?; + let mut random = [0_u8; 32]; + getrandom::fill(&mut random).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to create setup preview id: {error}")) + })?; + let preview_id: String = random.iter().map(|byte| format!("{byte:02x}")).collect(); + let stored = StoredSetupPreview { + format: "getter-setup-preview".into(), + version: 1, + preview_id: preview_id.clone(), + candidates: candidates.clone(), + diagnostics: diagnostics.clone(), + fdroid_preview, + installed_preview, + inventory, + context_fingerprint, + }; + persist_setup_preview(data_dir, &stored)?; + Ok(SetupPreview { + format: stored.format, + version: stored.version, + preview_id, + candidates, + diagnostics, + }) +} + +pub fn preview_setup( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + inventory: InstalledInventory, +) -> Result { + preview_setup_with_transport( + data_dir, + main_db, + cache_db, + inventory, + &UreqFdroidCatalogTransport, + ) +} + +pub fn apply_setup_preview( + data_dir: &Path, + main_db: &MainDb, + preview: &SetupPreview, + acceptance: &SetupAcceptance, +) -> Result { + let (trusted, claim_path, ready_path) = claim_setup_preview(data_dir, &preview.preview_id)?; + let prepare = (|| -> Result<_, AutogenOperationError> { + if trusted.context_fingerprint != current_context_fingerprint(main_db)? { + return Err(AutogenOperationError::Autogen( + "setup preview is stale because repository or tracked-package state changed".into(), + )); + } + let known: BTreeSet<_> = trusted + .candidates + .iter() + .map(|candidate| candidate.package_id.clone()) + .collect(); + let accepted: BTreeSet<_> = match acceptance { + SetupAcceptance::AcceptAll => known.clone(), + SetupAcceptance::Accept(ids) => ids.iter().map(ToString::to_string).collect(), + }; + if let Some(unknown) = accepted.difference(&known).next() { + return Err(AutogenOperationError::Autogen(format!( + "unknown setup candidate '{unknown}'" + ))); + } + let accepted_for = |category| { + trusted + .candidates + .iter() + .filter(|candidate| candidate.category == category) + .filter(|candidate| accepted.contains(&candidate.package_id)) + .map(|candidate| { + candidate.package_id.parse().map_err(|error| { + AutogenOperationError::Autogen(format!( + "invalid preview package id: {error}" + )) + }) + }) + .collect::, _>>() + .map(AutogenAcceptance::Accept) + }; + let fdroid_acceptance = accepted_for(SetupCandidateCategory::Fdroid)?; + let fallback_acceptance = accepted_for(SetupCandidateCategory::InstalledFallback)?; + Ok((accepted, fdroid_acceptance, fallback_acceptance)) + })(); + let (accepted, fdroid_acceptance, fallback_acceptance) = match prepare { + Ok(prepared) => prepared, + Err(error) => { + restore_setup_preview_claim(&claim_path, &ready_path)?; + return Err(error); + } + }; + let apply_result = autogen::apply_preview_batch( + data_dir, + main_db, + &[ + AutogenApplySpec { + preview: &trusted.fdroid_preview, + acceptance: &fdroid_acceptance, + expected_operation: "fdroid.autogen.preview", + expected_generator: getter_core::autogen::FDROID_AUTOGEN_GENERATOR, + }, + AutogenApplySpec { + preview: &trusted.installed_preview, + acceptance: &fallback_acceptance, + expected_operation: "installed.preview", + expected_generator: getter_core::autogen::INSTALLED_AUTOGEN_GENERATOR, + }, + ], + ); + if let Err(error) = apply_result { + restore_setup_preview_claim(&claim_path, &ready_path)?; + return Err(error); + } + // Any accepted generated package is enabled by the committed batch. Empty + // acceptance leaves the still-actionable trusted candidate set unchanged. + let readiness = if accepted.is_empty() && !trusted.candidates.is_empty() { + SetupReadiness::NeedsPackageSetup + } else { + SetupReadiness::Ready + }; + // Changes are committed; cleanup cannot turn success into a reported failure. + let _ = fs::remove_file(&claim_path); + Ok(SetupApplyResult { + readiness, + applied_package_ids: accepted.into_iter().collect(), + }) +} + +fn append_candidates( + output: &mut Vec, + preview: &Value, + category: SetupCandidateCategory, +) { + for candidate in preview + .get("candidates") + .and_then(Value::as_array) + .into_iter() + .flatten() + { + if let Some(package_id) = candidate.get("package_id").and_then(Value::as_str) { + output.push(SetupCandidate { + package_id: package_id.to_owned(), + display_name: candidate + .get("name") + .and_then(Value::as_str) + .map(str::to_owned), + category, + }); + } + } +} + +fn candidate_ids(preview: &Value) -> Vec { + preview + .get("candidates") + .and_then(Value::as_array) + .into_iter() + .flatten() + .filter_map(|candidate| { + candidate + .get("package_id") + .and_then(Value::as_str) + .map(str::to_owned) + }) + .collect() +} + +fn fdroid_handled_android_ids(preview: &Value) -> HashSet { + let mut package_ids = candidate_ids(preview); + package_ids.extend( + preview + .get("skipped") + .and_then(Value::as_array) + .into_iter() + .flatten() + .filter(|skip| { + skip.get("reason").and_then(Value::as_str) + == Some("covered_by_higher_priority_repo") + }) + .filter_map(|skip| { + skip.get("package_id") + .and_then(Value::as_str) + .map(str::to_owned) + }), + ); + package_ids + .into_iter() + .filter_map(|id| id.strip_prefix("android/f-droid/app/").map(str::to_owned)) + .collect() +} + +fn setup_preview_dir(data_dir: &Path) -> PathBuf { + data_dir.join("cache/setup-previews") +} + +fn setup_preview_path(data_dir: &Path, preview_id: &str) -> Result { + if preview_id.len() != 64 || !preview_id.bytes().all(|byte| byte.is_ascii_hexdigit()) { + return Err(AutogenOperationError::Autogen( + "setup preview id is invalid".into(), + )); + } + Ok(setup_preview_dir(data_dir).join(format!("{preview_id}.json"))) +} + +fn persist_setup_preview( + data_dir: &Path, + preview: &StoredSetupPreview, +) -> Result<(), AutogenOperationError> { + let dir = setup_preview_dir(data_dir); + fs::create_dir_all(&dir).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to create setup preview store: {error}")) + })?; + let final_path = setup_preview_path(data_dir, &preview.preview_id)?; + let temp_path = dir.join(format!(".{}.tmp", preview.preview_id)); + let bytes = serde_json::to_vec(preview) + .map_err(|error| AutogenOperationError::Autogen(error.to_string()))?; + fs::write(&temp_path, bytes).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to write setup preview: {error}")) + })?; + fs::rename(&temp_path, &final_path).map_err(|error| { + let _ = fs::remove_file(&temp_path); + AutogenOperationError::Autogen(format!("failed to commit setup preview: {error}")) + }) +} + +fn claim_setup_preview( + data_dir: &Path, + preview_id: &str, +) -> Result<(StoredSetupPreview, PathBuf, PathBuf), AutogenOperationError> { + let path = setup_preview_path(data_dir, preview_id)?; + let claim_path = setup_preview_dir(data_dir).join(format!(".{preview_id}.applying")); + fs::rename(&path, &claim_path).map_err(|_| { + AutogenOperationError::Autogen( + "setup preview is unknown, consumed, or already applying".into(), + ) + })?; + let metadata = fs::metadata(&claim_path).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to inspect claimed setup preview: {error}")) + })?; + let modified = metadata.modified().unwrap_or(UNIX_EPOCH); + if SystemTime::now() + .duration_since(modified) + .unwrap_or_default() + .as_secs() + > 30 * 60 + { + let _ = fs::remove_file(&claim_path); + return Err(AutogenOperationError::Autogen( + "setup preview has expired".into(), + )); + } + let bytes = fs::read(&claim_path).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to read setup preview: {error}")) + })?; + let preview: StoredSetupPreview = serde_json::from_slice(&bytes).map_err(|error| { + AutogenOperationError::Autogen(format!("trusted setup preview is invalid: {error}")) + })?; + if preview.preview_id != preview_id { + return Err(AutogenOperationError::Autogen( + "trusted setup preview id does not match its key".into(), + )); + } + Ok((preview, claim_path, path)) +} + +fn restore_setup_preview_claim( + claim_path: &Path, + ready_path: &Path, +) -> Result<(), AutogenOperationError> { + fs::rename(claim_path, ready_path).map_err(|error| { + AutogenOperationError::Autogen(format!( + "setup apply failed and its preview claim could not be restored: {error}" + )) + }) +} + +fn current_context_fingerprint(main_db: &MainDb) -> Result { + let repositories = main_db.repositories()?; + let tracked = main_db.tracked_packages()?; + let repository_state: Vec<_> = repositories + .iter() + .map(|repo| { + json!({ + "id": repo.id.as_str(), + "name": repo.name, + "priority": repo.priority.value(), + "api_version": repo.api_version, + "path": repo.path, + "revision": repo.revision, + }) + }) + .collect(); + let tracked_state: Vec<_> = tracked + .iter() + .map(|package| { + json!({ + "package_id": package.package_id.to_string(), + "enabled": package.enabled, + "favorite": package.favorite, + "pin_version": package.pin_version, + "repository_id": package.repository_id.as_ref().map(ToString::to_string), + "resolution": format!("{:?}", package.package_resolution), + }) + }) + .collect(); + let bytes = serde_json::to_vec(&json!({ + "repositories": repository_state, + "tracked_packages": tracked_state, + })) + .map_err(|error| AutogenOperationError::Autogen(error.to_string()))?; + Ok(format!("sha512:{:x}", Sha512::digest(bytes))) +} diff --git a/crates/getter-operations/src/startup.rs b/crates/getter-operations/src/startup.rs index c2a650a..72cdad1 100644 --- a/crates/getter-operations/src/startup.rs +++ b/crates/getter-operations/src/startup.rs @@ -2,6 +2,7 @@ #[cfg(feature = "lua")] use crate::lua_provider_host::{evaluate_provider_backed_package, ProviderBackedPackageEvalConfig}; +use crate::onboarding::{derive_setup_status_cache_only, SetupStatus}; #[cfg(feature = "lua")] use crate::provider_cache::{ProviderCacheMode, CACHE_ONLY_MISS}; use getter_core::autogen::{ @@ -55,6 +56,7 @@ pub struct StartupSnapshot { pub format: String, pub version: u32, pub bootstrap: BootstrapStatus, + pub setup: SetupStatus, pub repositories: Vec, pub apps: Vec, pub update_count: usize, @@ -206,10 +208,15 @@ pub fn startup( .map_err(|error| StartupError::Inventory(error.to_string()))?; let bootstrap = bootstrap_data_dir(data_dir)?; let db = MainDb::open(&bootstrap.main_db.path)?; + crate::autogen::recover_autogen_transactions(data_dir, &db) + .map_err(|error| StartupError::Inventory(error.to_string()))?; + let cache_db = CacheDb::open(&bootstrap.cache_db.path)?; let stored_repositories = db.repositories()?; let repositories = stored_repositories.iter().map(repository_dto).collect(); - let apps: Vec<_> = db - .tracked_packages()? + let tracked_packages = db.tracked_packages()?; + let setup = derive_setup_status_cache_only(data_dir, &db, &cache_db, &inventory) + .map_err(|error| StartupError::Inventory(error.to_string()))?; + let apps: Vec<_> = tracked_packages .into_iter() .filter(|p| p.enabled) .map(|package| app_summary(data_dir, &stored_repositories, &inventory, package)) @@ -222,6 +229,7 @@ pub fn startup( format: STARTUP_SNAPSHOT_FORMAT.to_owned(), version: STARTUP_SNAPSHOT_VERSION, bootstrap, + setup, repositories, apps, update_count, diff --git a/crates/getter-operations/tests/fdroid_signed_index.rs b/crates/getter-operations/tests/fdroid_signed_index.rs new file mode 100644 index 0000000..ec96ec5 --- /dev/null +++ b/crates/getter-operations/tests/fdroid_signed_index.rs @@ -0,0 +1,190 @@ +use std::io::{Cursor, Read, Write}; + +use getter_operations::fdroid_signed_index::{verify_fdroid_index_jar, VerifyError}; +use zip::write::SimpleFileOptions; +use zip::{ZipArchive, ZipWriter}; + +const FIXTURE: &[u8] = include_bytes!("fixtures/fdroid-official-index.jar"); +const WRONG_CERT_FIXTURE: &[u8] = include_bytes!("fixtures/wrong-cert-index.jar"); + +#[test] +fn verifies_official_fdroid_signed_index() { + let xml = verify_fdroid_index_jar(FIXTURE).expect("official fixture should verify"); + assert!(xml.starts_with(" Vec<(String, Vec)> { + let mut archive = ZipArchive::new(Cursor::new(FIXTURE)).unwrap(); + (0..archive.len()) + .map(|index| { + let mut entry = archive.by_index(index).unwrap(); + let name = entry.name().to_owned(); + let mut bytes = Vec::new(); + entry.read_to_end(&mut bytes).unwrap(); + (name, bytes) + }) + .collect() +} + +fn build(entries: impl IntoIterator)>) -> Vec { + let mut output = Cursor::new(Vec::new()); + { + let mut writer = ZipWriter::new(&mut output); + for (name, bytes) in entries { + writer + .start_file(name, SimpleFileOptions::default()) + .unwrap(); + writer.write_all(&bytes).unwrap(); + } + writer.finish().unwrap(); + } + output.into_inner() +} + +fn rewrite(mut mutation: impl FnMut(&str, &mut Vec)) -> Vec { + build(entries().into_iter().map(|(name, mut bytes)| { + mutation(&name, &mut bytes); + (name, bytes) + })) +} + +fn append_entry(name: &str, bytes: &[u8]) -> Vec { + let mut all = entries(); + all.push((name.to_owned(), bytes.to_vec())); + build(all) +} + +fn append_duplicate_index() -> Vec { + let mut jar = append_entry("dupe0.xml", b"duplicate"); + let needle = b"dupe0.xml"; + let replacement = b"index.xml"; + let mut matches = Vec::new(); + for offset in 0..=jar.len() - needle.len() { + if jar[offset..].starts_with(needle) { + matches.push(offset); + } + } + assert_eq!(matches.len(), 2); + for offset in matches { + jar[offset..offset + needle.len()].copy_from_slice(replacement); + } + jar +} + +fn replace_entry(name: &str, replacement: &[u8]) -> Vec { + build(entries().into_iter().map(|(entry_name, bytes)| { + if entry_name == name { + (entry_name, replacement.to_vec()) + } else { + (entry_name, bytes) + } + })) +} diff --git a/crates/getter-operations/tests/fixtures/fdroid-official-index.jar b/crates/getter-operations/tests/fixtures/fdroid-official-index.jar new file mode 100644 index 0000000000000000000000000000000000000000..46dd0bf621050799c614ac18c2478be2d10ab13d GIT binary patch literal 3375358 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

F>#n7+F?X)?uC=i3*5R&C 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

E(`d4_#YY-f=?`tOk(R5w>#5jC8$)me37wE9 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�jGf?m;m_ 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%0AIn1={fTJ)^g8vsu$}bZ6@Z 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%0DtdK79OP0JdhlBs{;G8=<0R|BP?vN&zkAXK^? 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

rI#kJkP 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 z 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 ^&lDb8dhhoy;=0+7s4EIsy7 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 z zKh5T|a4R!&ur-s3GHbTGYl7NDCZo=*-)Jb@8g(}Qw%eM+baV$9N&%``>51*cV77^1 zwj*`PfnU-vL2v_#0TlWJWo>GL!UiP^lL!P9KCw|4mF#c_gcIOPs-nmJO?IKTofyC3 z$+7(exm70U0TWc;0_hvk1|m7j1^XY0CyJfxaBUpS|H$&htsN;UNW$t8ehmL*66U{5 z!kT*?P$nLv->CKu0n;&eC~uj1C>ZbfG1YtvP`(n}%pf`eJq2oOCw63lG6_)x5qS|( z?S2snv-c9Vkg(bks@}^a%zK%Hc{`+LCSXvC5&%eJhw!E`YVsh5ERCE5J}=z`K?)Hq z`KC$ba8m(Psu2-AqywU^Fs?|LYk^k$wI!@lP4#ypVcQ{$pHSLsG@pcb_u;2bZv<<9 zFc#A#m~s8@6(5>G9)Clx^Pw5UbVX>-bw0F3iG1bLgfjWtmby8 zhS)DQV&zlIl~1K^E*)e!f+o(ghY0_xddryjxuqa`(o(!9Ju~-4(Sx8X=U%4o4i4Iy zVvAP4YClQe5PpDzhHxq~;IIm{)ulE|0GN`Wi|)le7n3`<3a8~K5^^e@52jpOHdiSn z8Ay0HRZq&V&D*@jC8rFVB%qoqK{t7jwl1JWxmSwr&;41sg`SljJsZr1Y<|JCfMG;L z-MFU@glDOng`c?>@JxOZvH~C>ILWU!78#}%HxswGB;6264&9Js;)KRy>X8Zk6sorA zdDV&FD)FOd!U07z8qdT)PQy$7QMihTM*(m0NW2xqPClKIhk@$=5)M2Yd7gp92*cFN(){Lv?mvfLhQ!O#3j zQIyX@T=YI991VC_079VoqKSJmB#fWLWgZ;7&nw$Y`2))0|A0eCNO>(7U-<2E!n`On zK*x;w#CPZGL8Ho=Oym`C=44!rPg!O#UfaOo9UL6d6!Vi8z~LwK_z9vkBlZ&@hn{6` zcq*k2>YO*JemohBXYCJj%)$vg3-e(j&0R)Lg@6u)1hV8Bl2THrprm#$hGU*U6l{jm zo4HaHu>=5=V*;>BwhUzfvIm*P{dw`C-25UhtJobNbW|lD+2rHnd_)E@dZ-*AtKDR* z5102@pnh2UtoVt&yG`#*8V$^+h!nOWWf*Qur3TlIuz@ zR(99*br;1b-~7lB6GP}0PLO!U*! zQ>S{ZBF$KmXXKpkC^7ickDs4JjFaQX(a`(p)AP?yKmGK8igL5`Q~dm6@YDUXIUN7w z*Yd=ho$bUM*0vhc(u>ju>RW6rGp!!DQ&E`ugZp+9gJRt+J23f17LjM73Xw12+El4hOrJ~H`N0Hs16Ja)F3jT6naRM_A4ym`2XH&U1{q}(oU2jQqTqC zC`LJ(2n@vNgB=5`X5lLvWS@w!+E%ni7hIFiOzFXIZ;+haKf3Dm&TmOGJil&zIC*nL zisB+$_!lrDOw=W>Ga}601;*T6V9Xr~k%Pq9P5mTwtk6Ie0*tt@LR6&^QJRD{nCHyY z5-#!=UDV(UV&pRgmhT51lABNryecrR9(cxU3yfj>)&-`XX~EcCV2s@bra7IW7os94 zs?x{UJOD)i=Ynei>g0KmVF<@JK+P!<0WbxMorNKhB}Rdlf-KxkY!5hws=(MNa%R1% zz?3ba--5twHzH%gG0&#NCe|UvkP=e!Lt2$Gg_ea z6}MVUwaI6!YsnnCF2;zo$l1DbQtLY1L7m|>)hgwW%I=&mp4(b#k!_BBZtqA-(F1UK zO~m6={#+@kY&9=W)~(`}hJ$E=_b8dB->Rsxx}?aKS4g3dAzD0y)KwMGr#}nXFCmvj z7vY1#wlSxm@^f(pB$1XvEyounH4P5tTwRiGT z8M7>rH&S{5*^&PZnBK=JN1yPm;6bOG4><`eAH^v}u+%gz(&5%kz}L(8YiantshJuY zvdWm`*(pV!4Cd<`cYdd-h$>mGllh2~%Br`sIXzKq6FF^#6te|C1$9OdXoir+A@MG% z=wm65rP?;xKq~>VvyY^bLYy>u#*C zMYX(mX_iz>j)$zB4!G&UCQ--K2cvCkSt-YBP*zGcv>uP_ZL$Q`ZDqH#mDQiqb+qLBvtO6)`wpHcp zvu)SfjeOtP<`pg-I9w|-L6w@oNb~?GYEUAGBy!T!jeXxm^et*!E3z5g(OK{KqHFuX?b9dQoZj_*0NCbkcI5b=o4{=Oz zb^ECSs4j@}98e}8WM)8(gDPsHe43pYL5zs4fzoM)Abf9Gc3mV+IeXdt^~mn-bn~0V z*VtWrjorn!xrr1eMC2I3(c*f6uu$F4GGQ)-DBY%`Dm2LFkzU}VC^V8rVie}mT`<%~ z21;DOu(Cu@HIWQ5wf5rsD-z!?Bfv+jlBfx&a`62EF2DGuVwhP>&uMHl%*fnbXP7}1 z?xIDlt(kAT{1H!~o@A4%wMH;Aq`*xwxa^@H&M02f%A*#oBJ@nV1|bKP&Ls$au}Lt6 zjH`xR@x{O=se5azx4f5>q)Vl6g$k0fqD-{%)cLUVBnoy=Y>LU?3AM^8;s_PYa(Xvp z?t^fCg03UpNLCE~S*abnFd<^e8zB>esU$8&p}@J#xJ+URQMh}s>|gXLnL2KPpa3zb zC)|vb>-lF@g_e85QHzWiJt!8bK(?u(ZOOp0#L3Q zP>6(b$JMK1q}ae<7%2ruFX#fUQ_N`FXqpY@Olm$Pr22p``)%r|d~LBzAz)A-QHKu{ zNeZE2nGA+QGsA4IY?;-u(p zeAS}3u%d*J5#7S9fgPU;M8lfrx8TwQTxeU;BjllPEgIa($T0&&C(_P;mt{1zr3FYP zsd%P(%|q27z2=04j&l=6D(3Z$Z8HXX%g*6ySk*%ojqE{5C5Eblt%Kxxim)2`dOe`P zV_UnS@ZW?@TEx=eR$#K0>I&^>a#JY*0XQ-$*MyWKxe`0t2d>4yR5mSUx2M3d_HWr~ zWv$5AqTmQ{st6{Qvz_l7YP3;SyQ5uj3GiWSxx4U&f-U6at4~U_pOKS5?>Zla+9}ox zxJBqi-@+!xzB<4a>&K(qHO zsV?(PkgzL29mt5e3`t^iQD9p(e8MqaV21&y;M5_CU@cjY=z)mTN?#Z-^*L7))odr1 zMH`Xq(Ev#?S0N~s=ndbT5|ekUsN;)Li|vN01doxpK3 z7CD+(7FXxWn!f}kAm;3GC|L6a!ZNI3QTa^~LoiOWqN${?e?|svkZV_7A!7 za(Vbr`?o6=ZstW9NW=n*kIWZXS$C4DiBJ=aF_#1?v~LY5o*&AEB5S!k4wyU!r78}N zt}gn04Jz3+^tH3{z=WR=_~pDbc{Gldf}l8+l)H|$9Yl2(j!7B&2N77iP)vUN2}(E7 z5ehgyGx};hv)9u|PrSK2C@vIr&aXY+i+c;#ykU5e+Ty<|?D5%z06cseV0adFFjx{H zFhB7AAh(q8kx>n^40`2MPTnOURi-j$gi-zce3_30Az<9i0#ZUJ^4qtk;Kb9;E{=N` zmh%g(4=>dMf}$k1_6}iV7}A9NFPzL0&>^o3 zWf%C&%o}BR5f@eBRy*3C=kzPu%NF@iEX~F99IN661_dmFp7!zL=1PN|(7QV6ogeiM zj##l;0K~9q3BtC;|*6tXoW8yUSV>DUsmcArVfmYW7O3w|_8UZP7D5evqy5sa<0Tu3-1%a!V1 z=`CoD_Ox?YU)m8wn$u9Rwpc@v{{8%jX6O~GQS%XNb1G3J*URJvS14OUCUq{mBb@d5 z2>qAB;7Z-8B+`_+MF~+Pjud`~2x>T01$ipU0v0m@2|ThLt&jC_mt{%xyioplU+ll{ zVh`edo8IsTrhSw9$CNt0$ZJ!$Hg#5o%;AT$jk~!7Pd$JUj39A~vQN~P{G}K{C-wSd zsSx*>dyG!}8B3KqC~P93WNaEeRMtPzy+|QvmH1hr z#`2ER)pQ>zh%w96o;4u8q%oHk+`Je;sj8F{;~19I0opbp0raI~-qrkRZM(-m~ zqg(cC7GGa@n2H4~(#S$Q2@r}D;IaVpC_?y1R?$&|6?__vsVS;OD;xX&2#fLNay@`orPYM$KSDqwC+5T5SSA*(H|5OR2VEnddfHNw}=ZLc(Rufk|+wCr*^2P&m~X z2f0gZ_(<4`5G9I$a6?9)n}z|fKS>INWb6j0TL!YGB#vX^_9@qSMR_vc4g&pk-R@4c z(CdC7{l*s3ixyKI_7C)YO%@X_YqFSdS>weNn^XbWvr&~Oz*+&<6#xPTV0{S4B10^- zkhFy)Gt+cZ&vOF-#;g?O4NOXlP0cj;B^Hz2Hl6)q`W-K(W>GeHC!qRv(lR^M>QAeH zNULpaj=R-WQ-VQu%1u|JZ2cK|ETnlLZ8Pc5Bj{I>#d9X|kjZissF6P3aX6jIWHC0O zG+4J-7NeL*ed^+Er8n!~99$oFlf9H6Hfc0!pF$Y4Z1+*1isLabY{|h0r?&R@fB)|V z&&0aUbL_voXBGHu6j1|Cp$8pR>ijm*SB z939iF;IS&2&}6J59!D}ExcE0wkJE3`1&8<@WMszEMnN`8*TPKU+1!6T`OQb|xJO>c zgO9mmQq~{NU8npe+-=Y&E%>s@eY zR;+YMeZ;^7juU^4pC2 zjSX+J^ACq=JJ`Y8{{^gl*{j{{edY51k1+X>In!~sS^U1)gpEY6EfK{HY}Bht`{ym~ zpPLn|mFt^h-6AU$;1Ttr&?oIB1=+Ucq9PCq#)6InOrws4x>1o4>rk}91l587#dJYY zg1BfByA@4~m@C23T-CJPFSP$F1QGB(ph}tT0fpzlJ7L?g58DcGQr~jzC^cTHX}v_P z+M-UCPr|)kwL2q5J@n4do!!=v?(DXX^yXET!LDq1pk)p$)ZK!?qI)Tjb0PpN6eO;Z z=h}wn!lx!|&-5)2PQV@DqD*Y8JF(Ei>N-L~Q($@S>#idZtyueY^!r^$_5SA_&m(=u z^GG*$A(OG;J44x&VqTtPtf$>JujOXvrY*W|&Jv}DX} zGY5*N8dFX6oYVpEO{it9g4-nPF(QZ5{5t$mOPS6hobKF??5m!V1YUcuEyZP1#DDJT^SY} z4x@SQl;AH2|4HrwLOFA+U&3N}6bTQ7bwzDgNqNTe2zdDHv|cQXsE$KcLy`ol^kfa!~$yyKzbm36wC3*y%mHvr2Rl*VXZ>p zM8YC*{%Na#HVA~sBX|bg zw5&eJ3`yASreiMg$fXg9F`c$CxOdd1v2;_>^bMbv5EUYdnm>l4CBikI3);|#85dsy zMM+Zt<|n2)lJZ#?K;&cd*;bY(My@LG+M~jV5@MeSikW3Tqw1E#*~`~~*+Hg712)8D zVs90(7*NXtg^dO(XpmzP^XIg3l#kp~8G#J(CwW_x04>5!<-XmbbPxJR=1`+JG7`pF zKA3WTLZny61(-s zEg)vqU1NEWumCeqv*^Y~JGt|a9RD|$43l1L#S@ZO+6k#2B-HoCC5EoD-ofH3cMY&! z=b0``7#x7}m^_u}L7DXnho#IV`8?)S{Q@^Z4v=IjGI}TU!L$K1pA*3Q)!otWLSnCe zZml7zensc=ug&W$7yqx!>uhzLZEk1Mp||mhjVR>>1-ZA9isesSPZGsX+UO7{srR?6 z-f#8@tlrR^N+5P4*9{C8Nvf!NhLTvQfaSX&)iJ;Ul?KF&t=p~_22K(LfTx8C2&AmU z4axqlR6B$74bB8hcV68^U52O`;3-OU&kW*-9DcwA_~@I5S8yUzPu+T-fJ{!U*0Nvg zDOi*3x^Nzxb4ssgOf9S(Jy}U;pDh?(NfNW+cy_*793riU7q&fEok+R z-uI4gPRR`K9$nvdPrFxVO%Vy(vT4^Fm3!|+*?X~A?)fuFE28SRotRbE4xrKBA1*hA z&4tuDR5`AOh}ySz+8*eJowf(Mq4D+ro{$=?h(lL{m`a5dwyzzn6Tn`M=4R;N5Mqv)jjX~?P0$?{HC^tdY*b;-*tP?cikSE zLkSTXaKN#PI*PUjIJ>V0cH}!j0L-n8G6*p$Xj+zoI0LFMrWOkMf@#tVp=k(_0$o@R zD41U0dZwxSuf07~C?@yY!+v}ChuI#s(?jC*=0q>kfek*VUf*&)-jX4*=7NbV1Y5E|M4| z%u`=N1m;!4h+LlhI&D=ltVRE_bACCXl#D8f=V8IqC?+X6T;loopI%l2Y!iKY`L~KI zq~H?hD@i{*Q4_3g+~OmtyRCt@Wc{tR1c>?NRriO&hW=@>R1;BK(97Lk(93Wd`7%IF z2{~-w&T#8%e;LjOg07mK=N5Nu;ON?LS97j($A(kG3Q=ZBcyLwdeu)1H%Zn2bU!h); z4PHct_^g4vgCE005`ht0s0a1zJn2fhGa;?=k{_UoRZvh>~?YD0jq5bjp;_~|B?Bs9iqP1$os-m!A z3jZT(_3Vb%aqDZ18TN}L+J2ElH@CzcB!wb52W0} zt_^@5AY~HI_pKD1qMnTcWYFn@UqGUvKUjN-{!f$WTAR_ZUL?`hizK=^i*4khP#756 zEj_YBP}pMIjhPS8uo5G&^hA)e$=1UN5{0s5k?jjOk95yBbr*E=zko!eo`t)Y=wCvj zx7#`QKoTHsiyjJM03Sb36Oj&(9#kO1ufJv;CBCr*CFa&xJJc{#>*Gb!Wn1SA;rhpr z8ES?h%5dK+qAnWVHk7`>&t!RkEkx14O*-o+^6OuvvzBPF(kY4kdR%rzA)G2*D)d5@ z5J%jVIN%S`oQXtJJ)VBY1(PExNqn6pp)A83$wJA?UkH%(gAfn(zn($Yy)XQHQ2zNN z_k=yx?t(E-VtrzDD|P&D=;awD znbF{b1&Ayyx~a`@3hsD`sFGd*w_S`Cbxo21{)T5tc22n{Bn=(X2e}|Z7wK+HUNj^F zmxezj&RK(Fx^2a}27=E+Ma7K$tb-IfB!%KFF;9VimNCa6=eBxc^H($nf~DN z?kGe-{5@DmYSWk3@o)T8mD5PmLGsArGaTj;rRbuh=oQ<&B3w%@8kHs)56U8(lO4rn z@%xtrpahwt&xBJdv`h({Pv`FYOwBrn1X*2B@12&&TG{b^f|7dEi6?5cZK`n>M z3KK=ZTdWJ87lda|4-SLjGg3~K$4UBtItTqiUMH2(Bk<^y$S%2DAP>V?_A_9yV8vu$ zDKea-TXdq-c%t%a>YP!_Q=86@X|3yN`w(n&KW)GLu)XSps$gmA$E?2GhDBD! zM(2~)q}Ya4Ypi%1OqF_-=+@g{t8BWhd88{8J`y#ZKu2+7Hw9Tn=*OO6hQdZR3gw`A z0#r^W@v4)-ZKtRvFA_?h5ozp~OKuo}?fTA2Ej7#fLf-d^u@Z&g%`s5K3$|03BJAcQ zDAWae1(uYJ64Gmo6_^;XDY__xbqObNXK9nz&OC zEA4=>bcPHX#O%db0@$S+W&Lku_X%PbD8yLEcV2a$ptOat)_rmbJyEt;@tVMbrfP6X zZ>mIk^Se}9UGas=sdOP5T7-M2c1Z0%VDoW8#tp_7K;*$xQQ1h2?RzfY3>!e^bC4p; zq^=278fWViKfx-D55mdh{&{x)IG|ALwXB29<)~#g5}uBJ<_2s=`gAgx-@(+Jit^XU zO?o((g3yNIx@h`tlo1iV>~l-&9le8jb~m5oVc-9sy?0rTBT3i9_IQdc8#`0v86Xk& z3(mAvg8)dPiXhPhNS^KuEv`To6PY-9Aq!m1MrK;bOtYAUOeUFTp^Z#7W?E`7EA^Yq z3rLTk@B7^&GBPqEGZF-=>vXZrx|qm}@bGZ|-Tl9QA5-l6+mlu|uvE9Gwd<~|X}F|> zF6Co7&Sxr(ftlmCdxfxU2)MH5E!|(pR);|A@-1Bw)7x(bh;X+59LpMNZlV*^r%jpa zn9d9JqV<5SwD`xyacBwLT1e_*&}psm96Fn|=c1v;<0EVfUNtZ4_hDPus`dKrpbZG6 zdI+gHF^r519CN7L4x&7X9T%+BE?m#RmJ5I$7kNN70k%mDr6u$M?_dJg!CX!q1?pqq zIuA^gr1q$4W*FG>pvFUGvJ#YnfkM^;yBkR=p#% zwiFy|OTp=mQh*#m7+D#iJ<}{U!Fiq~X%O2Hf}2+6M2NwpVHT$$viEW%CFc=f4U)u2 z{nYm}Yq=!Bb%;^0g7sm3k>DWE>HfY1CukQOb4$T7w-lW2+-x2oBEeD}0x8yHF-rh! zO2@KYP+o$w8o*1o4dW=8?)Rps)!Yi zE0l{^S&!jyCZtjl6%~;KC|qbu6i%v-J`csRxs4?<(FC?_lrYY8u^tv(( zV?c6f=y)oF5&KP((hUT^OG7K_;j-4{S{vP%ZU-1Tx7~H*wjHO9V)Nc@J5Adz?_S>r z%(>8rLKN|tneT)!(Vf_}BILeh7A}cAR05m12lO8^%yF>hX&i-_gX0%?^r63Ox0_)D z;7|BP>G^8aR<=Yx0|t@rqUZ?wVrpeZ4ysr?K|)^)kncFWcZ$WupD!*?&af4poSic* zz9Gk=(fn`BsV}}}It8}Xt{L~0zia#Nn)BCP<1R9{lQVNWIqQD*W~S{R-_pbhW?NuE zwKHh=mYK(f75h1U$UuCM0%QtmTpA;V#|Gs=>RJg=q(UqFHRR0xCTHvA%xIT0V>>xB zwv#hsi=?rbnvwTVMOI2z@Uz+MjVv2n zRtJ9qp_2-W)0L=zLW+k|#{%~8N7Ak-zQ=-kEe9qGl_QY8zEzy1D-vM4nHm8tNfNZUD7kDuVpOF5)r&Ey*dJCKbRDejk z5n9oCH3cJ`rYX}eprJ}I^Fr{6$Bf1cOeM`9SwcZUQ$?a#S|m$B9(L|mF|rX^qv3+C z+A4%@PfOkYLh3qbr%gAumV;C-UN{(+)l#_nyp3){%Y88HisE_A*cI!dQw{6%y|peb z)qF$elOV?y3ZzP$0T}{M2uHcLlf_o*yIDkvr4$9GfQ|&y5|W5R=(MKg*?F1=E$ zTJ0&eW8XHnvi9u_*1q0#Dn|jdZKODPevA|XaK{7J5;QeN3Wj(ybyCmnie4(!_ul%dwDQ?pTTM z7+z|7@Lq+k6XM82EZ<8kKS%*~j7U`;0MIyc0(e04nqd3Tgp3CiY+D>L-Z#Pi?FhEp zzGK_l3bwtiV0SP00O-8|Yyfy1fG`BS``8CUcWm3pO7RdJLJ@_Dn!E&fL}r+zgfa4y z$UxnmWf)!@*1$wu1MUg$0R`J)y+_Tl|4p!eHNoC&CP0mjMi4?+cauKU&4rCn|D}oV z$6U|%V|r}{0-T{)@3pjDw-XLazM7tTxa1k1A3v3_{mI@FfFMqrsypf(?z_&4vh+^nfXl~~!iIxIDtP4vew+{GV^8v;hl8cN zj|&c<%I&?kgrSwb7Cq}dIy-*6+q?YdOVvB}Q{j55-lsDQ5#e8%@y$Yu4zF-Hf|aH4aI0+} zBS9WHB>?&)sWfx@g}C?;KvanpdUZWyRS2OJ;f8@7_gIQwNDs|24Z#}N{A^8-{aGGg zefnYgGr6~O68Car-o-XC!Helc77aD3s~~t4de)!K^4(s4Jj)+%e)1~?^RV*ZCoEpt z8Y!OgdBnYYu@2!L+kTxd?&ky{qVbO-pzrH8DeE^;vyJX+Srjht@u zMMY!H-qv;INd4$|@2;~&8m{gR{)TK~cu%4K#1U$1jX24X>KWxo^^D9YaFN$;S!NV_ zjFm@7wuE!mG$Y2DJix}e%LTePXHmOrJ}g~?I=3qd8^Gz@1aW^Kwv!JvGPgq4gBF>a zj%fkLuB*a!ud3Y#)ABaZP%PE)uBZ~fCGe%Mk&eRUuPX2(A4@-c&N%jfTU`KJEs;uJs!@gciT{()kUF zu+cVbN$}gUb^QrJyuqZx{Spc8g2F!-tL!yNNGmJVA63aQP%lGdf=_Ts2phtuND=t3 z>KBC)ENI_d)Uxj~K~DkZ@5yC+b=?2BFXhe?6h$+=PEnSy#u-h{*gpP3E&dF&AmSxZ zHnA?P{-hZeiU4IMu!4PN0T!2h7@_VzLZ@bc8!Y}pNj$hh22yQODM}U!BRvMfi9oxN z_yn}O@y#dFX$Yai_BndzGLWiF19P!HRj+n&XX~#lF1|yt{p9LyGQZhhlHo3e$<6n+ zMnWss*Gnxhw13p%I%z}2gFUq|NE&Q5%ycu2r(zpuf8%0$--g)Va&`CE5Dq%qM@oAR zGmk7pf}$Wp3SSnv8MH_jfg%Sr%AuFJ`0V&~z+J;87~#^HXs*kD zVRFoxGqK#UDrX`RoPETvd@pgJ=!Q73;p|WR%#Jb>X)%7onYac3mr~!!2fj99;%;4k z-aK*sR@3Zn0lSU@huDMSfLuB|KxSQlcnAOtgEX=;55@qv8GJLy60!#)yUq&1ON>$* z+XjVW=GLYe_{N^`u+!|+YiE9o)9kdaW^b!$_O_bl?uFbz9;Uu$0X86T;A+Ve6R>7r z=nP!czQhjdYwg&I6Hs)9HY#del5QYv4D%iNZeZjq(`>lz!%nkZHxU2k)4Y*9Qv3Me z@*JLkaSZ0M>dl z8vFtbC(=@(OXSBI(S@CmP_(GZ)HR?hgplp;(s%3zl@31?T~H}w$mbV!y_>OemMhSu zhyXb#L!f`Pppnv`M^nPo?8xiVqpfJ^rqG;6;7=sJ56PWIndWu`S>E%*O9WP49X;<| zJb(4>qW9-lZ^%41_&69I5LuY4j|1|E0adJQh@T+>g2AM~mlQ@9ul&sBk%%@-*-5l@ z37`Im5fwF-x;Y>fDNag3d^%*rA`y5kxc?7N5juKyej%5VH4Yluc*HB%$TL=3d}40f z8Suh-7kXyo=H1?p^C9ZH!GOk|BeMGyz?U1iGo(>4-e9*g)gg)ILB-W{BX4i}# zROebCprMT-T5>!d-%L3MNBm)D=jheTr>D=}ygq#L;$(-x4oyhOI9MT3Qmp)rj}ic- zSrp4uF=T(Rr|{&s2eY;J^7PsB%O32=otJ`0CO}uE3<|zBV^3JZ z@sP0*fjVOW%tWaA;d+?S_e_Ko5ou=^tNhkA@Wb8Y<#+(lG)U*w z1Z-exJL?emX}+~p@n71v+L9p6{^o)m!kTOjytG&BICeX)M*AN7-m%-+HP+tNSxG=S zGzukLWE27{H8eq>;iGghitIQEfc0ycPMRc1m?a^~dQvcB;~0jjpCJ3u3w&?c4`!LR z;Q$oo0bx>`YAYhgM-QC@RA5PFngFNq;0cS6&f}sI-16J~U`MYGUtgRoI5yUwa9>{Q zxV7_ijRaZulOXGU64c$38YQT&M~Q2k1>j<_{S4qNsC$h7&w^ltOO#!(slylyLKIzx zF}Rzk%K142FN?EU%|sZ4o?|=TB&hCLd>A1z?ey5pzztha_eww+M222Z!PCYYEk@A_wC!fxL$4ULzvOtAkn7=pX~1E6KGp-7`YA<<0$ZXoL(dRTr}b;3?NdQUPien1YiSM(m}vgUBOjRq)XAQgJ>0y&ObqMCE=5k$Jy^ z$lL-$BMULWrsEm`B5Ywo>L_qULi>fi+{tPO z5h8NlDn<6+zo^{jAX+0W_dAHp`yE7P_k$<}@Ery}V=-Qi_$r3qLP$2m)`COFMdBZf zC8Rc)4$7Orc>~P^Dz6dd4lb~2jOq}rz_SDUcPTBK4x-H!Lx_V<1|!lL_ThyE&&B6# z0<79a;ZVy3P1sYGu{I23He*jMHCjv5k!Z@E#92=Mq`dbxELo2gK>rPW;8UTm91d;< zQnMj^Bn150`#6}Q=^n7#Q)c-za>%s29Sa6mP^!k_NS_I?6md}nYL!6`c(_3V@NfSB zQ9!Q0Wj>z$6DuCnC-!6?-gosE3m|&RIb8fa)UlV35*T2e073vnG?4L;C`{u3sIfW4-zd5e&VVRK7j)T046IiP zV_g%BRZ*1Okg-!?z%uSK24e_%B)>_w)F^!7f3D|nD0;3d zo^-S>N~G%mWRnn(Fwk&lGX?^B?}*xUss=nui{v4Nh@ip3P9-f#(P3O;@YEZCQG|ns zVPXXmq-i!mNGtmn4BSWvIkXfhR+nlR;-6I017h3Wq4nf?%#l!eNUCwZM#I>v7kVqB zO#$L$ppSaO*a?Z1D1r^$`)I02D@bKQT92k4$sLP%$!;VEi%oHCiK%=eD7zSbA@`|{ zzJ#xePNVUS7A5J77F6_(ok~<$puFbcRGuc0(rf0z-4tOzLEYdID&RvXH>&%mCub_I ziav|fA?Ix@32xahn7TnmW+`F(AhnNRcrbHMz3x;1n9D^b?Mzj6oe=$&@(g%8c|$VM zkmxo_KjODY1)9~Yjx}1Iyo2Isl2^qay_-ArBexb{n`8h-nIJ^B^JZFLiYyE3(pnM* zT};eGLfp}vngtFVs)`2a78_>(Tv&bOef35o6u62QNd}L9FNC9*a4yY)YvzX4k`N8n zmcW*b5#Ji}iX|9wbML zgQPk|vI}x6VxFd2MdcpG^~RT=0Bhczw#6av)QlU;zA5`|`>J?ENknIE2J@SqN^V$w z^5cI}^C3lqEJ{SGWjx)V4jF)-st|Q5vY4pVz?qaytdvRo8cFFRU~=m3CA*8rVu?l5 zew=6_Sz4a$Vq1HX8a3p_%86F4S8iyF%><23xn*D9sUbW*e5ZiP-(FmT;%UyhNtuJj z9+p$j=j!brYaT-*fo^oSTobkRP`R;TT8noOYNYQcS^eFwN0c z6{iKiAmP@mR*I-fV&_Wu%hRN`O)nK%YCWS@U~cywJ(ZG69T~4O66x@HK}B>A2=%Xt zF|^RmaO3{6rr06S-hO*)uzhh$JKPZAB=+KL7HuM#cDv-$R$6Pe;?^MwwWzP}*Rn^q z-_$)uLAFt(v?1UQ5@`cwkONS1+eHzilV$-d{4DU0?-n5o6S4Nti4m61Ll0S+R^XY- zPJa?r0X53M0C~3lNWDJ zE>Q~d9O18*FHepxcaNSQqVPjqbN1%NCF)5~TB5E#esgyIg@r0-uZ~Zj0-x^9@#!mE ze0hm7lZ!9Lv3vFUST(u3&IY=tg}OH16w>ajm3HSo((c?x+PhczIx+m3F)9w3GZNWG zk^o#W0^&N{pzw?%odjWAKzAB$3TFjUQ9wH6^}X$*nwjR$+y%|576}a;2KV zT~meUwIR#v=9c|>N4o56J{rs#!GG%g>6@#l@knEy6$Nb6$`|}WT0zH`2Rq;u6-HxG zj&t-+9;)w?F~k*OF+vp|^-Ke<5phC|^p#vw(_Kwq#OH`bq?mXmFFd(J3!&mKB&OpPCgmS8^mL+n`VWpF5BdiSzWf`xY?%Dv&4qk| zgZt*a)cC6RkuM7&e9waiS01ajM}Ig!{|75QE}r=0=|9*}JR7M#WJgEuUp&J@t_C^2 z2m0yrF}ZXX;4Y}zOvVEQnB1R^J|CX{`0?V$7jN>P=a+xFI=s3VTzvY|+?&4`WWOB! z`~Bit2-YlBjr<`S}~?e^tUWCZk(N9o2l$x zWQ=qM5OgAsH_9?5^dg@jL7t@@rM_dOz_7uJ1ab}Wmf;cs$OV$_Jb)^v9@OikK9#lw z))8l`sa&gCn*pNNdzSmRnaa(2XbOjFHbPA(&qhXVt3Z_vKi0I-kSL^PE6Wk(#tTz9SYOfT&F-VGlP!U6#N?2ImJcJ%lR-!4q z4c6(YBD)IKemNeW%4QRgqE4p$`_-Pk#JukkUQo)n8Z~8J(#9rQt_Q8!4#xZMlte<* z_BSfe#qE`wI%&@h_qD3e#kK9lY2F5n&hJv0=4}Z`(6XDfLK3uI-}zWT0h$$sZUXQ> zfKHHv#S4rW2}7O}f&tpJJ!o>i2YS66UURVhaOd{|D~uu(spZS5`vhCG!WCuB`yyO& zwKGcez)VM%)2-o~TSoCKxaG{gdkoVvu#Fp6M8voEWgH@OOd7698F&If&E9t5Yxp z4pRBIk%)Vskvz0SY%`(GJviT+Cdl1#Lbr%~rBL?(Rc3l|>H=;A-bsK?WJZd~iH!if znaGOAPoR-YkRfje22!LE96;4hh6=(O?uZAo&3xbqwZ8Te{{qdA2^G+NfSFK1CNICM zxvG!k|v?l6)VRPBRe&l_RI0>fZ5XKEGm(*&?A%O#U+msnpJNa3U2 z1^RQntMYWBGBcwW-SJzD1wqV34b|BX<4FPPUL6(vS|7k1rP4+!saVqiGb(V+Fas?1 zOudgzWFibhfy2H7L<+$-=l!ZZXou22cI3zTXIyY`GiDOgDEc_KQi5HK9d{=*0+ft7 ze7q5<>M53coJ1tI5axnNZ-wU0>mD#FA-CCye#{s{~0l zqRc+kBPE^uiwRvS!w+&mIExIM(9%A@6rLj8YCaleOfwR|6G;a8g=>tww;H2_lx9fZ z5^(&&u}P$q@toj!$gO*Gs^4*uOEcsOr4o%Ohc(WS)HuM)XoQ+# zGg_=DNtw!uH75e0H>AbxaRZQ(iH4vrq^z=!6`+S#<6=2tHb>mem@Ua&M%ieS@fFkZ z2)e>}%PRQC+397o%~jyY6m}Q>1*m^D_^@n%71wVao47-q^7_-h+W+5UCOa8dD|c;) z`o4O9BjEe8$z?0Bul1gF!|DJM!j@Xy=EmAs_~Nj9FNftCVBF;^y92{nabzU92mW3F zGbBE%(og}N!t-jxQJR1Z3Mm}$l!v$mQEupDP|plxZNT>m#jR$8L6HlLuU+K`Zz164 z9M7_H5AJ>7m%vm^Q3L}TpfI=X?p65uYoO7te)-h_bQ=TUjl`I5PoKPICu&H!3r*^8 zQA@Q!tgX&sZFLsw?s1?bGT}dt;8`|NxSaqJFHS8G{6(>W^2-pB0w?l8{2CY$YVX3> z4n2!Dr0d!t>Cl$#ju7>~PUwFV>wk@6y><2R2EjHv3%1!=u)7xn=D_!lAl;z7PE$n2 zk`x|(fL)km#|Be|$KvBS0~uO~^t!}MK{W=FKftCSl!@}v<^38?Gv6`oZ-V`=PO!Hu zlNOnll)P5IFcQ`}t0G2cRm9i=$tCdu8+`W&8>PFmMq+f}j_Xd8o6@1&he^-i0TX-@IJL)C4u$y4x!tVQa4jSP!L3A-g0e}z3NFEuf z1JF6k%>kJ2hAFbs034Q@8IsRXu3}_a29*8SP7}m0e;uvF{U+DHWv!%Lw5_h9ZFLpx z?kWjN4Fm9EgJ%)2nt6gR!M*5OAZN4?X3ZR+{el(ChArtL)e=x>w&R9&&Ye6XE#t4F zl6({G-?&QBF5G5U;WoPpcXxdR)w33eCBXG=xf#@KvbiN5OSiyglV?6am7*Z_=`l&o zI51ovnZ(FN@Bj-5e8~LQ(MP@s_up0@*+Bj=?JG8gKI)6c*W)30x-{oU!!Nab3{%p_ zTJtgRGxCmu#=KhuNB{a?J`Dc)e<8yZ|NWt!n(^vZ1qKntECQ%w%|xJREmWTruYe?= zo+jBaVwHZ4?xW!~`rb-T#$k_{7l|&wl1B_~1fxJ`t4Uv0WP~C?6rdSNHZ9>579Ay* zTU*a6xdjI(#yT7=o*)^oMqysNP>#w+9UN#|M!2newqG&u=b7I1N&kJ7CI_hqO7 zI#dZ6;1Kkrci9}sS9RA!;M=;XB(S{hvRtvIT(HR6=J}PSI_!4X%j#9^K5uE=sllA`Kb2{`2Z0T z)W)MPA%5__jQ61}o3d%GwWd-wU)&vwG)GlaWPw)+Fo6-6e!-p%Vm81=jEcU`DEw?)AAw#>h5oK<%l5$K+InLt}dB*=;py9_gU z(BUT!kx>o0Ehj)GWtt^OZcHO)*xGQw7#T8XQBMxQ9r)Pm@Wp`0wmkQNMPTW8{OyZC zSE8^FiS7tJ)?hr+6wp4ObFYhUud~`urJy8+#SJJ@arf0RZy`0|(`mXsz zovTyi$qkv|gk$4oK3Xa~k%#SF;7|rwUGZpzUB^sPDGgF>NOQ>GB6aGGJffaXB@m9& z)zka11H2QH(XN<6^_Hn)CDvh@jeAFU&o? zJ-HaHS!{c`*n7a=l?co44JF_8vv)J6DmR8M$8S5Ws%l{akZ=3Kd(ZnfBKmH)zk9s{ zm?$zh&cUk0N@G~5F$x}05$S;c5EYR|2;y%5jG*c;2c$<5!fb`dCd@$Rh)k>H3K$eL zlgRafJuq&amlKreI8>YvSz%BY@))!~HXNZr0&End^XFaj<4Yux-J`>^lh=m@Hq?F0 z)7K|wuP$k&UR)eL0}4sgH5cb6N2lO}1Z~_s0HfA$PAc12L6%g$I6c!qC*?P9PmfPt zbwWOQeFDf7^Z}H2c#-!{4lgf>pz`Yc^7QQVFS<(?f4;apIfEsA^7;~pS}%`Zy=yS2 zo7pgTyU6m_qFy)FP_LI#LAv#?!r{K5Q7c%}X!BsUF~ct_5+$rc(e5>PyNNg0Pw==& z)JNX#zu#YgFLYHruEuFJ@8@@;Q9qr=DVPD1nNl>Gepx9FMgffJwJ8>=rlgrL{^H58 zZn0JyEJ(%3V~(e@!E7%5#Bn>CHdX|epo=U(RUpT#$^cLbu})VS5p#Sph}AcWKD75J z;!y;B0eyN5lb;--NF;f}Y1>{D+%WSw;Z;Sqo5Q)*dKOPz24OOnpC9xTTbo zY0({aSAl!8pm%`+uD?8-Pp|*_zr)(szf4vA4sU;fA_)TO4vQf`eR%Q#y}n<`@jaa{ z&AJ(QL8Ewn)gMo;_PO+A5$+!^R!f_Pw&BJuUX@KvG^V|4t?ntV?>*xUbw#gi>5u#~ z9{Y2k!vsyk{gBbyy1l=9k9-AlTfWX_8M3yLxb9(H z*a<+Eg)I<;Mw+9>7S$SHbw#q7Wncy^xPlVIm|$0=K?1_g*hG96m^lc2xjB$%YZ3=A zLBn+(TH?qU`%U6HO5C>8p3M?uZY6KsjaUTzJ=2d2f-rer9OuAsNI{_or=lAfLGB~R z+yawP$}Dx1^aHXof5Uh9o?Az$iUl5+^n_YW(m4+_;tmtS+*WtIyTpgUjY{( z%lc4*0G>09poc>LT9l7B{=DjKH22JP-bVFvgHa&O_Fk0jE}I5tinN28?KO!jbVaOa ziY3xL`AJZ5O&a$(G)e_MBWiz?C_u!T=W+R&x^levNyQ77MWm~bm2cHU#a}p1SPHAO z{Yozg<%Sx`nq?Z2~|wxmI5fQY;d6$?7Z z;_PUM=icoriQHQ>*1;*eu7NEBs%x8CX9B1jd+N@G6;K22tUO8T-2wHq^vDG$W*w-~ z-t7L}?E95w_VO`t&YB&-Ym$x{cfa@RQtMjo*dpye^x?2IvmE~*IW!7Tmt{nNxIs)6 zd~P-rE<|JV6m)4u?y&9!#abznjevuc)Y6bVp~_yVL-fAL!Mn88OB4>Y?!f3n!5Cb< z2pS?w+XkZ?aF-kxqy%dKP@bP2AD_H@fByQ_)6*C5eKz}j;i-Iocz*uw^!OQ0&g0XI z!zV}!`sw7)&)yuqK5p{(o*lk~n~k2^*B6D$_D^q4PcGl%kCSG2>BHlNKeyH8s=Tbn zC;WSHGwyr;4$Yk(LeZ<|CTZtg1h|8ns%wxpsidY>vH2TnmFx8eEZ+W|Wqi>6y&b1Y zVOagX30rV&T+$O@#Wqa=E%x@n?*ouWoYd#=Vy!9-i!0@1R|q8k+wzs21vCL{H22hEMOqRG>IihslFR?C!flZ`xHM*3vCl&W}2u1tF1` zJBMjHQ2x*%fVY8$O-*7d@Q;hZ7=6%KCNsW&s+^)Ycon>kkdR@~VDW===%<~%@_ztR zIsXYjAJAw7Fob{^M|i(Dn_R&&sSZ5F`KIE^CC61h9u8IR8a>X+-3j1~i|2CZ>mlH9+-5X`U%!&zv96{w11R)ZLjS5+y01mU| zJA8f8 zyL^7qJ3e`O2>zE|u}A-)fo}HHHUTUgOc|F#35uhzZUi|VS${*oy-xrVI-@>~dlzrd zoV_fM=H%g{NNSuTERsU(cXo~rUsi_mr<0TOi{9BQ=^B2)Y1KP9KfS=&a#0;u<*LWa zY14%7IGW$Y3T?hzR2TqxX218m;2IRq{Cuca95;!Br9LmPnCPa)Vp!@fuGAlQ)dr*Z z8wE^AF+K|VQ|+f>;4pyxfCl9ea8oSB1?oRL2NK#ShwD%P^E&9;3a!S1h~I{PBj`-^_<%Q`uMBp$z#b%KE;MH1G@ z;p?O4z1JrfSTz?tC^09!r>|d~RpnFdxtPq#5@2Wa`O0XP`0jg;3SIuT_RIcbt{Ear zvBNR;O&re*BT1+T!*X?{ExXD*Ox3U{j3Ts%7w?Zq&R1Fti!b+;dkKcs>Z7(umtByg z?dq4?noX_fT%r#Od40~#enr`7vijEEP`C8f+H7lDT5G5A(ly|*eRqSE<(lgQFqe`u zouyqOF#37TX^?IH$<>AM-fi7vBb&C5TMBbAfMr9AR-~b%fg0V)GV{fxv8l5kn>$Rg%Gq};h6AlM_$&^NjJQdY7FJxeJHUHX|M zF)czn^*t@HwESF*A+ ze5GndU$>G!PJ)74@)KMxE`9k-IbH<$H<>k)_0E>NF}+jlU7t0h#0#M6{<4eE#|ZWi zH2WfsBGqZix>dwrP+(O$*;L>|H4Nvp$Bsq4YXAuUK_wzJ_`%h9?~Sf1pQM8624e}Q zdpb_07}wF&9FsgnA(Lu}uUq9(GkWDU%3*_aIK)JRyCLGm(ldgnwvMZ2ITtJVN4nwJ z^6@++swYbK`qI{|;(;iKd@k{8r=C z0zG#s1r>}^!YxP7}X2Qlxh|b>1D&cMERvKWx^9VAEqK}X>>c&W5piIaP>G`;&RrG z#oF4K7KcM~PtFU{dxhpLEi5)wcyAOT5LbGYE$1T+8QI-uypElLv#tP3rjHIme@X4t ziMKL4+&DBL(Nq#?Ymb8!QbX1?#=FE^^#(b!T-R6!8H-ATl^C5F_@1 znB~Gz!2+NNzL*FFK|on{10R+oDsaFx3?(1%6u8M`S*KA?;X`4syMoGW-k%z$R2D1x zycGjn0|liKsO_yBJd92N1wRZC2Zh1m7i7d=BSp?+^scoa%-m{9&8?=iyC;hDG+6|U z;T$NemK!nq%mtlD$jEw}(^jNGe7f8IZ z2hIpefxKpPMl9V#1D#nh)~CoL+`nhR?|pSrA-WPuy? zrrL|{ugbt?9cn`xtrgj4*$8IL2?OuP(I^_#3w5IDhd)9I21yBi>`TQ`Gxv5=3f~PI zZ_C|t33g%9CAc0rW%-g81#Hn+Gk!|-rv}`xPTRzLwz3XXta~fnD}4-bP9q840uCNw z#sD^kggGmSeC+k15iorY2d%@B?+~>;(Crd4h>?7rA{b>Y!|5R(osQeBbT)T`4_5Dm zmE!?Gelg*PjVwgQeiTTaAL^3;vz>#E*KF{?b|1a3;DyJI5By})zDMe3#~HW2Txhmn zsYn3G0=#}mfCsLX*aNm`Y9Y)R2Oz>sVFH8#$p|?yz_|hlMxQQo)Q`n1omkch;j=ea zD#PqG^0Kx{XXRwwLR_}d5Yx#`@OCNB?9 z>xcZgB8sAugDG5ynA+}virh-7ai=VN{rs6}Q4`R$2|rg%U*s(l4n>>Dk<*5!-RXoF+(&;QJRmw} zruXPyaMnmg15f+ur-G6TN(AIvRsV|L?i0w)a5G&hMGu)@wM!q*Ks>VrA)O@5EP=Sd z1DOsu3#8-1dXa>|q7}*8Lw{4|OESq)1f&?@S32uPfPA+s#{gSz4P}msnEX)SCSNVZ4_QsoGhysch55+R%a9eSUN|bHOK;l^aCdcQ>~e% zVQLz#MM7f-%oT3rArS?T{-D2Rkr`vmkjrZR@UOAs0xP3V+a8YR)>#!ukeUq7rS@1$d75P|ajN;guNXqCd6?`3! zR0SQ+D8yJM+T2tru?^kOSRAx?M8k@3idRvD1L>(+}C#fl1xukUNagee`S$yittw^wT0*wBa!G15{d>Rtr9R$JmnKsogu$$E%iY9nmpkI1=pN+>%1!c(w7)-~k1W-zVBoYtGNG<7f zN2yi=*uDzUm8salJ=5oOq8ls0uMi1~U5NpXs3B!rhx*CB1$1 z5sP>atGK_z`503hVgTtD`$m0|^t8Uotjg3Wz93^QabXHYa`iFu$#A+D z!-`^Zf^MIyk=(Do!k$W$u7*T$;Nl~I>Y^zY=5(KJ?(A&qKDr>{AI7jv6|Dced|8@6BLMe%<;TyQB7DV+9Xt zmY5kRBN2_W;nZ=VN5TF^m8Tb4ZsKHw+s!g3c5Dl{rly6YJ5b&L)je4b<(MI%Rkw2V ztjE_V)sL#O=K(;~^?}a-idWyV5(h=5$a;>$I0t*$8f1B(%DsH?iWqiMg!y`RJrDx6 zzjau7=g(gOs8!w545qdvmfY8XfLq&WI|N)iz#HVO8QHCIO|()x`S)ED{(aYk-<|#` zb#r*6oHPScW$cHDTRU)7IYDZ~F)ExhF95DE7|+Z!F_HkGY@|YGA&8-nD20^I`kF9| zz`% zjJzpSu4SM4`0E`i@^+E=cBhi)uK2X_$2UyuEKIkeD}wn)O_*`b>+vTk8AdWz0yR)M z6N@XSY~!qWzhiknesvrX%%rG;R$wIjz5p_T!Yw6UMLYcMQ1$GSfRR)$##SPJ!SbO} zly`b`(Ufed$_V#W{hrd9FOJ1%lCIK}!js{iANJmy!l-yMzfxu%@Xb@^W*+pKbSRAa z-ZLn{x9Vdt=~@-r(XznpcdPr`6Tw=Mq>Ed%k5Y|Viw?W~+Rh4XOXs_(vg=2#vl83Z zOegi#=)2yg+BdJ8yU}gM_HW~NsIJ`iZX2;f?W*nt9zKrMBmle?0)v3j3arEhUX{Uu z4v;S9G5ESt&n9;$qC>#)cX1LUAc^{4(?&tavV$=|w8mP&5wRbZT`}?zMT+Az1z@4) zg(y4mJr95)2|SDlM`m3MJi3*0Xf))Le?B=oXT8QV;&B`u;hM{KK<6WXC%-&Ce69a_ z_3{$jLm1%oJX(!%RB zY5qM4d9z0Pw-)^t%yRYha_4@HRnl5->U3HqPN!Akbk7>FVmm@2A1nK!BntBk*@q|q z^buIH3~-VybsiLAc|(F zHK~NyR==-VYhZ*5tHtoE)*AAcoMvk+xjq)t2B0mp4&Gcm>AizjcNI-azFn#LllufK zeOla}0)LT(Er?DaY%?o^R&|4I^|kues@b;a;R+AVQm8}0u%IMG!7F}}-ZpitzXOv_ zW9{6aUIq4NJRVDwZT0QS+Z4Q#gb#)&&>7#dJE*v2DDJ=gm;dVdy?u#`K30#bm7kgKm4!%gZw~)Utf%v%*Dm=;wO|meNaXj)PME0 z!di#XRyVbG{2ZIUW#N|7n)O&L*_w?RW2A zUF@psdO$-(hAqA2PcDvkdruHVnZ8oSuGFisG*tTYI-1@h_6XFi^V4!*v`{=^RU}2_ zW=)6thz)&5vSnNa`N){s2}&+ zK}9!0ih5lzMO%x9+N}N6ZU4R1X7e{*+g;rQEhAJo3kn^|PT~LsUapPuq0F|@)C6p3 z2xSob-5$K@_&>P(3?h&qFEjS3Y%QsKju*P#Dv$A(se9^JuBdxptwJ)Z8D&sG-M|g4 z2$0{26D9x#2aR7-YEehcu8G5`L8DvfOby5LubrsW?gO7s<31|3XB~q>YswbgEyUE_ zLQLH)kd{!^mc^0D@J{OZevJ8!VmtF(IHJ5DK(xon%^c9T2ogoP3u8YuGUUi5xH@u{ zi=$u^1h)II+3!vrf#g>a)8CDBn*FdpNm^6$?JXqK-a?|`fQ>O2&!-X;g)MmjGduyi zUY72^AP?OB;c%!Dd^O|lY@pv?V_ht_THa10eLk9&e~P#kGE{AAcLBw$&|Ia(p1`y8 zVzN@SKkZm!QCHi;i=;Hws~FD(2rM&&nZerL;?wBkSR5`<*```pLNBM;3EOW!L%9t6 z3F2MR?|!Su^JM<`Hd*UP%IEp8Hnm5(UguM0x{>VIQ3z9kM-{Uw8WFhR#D+J>@>YhM~ADZrzEHI^j={5mAz$LR+3RT+0<0f zdbOl$m@44@;jK6|+=V_70;rtc7y#Ot0`k6Te`&fJIVXb=*#DL&b7YqE`|h&5yR0Nj z3kI9=SvNv*y>0{1)S-%%axxY`<>DdgONzj5DUs?q5a%o{pQ7cx!`e~|G@oVSI*t30 zklmDHR&1V<^`iH$X~iY@LKW*XM`pL|-m4cU$Ln@bl?qd)+7LCH?XQ)|VmD{G!CnCX zr-*^tZz^7&9z6=aUTN(&yt-9j*zhyYW&)S=ad4%p1ENWXeHxJ8Lqf+*BB3sj+JmGz z38iGUOx`{C*q@Fk^PAm*!E?8Q8~qwy?<(Q?vI|e{>y|;YgyQ@p`aF$DcKi7Q>H>_Y7Kyvg4hQY zosAUMJohXx)q#j74Lk#+H$aNcfVpJlV7N0fEAiqS+1!Z-o;cKJhgOX*5lnIbUwz1Y z?$U;+f~WdXl7oUhc75di0FVaAPX=PW0WyD_&`KXRpn80Gc_H87605oORmjNimvvifbq|ua zXW3to>HJFpyinfT%HwO=<~_Cy^B!A9_i|r@i1Or84FO`Dn2wjaN#bMSIi87x1~W$f zxNF1R876>RCRr=zL)6>qf8ME2?Y^RQ?zL;CU(*KquU6sMtXeF&&NV~G@0vs7P6EUhOa6S zRv6!l5)8b+(RBt#4pGaNs(ZEMFz79VC7|3@XxyEhlV~b+2QYGpPSG{IlJKHs z`Z~O*!V%A+8*O^C6#8l+HR^tluk&RfQgl zmq)`-(cM&y8lFlpTHL5;8h;vQsLN;& zG7Q76RCG%ZKV?!oJk7Mfi-600kjk=8aRzU|M|S%Mv0qVI0#so9vfKNaNh%npL4GIq zu`+*`2Bmh@DaW%I^{Svb(xkI`3h_oyS(*HNz|xhw#mnp*_HzDkt$v_^S@uhyY3L&m z9P&|R4bCtuFqz?M!1X`ldYZSIi7$f*RjHAt4`Nq59lF3~!V(_#RaM5*wacqzCL~cN zQa|2~2jYAb5?u6u=Q#>(gx!o)@NkF!urwu0GPMwxiWrIPs0#332C@=_ju&okynl5m zrDJ(>I*Wk8{itoRJl<6waks_<(rzOf&D!mP0&JoL;2Al;WR{vLATP@J731$%#<^{; z5y@HF=5Rytu1dz2U#pBQ;Pq6WimT9@HbdzE+afbGgaQ3PHP&GUYZ$z=0*K-ngD(-37&Njn9t84f5S>Fu; zdikDh;h$@FOTqthXhQo(a@&jX74XlO{1h79P~GsyrPpgtA*yfcltlz0=13&^H|o;% zd?J4{$TTQ>j4nw9_6l z_qUxotvs3*Ne#B%PI1rH+bQnp9;OSBV(i4J=?5;7i@|dUqE;jGj41bf)I^f^B#crM z9(fcwV&et$b`EmG3>=&heBQO~Gyr0!;XNoP%I{A*RUC?^XZYgc+Xnk>gZ=$(u-$_f z;y_;wTgcb7Hd7F9@!kg*ecAx&cmno0iXNz z0@;<9?3?1rsF%oo>4WJeqWU>9=Ub14k>g1G~8C>&7XzcXpRhyJE~2#p+W_0i zR<3O;+{E2xuT;?z+RH%&Sekh#q0H^vz;AgFcnQ$E0{{NTLxtX@}02o z8%&>O4xS+2p!k6rDo<$X8nG9+APNdH&x>8FE~pkzh~0-2R71dp!*7E6H3ije&!0A2 z!`wnparG90imSVOw-XzQk3_%@gE&Y{%zYBZkTx6TvQCx;;50+_v}q@K9DxrL1>L9( z&x{CsRw)0@>w*e&x9w6Oy9lb`eiPJRiDm_4FnM%?`j|-{wK>yBTe!7vR}I{y-DEW* z&yh6N1_Sr#`$yCo?*o{~ZX6kLnq%`_vv_Ot}C zk)pM$yQ+;=B&6s|(TuLp(T1ren~)@%`9SBn(CVBXNYz>o^@WkHzRFR^IG0LB^}4$- z8d7QIMp@F@K&*0}OjmXORwt z<@l{fN^SYR_gjyX)hjz6CFDoW;njyrKJ=k)BRL&e@~H!|LD$Z~fr698#PJgOrWe_P zZQCHywgNyMBE!TBmb2-R)NWaBof?}sKJAWue>0$AEt%dkJtqCf21vR=`U!Xj({f_V zi;yFenP#w-JgYWTe?#W1Mk#4il6|AIlo*|*WDCtW$Jrec+A;#6fR#q6YecXDfz(f! z4hV>Y;w|Jb#TG;;uwl|=zMFus64YjCT}liubOP_8rKAQ9`}Zp)W~-EVou$Ox zU*%d+L3(jCy!*Tg5vsrM32z$|Ncm($2Rw+!D32kz$+Cn0bRH+ z@dZt%F`@5!iX}1>bv-Q#;NM+m*%0numCBCce~03;lvpA8m|CN<6^<_6a$b5wKbVrs za7+V1LGL^o7e)95+ae`^2m>Pw$`iyp()rCTN_FZL^y=d|VqaNB9#rT8DfCjQgn^Yf zgL<$M?uvZ<^9O8-mD^-y+?S8!mT-abMfYEFNW^UcuLA0 z31s&z4?WfPD%^c0b1`61egSA(4uyg?0k}Y>kd=UxGR-82Ns>N* zZg2n&dgsSa8Gm9kkGPfJra3~LoJf{+VD2E`s$Zxo83UG<@+EJyL}IOm0HR8>W`Ogp zxx*LN47#BvLc{*>>C(39G|-K)yCf_SA(@PVASre{yawYc@&HIcx4#O>a6C_o72NWy zciex}j-!cf4hRpeL%j+rI=d<wJ*mC>pJ?f`%{8$$;No5khU=0;Pnzh@5vHdM3;|a#} z9OuflY$ZxKRcv3K-i5$*AL&zJFj9TdD-5e>e4oxivyl+5QVrV0q~+LJoIkabOSoVw z^TMM^A4~XwWm6h#x>&C*llB@(4l+gyDN1;-j=DdVEdo* z{90%th4NC-NZL9k@$$vdr#hm~yz&ss>wK=Co+z6wtnerI z^T|F`Ddo5Q;d=Ood?M`-$>|TN^Zx43e>ywF|9;%)CcT8a7X$#Hew`=paqPbTFrMLr z>f|kLxp&oRs-2q8_%Dv+-iwpolf&1Sf9bvW^Wn=nYdma-V?sR*vUvEZ&t z8iLyjJ=^AyXtO$znB3x-SaU~b4@H=`S&GOL0=Pg%27gfia5~VnAfk&brGku!hELxDtu5FQTeVOKt zp(e^Oz#Zg-c>re_6ZivUM*wylLWtNkFp`1c9HPR?13Z3e0*eP|_#3-cGYZ4(cHcS{H+&FcT(oi0{j%{wE`cJklX=Z9(cD5h<@(dZej*0+~g@13lcbx z{08JZQgR_v&JVy{Zn$Y}(KxPY8{We%nw7}fw?*@Jv1m3%1G0}vvJwQSe!1%V_x31k zYq*^G^Q!Nkjlz1LQtuYS@k($%m!1Cdy-p85d8>G`Bs&78D>JgMm6M`k1QlrCFnw+n zhA{nW_3DS(OZ6RdrSRdX^61jS0?P5aPRLL7o``=>oL@xzP3igQgTzVF)m6#DZkZ__ zRiJj6IWp|YFqIVuV_lYT%^ZF0dq++RIGlP53Tm=B7yfA`u4s2~k{P@98d^gI@m68cuC7`2CKOMAN}-n!!p_e5 zBooHADY9S=cK%!tWJ%il;!Tl;L(7zoBe@Tr;|15&V7FkvBK1|WAnrOQk~e_6`u+Z6 zW)hVHg#mg;0yhjcpYg+vdeREx8yUDrV4&#UBn-0Dj1F#8JTEd}f@6RFR>6sD!&?!O zO*M!$^1sK!X(`1@s7YiFza(I@1)dwTgNEivtKck7@Gw1H6uIk2b#^4Q-Qozxg;_;z zhEr7qGrghDseI&c@F6P~7aV@Tub+zp8g7Zf&HSePO{0w$R}Kd^gBih#2>maijUk|S z5M9s#4mfb{uu>skRcXhWGRSkZu6l7h1UNn_Gom5-bEj9j9vO`hErekr%xdHYq|n=0 z5W-=!)H<9%t`DC)+3g)Zd4c~7^K8U_ZZT6?f=@(@f=fza{s*xgSjPY&^4kqdhLhJ?8LY0Slje?d3W<7+=1|AH>OczwCs zLwepQ`yirnw|9=vz{}UqegfY9ZtvBTR~TN-76cXp#fqSlw`_uo>D?24Bb|CRi3hm< z{0O6Txt7r&rD<{V#nbI}H zm7d!VbV*-exR9K8F(1LLiVB`f@$6j?XX`SX4$=JO;A+hOjaTZz_6A%(rnY!NGQpv{ zfE2tLQ|vF#_pqyes5X2$V#yNgsjzaN=#5TGF`l${Ny$aO4-=8+!*sf*)+Sdeg#JP= zQ@qRJNOtPO^Kv(kjy`#IqE^k5F_6vh+E}C@=@?@ze2hMS9&l0KEv-W}rYECY1^}>l zC<3ou;=}kCdEjC-4My}t7Ypjyc$~iDznE3L$+|ImKEAtALA&Sk=u?JHoIX2wDUz^Q zK`)-+|Knc}kHyN_yQCbJV=L?9?1CbWcM>7|(~%P6LY!Y8VZ~h>{>cCQ^FoptKI2L* zW@jPVb(zkiiC)w4Pc-_R4GIyjNc-e0DwcqH?Q#hW#escQSq-y2nV1XANK{z?_<-c} z5Ap$JgH@5y3qnZ_gd0A{!;0j;!xIsY<}QGPpNAgA_voT``1v!qKp@zE!Jdt?uPSqI zGnCT}sni?}Sfp5P@7&gpu*}AP{Xe-^M+jKrAI|uIixVr6Q+J1Kg9w-Z2$w!4i`SgN zR|z(f=^jpq&-fD)2gXla^Ah?uX+d9}zL7Z_aK4opt`E@`J=qrWDLy+p*EEZ(mAL1h||HCH+PejY~30pUFF(VnoDZEgYo8)JzTdp z81D-sWS2s{#r4~k=T+`(Oz5qZ*MXjP+SjkKpN-`2{pl@?Ih-KZgWLAvI=jEgrR|tq zT-wr3qoctw+Z_#6d*i)hw)+=q7k1CYL^VI1$AB;;mXBOpf|A7J$BZUyIy+zLykA{_=sYS|7b)By93OgsEH zwLQlT6C;k}%n0oJmwp^QKLh}^`sVD-3xYHN;i6Mn$l7v`R1wdN=^c)=E*+j7|aIh8i`>@_Y!ScN-0iTLR1#5}yHASV0Agksi`=jS!Kpq|;0UwNZ zE)h)6$ka_@H`2V#)YWBW3Nrzw>Nt(>Dl9&;0^&H8ihEuDzXO*7^tvdPOuuWPdD2!S zKn@gr)S?5sJtkJ&=w8BKksyh2R)9S0sOR*hg1Fd?xaHbS?BK;bggv%h^2{WD_Ds=41i zlk-6bmd`l~Eyr=?rmf3sE-?Et_)s_}DSN%IBydkPxCaT`g9PrC61e$@-m=a1I5X2a zss7^a$5-|EGJoZd{H;TjdDDr^a_$Z%a+~8nTvL=;U83)?MB588Ygcy9dJhr=yK;oI z3>y{g0V~-}`1Au%J2=RO50K~{xv0C0{WMAiOApD;A- zwV1)W19@e$A}x;*u3`SfJDZIsK+#@p=a0C9s$AdR?fmm+>?2pWqMle@5^8b# znb5xBb_=K1M{&K0r%T;FRU4vgV9<9bBfZ;>kn?m?eHt7b5&dB=$IIU@AXWxU4ue0H zSxs?vHx?J<6jm%eakFczcjj!}12yDay(hP@-R%-@DtEToAk|6K^Ga1-4gdbey|Hrp zeklVT_P>Vvwlr0mTBzWNh{pNi)w4Q|h~6N1aG>J#UD-xkIiiU=9&gjW=yur`rJd%T z(cKslZCGpXO?P8lwE4EqduG7&08o1wC}KxaL1J099R(f$TT|0<($I%tV7oR-!xJO* zP*Ro|c8H_{R27AefGR9i9a=yX41@Zn8GaF-pxQtxo2C`S2B_XpO>c#uPR9oCp^6;_ zjDt*v%rP4_&9&q8HOqyzi>R-OsXO-*Am@Gp)E$r#(QwzKteCk6);9+L0zf;6A~&H} z0nQh|AGVDX*G{7l8DA-~w}3d~reT;RUb>u;f-@Ho2wuI`%v>8?H`fXf4ydq>@7qy; zzQ+7It^8j1n_v5W^V^*-9tn9Ssl9B+42{eHa)kkoKrqAy@MriL!eU?sg!Y2+LCk+`5Vmd20aU)2K~<*?~nb#@Gg?Ezk8pK$J(@B zbHR#^wH6=36&0*i$GStLcDz#FcQ%e$S3_qx{N`CUJ45^)l%TV*GPloPooWpPQAhaI z@p_Uku&jiMS*`C9HWz};iII3Gl&x?~_Yo39$oXo_;52GapqJ@jq@?RFdMH}j_rsw8 z)bV-Hu~eFNB+Mji@&GZcqil)+Z$bf*5~`d)2E<8ppP{fy zJwmbDN(G!0(hZKwv8e0Frx!=fs`1N`V7EmBZn$8@36C!Babfl8_1>uQ{)IE}VZXW_ zJLtPBcWtTduHN5W*FDAl(ff<+C3Ob@x0XK^+YGjxHWy8f>2?g=0+C829ZsTU?-x^yEi1e|whm@dvz z(@uRrFd0b<&71rORt(}@XiW}IuiUZC<&<~VGhFy5R+td%btkF2feAsMBUS%I>VQ2b zwxQD5mJOJzBmuC1VVbGqA`dZa4;X-uQh=HS&U*Lg@a*LE;UW^SW=tPa8}F=|t(Pb3 zUh-t!OP;Lm0Vp872NqOr$PWH_9V03-|!(@QuW&NtAE1jc+?tlSFN4IlADfXcZ>oUdNqr zuj9_>e%$5osGB*6mN79|0xwdY;_S10&rQ6HXQ1Urs7gs^ZkyknVNP8TUHpjPB>0pQE-lgS!Y*VF$UFqFf_@ zZy^G1WgcYEgEG+Y`JQKIB$J9`8zq83gavjiiUo5UcE7N%Z#g`{(1Rqys2Dyv6ySgX= zJI%L)Z=&=?C!5_WMNV*^lMQ#>LyB-$_mj=E64x?})DEZz0f!N=X&E#p&$1v%u9ez8 z{Ed(e(%RWZV%Q1TL_u$td$DELYL`)<3FoQ(fKucL=x_LYJK5F=l65aZvhF2F-A^{l z@$v|#8*2S@Da@gFI=#j|Qb#|L5#BkC^N^WqlWUmxw!9BwDVy zU^U~$-?PoNC7^^YXo&^^z?(G$AEv8`H-uPI;SJ3SmFXMm8JBB13tFqdSgr0{zavEEV#7YI%}JICV%Uk@WRg4%-`z6mNcZej^xf7=7Toe5dWRDguNtDdgb9$Eg$NRaS~&zYfK{W3vdU&9w52YbjK?>E7AXNR zDK2Cfh(G=S+N>NoNxKTeQA0&uRmNDOXX*Er*H)>W025^HWl=$>Vdrj8 zlmayQ`PfY>AmDk@G7i@WxPSL@Zd$)`cwzaYjng2%$oG9q^2C+h9 zGTBHR21#xf7)c%wSUvY$DCIdE3FI;NkYn1yTyWnFa-_jrFzxAf%a_<>>jz@tYScQUMVyigpfPpKYqvS*I@i zW>W8hHMCekD=pS0`7`b*x7~r%kbRffIfN~BBHIDma~9@NltBQ1`<^iLm-+Y_sq6um z-@+)5trTu65CVj7sb^V(+*;1V%B@*X;QS`H4=1;Nd(N>T)ZX`yTikaqxy5~3%Fg4g;f*%Mj&3oO z(KONlFBgq3o69f8V=2#*OgpLYQ&doxh z!>ilWO}$j42}GOe399(8WDKZ+@0P7jOLJHC7RJUu_AY2XE=-d}(<5ak;<`OmR7ikz zF-UfGZ6nh+6}S-jlQp^$OuGSDk0l{f6YKtCPw2ra9bx}ezTj;(zEzK__N>RM92!B_ zKpE#tq)WY<(j4I|;EChO_arBaiF!2s4$lIt4yQ&!uPZYi)_tk)|!zS;>c2{HqO_VePe>ib4UnCB_zjHZUNjyI(A{X=O85 zWAb&lzqQzt^a?0TyWpMe=9<0HTywhtE3}zvZPiK}HmUb*mD+(@*wmr*O-RM$M={*ofq_V&>jw8J@!X;&*SY6ZI`^E)?v+E9Z94G& zCx}P5USeWqvm8M$gE?px$Wp_UD4m|?8bGrJX*FtPT^lK}h{c1x%3kJ9^}$3PIuAXS zbtv1fFqP}oBH-}>=^ga}N&zbWtf@MV!~~2r6(V?@7957t7rl$~)0f|USJ!Y(@Gzis z4Ms|-AiWSsHRq!hjv>M!%0Y^21-2p5JP7r2ktNOs0h78CT>s)h;sacKUw!VBoD5m_ zNACbUa2yLYkMKC~jGFeApDUFM@YT*{ceN+?do|vQYm3vNe3pe1eBr??ups58YpcAg z;CW)lrCLO}hdG9&CmpA#Kq=@V7oNf2l>iRPn^!nsvq)8VXy?14-*{x!AQAR1I<{zv~@A3_p}85hLNlO651I4Kr~- zYjsC+)Bx#ZOu7B8HgswbsFO_aRPFhW3euj0&yi+MP#JPbey~WDIPN2hOXf0QDqd(MHGU6V~77l*F83_?|MzWUBb^8+?1Ra znqWmbqTZT7k&4w!Xo~7&vOP8VOR$l8P8ql0wPZX3Y3dcZjg*w`if>=q!DvAdx*pG_ z%^k(CUMZH|?3YMtjr6d}3@^r(H)J=8u0vh-t|Y`!-xsr5djmD1Glo2-(Bc~B_Bqu% z$ntSCn(o-6!Pe5U?l|U_s;bUKR@IWO%ZcDHg zJl4ikJ9Q>jnbDDk<9i^@OidK!@L-SP5Shu~PYoT<%Nx%3a%}GFSy`*p8GWmDMy(Dr zYIT^=?&(Sqi?<#4zG7gcdtr!}NCur7dbbfIL6jw^X+ptQh}vIdOL_@ZbflX6|LncV zawJ)nCAP;`SXK*}?v;Q+=fSJ8D&YX!9T|?H0C;$1rldf0cnVm+rmz9-h?b`GH*nW2 zz2XOuD{e@x$UP;$p?`qCz;o|=Pfb-#)l3Zzk0Hady2D{+Iy`-o-r?MHfbY)y7=}`% zJOGR+zUe>Xh&mjx^M39l+MH%ET8yaCVno{~50W%PwgH9unGKy)$gTiG4ak{rtps#5 zra-MWK-6%P!YE0J_dx_%3^`eo7-}Fu@%2a4uQKERv?IFOT1UX7`!J#;Jz^Z<0hPVd zY+wGqjzz~bS4z4z@u*SZ_FjSKdI{LWk+z&t{XtTE7Fep4@KV)S|G)xseEkLSb=G_> zDP%XmLy{x-s08ZRR#Kvoo$(z^F0IS@z~XuXS%dBnYq;Or5s+1nwZU0@DQ zIlQ(sF7lDf-7-)znJ|Rg^o5dtWQz$myCj)1YN+xOdVS{3FZ9t1CU(mo^ra%Mzm@8e z%ta@8*(W_D7}QY9`|*!dXI*y@ZcAPcULqf0$PY^sl0OHRDB-pqqz~ltupaA7;=H8# zE32uJ;3xMLOi+D@WZUcW*>9o;JTy5)6n5N|2Dk+JG(0*^I-~IGbXRL!F1$??yYp&W zP}yrY6t^T-Q?c@KL^We~4t_{22P;N!yw6u-S^%j_Ko=%f0=jsH;5y9^sY5p-A8u)+ z(N9a0o^d(6`k0W9sRQRQ!xOMSpHR&DfV+aOhqx{Yfszlr*z6YSw$V_{KWbd=PLeP6%HrUdfxEA0jcHy9`N2e-#I zQ#>$sBRC6*4_X%#odUa%3O6$Z%AGSP;{Xs3nfMS~R$dT?rT`;vY=Q9)Vse$3Z;0wI z;DRiF{8lf$~NYJk+WS?k*T^v8p5_yXVTDmvTGT_|YKdh4p@Tz$BF zbATH!{eF6IaRcJti}z^h)h-b~Dmh3^jC(tn7RhlB4=!)sd^mpJ^3bb`W3+F1>ivW% z@Y=O!HLg8pleOn;vi8~s>4}X(pthUV7rc27RMF7Mg6$9LiZC(_@(vu(ZYDm^A0op7 z@G$3nKTRA9i!`pTJ>NuS!Sk*?XU}rCYj3;up0M`J#x)0~8dehvSO()SOIv%03i5#vx6fxJWFM7T* zT}xmyouM3!^z)E1{FJEQ9*adqc~GQSMMUVPLjUNVAqN?5F+D;VWQDFOrBlW5j?<(Z zdRjand|DDb!!NI73zWY6LdrgG)<^2XOHR|_Ne*QPUg-|Z<#HB{epPI6js=6BUXNZP zoKB-F>CTYR29~Fv&Ltv`YSKvWd@AEfcTyLR+;`5Hvd9b#9StmM(mJHh(_J)0UsAHjxV;Ul-=J){Y zf>FK{P-DW+oP4KEo7X=Tsile@{&8JvbX+qR z>rWLNHvgE~-VNn%1<38pq{P+L-U#7B@ue#0^oP!T2z&fHnw)1I06;$@{ z9&hM!vqyAzL^>er`&hN~LJcz>?me(A^m$sEF$Bzjnis;LTq2o2pqh+}5I4!W?Gd@> zR7!>FL($>5hoDNyd{W^%NT@MYsxJD(10?hul4i7UvfS!4^fa&S#V;ISEza4+8bJdQ zdq#^QkGI1gqUvJfY zB{YS`hRRt#kqXU7XA3AST5q5fU&JIKSAGjGCKbJpRh&D~+_Dw>Wq~%TjdvpynZDDu z+rE0CcMI33pUrgR*__fAh}&b2QnbRcl$^PfTbkTUBU0Gy$--nTzA^U|L1UO@7=0{z zfM=7Eo$a#hk-(8A~47GJ1?#ja!CdH zd9Ey#E`M3W-ote}yE+CFZs%p=+HakG$nAKUOE@__-aVajb_}LOL-t|$oQ-iuC2wXw zT6*4A`G<8E*|4~{-59juBQ9O43He0s7#uc#U9MyKum%Lw;>h}-PW9(yjiM$e^G2V$ zLbYh^x$R3j0f-`fgwTvwal=Yo2nKqI<2aEKla8PV8es2<+VV_lf_7$Kvaukek z<>WBJr%w(eeEQ@t+KXHz&|m;fl#9A_pyk0RZHgFCKm#Co5VE=`ey{{GWtkV5t|P)2 zl8-3r1$k8Hf?g+Zzj$)AV6dx{`~D&(2O4F$&6A`2^JbHS&wb+L;B(tkWr`ToNuiS% zo*zX~;<}*81Z`>v2v_9!D6=(9s)R8UfcEgw0XuLE2wxbwpmt4-GM+2IKC_JH^1cwE zXaCG6M_c(c@oAJ$5S;(03a6ERtb{0~Fsro!?BzE}rt!tf!O4>$(OY#$=a@;55PpM+ zDC;TTT%TT%6dJ5tm;fZFH?5YPHZ&P@2utOr_)kYO3_EP?G`p%bXzp`q6fG`sC*DMc@I#!JbVZjQzSl7qbxx zHgO|dW^cj{tso_Xg$GAyufDz#hYfH!GOH1tob648lx&q<)Z-dYT?@dsi-GCs7n`E1 z0~Xy*5z;?@JjboXZInEA{{8el`*zfQJI5c-)lJDglw}Uk)R^t{77<|GIH{Du=kmSt z>dc+xK4I9t?h1y_vkVBybEr|{2jAlw3Iuo? zgvTGS>&KKzG4>3^E~YT|)p)F(lH{X*hW**Y!7))I;i@~V3L{IG)p2_SJw)hFMk+ZJ z#ZS-dQ534efa0N7C70w#3B4}4o(InRutX1nOZKpTETA8lO@$Gpu{0JnE5m++}BAfL*BQ+f8 zJ@^(3L!j~vl6|le!*sy15khSQ43XeC@-6UHBpGFIi^Pv8t%X!-<&|%LS&rCWqJM$z z35dC&YXZhoH*f$J0p&8-O~D3YL7%JUmH+zvhvRExVBhWLf887$ot~T(J|CW+ogE)u z7rwbVJUFY%1+UHNHoX;pM(~AI5Z)#pV0q94 zB!*GYra&kV;(Q1R=gnPoyOfJoj&b#{Uk*L~jE=~W91wzKn@X6L5l0!_2c z-EelccbDKP+3=BoQGwf>KomMa$&!hWfrCvo233~fTc8%oprd0Ysq4jI>SQzvW7D=` z>&KX#mbIOoUlo3`Xzv&XH_9vEPJT}AZj#Scr>^>Y#jmrO;@E7K!ZuAtDlYqV;@^|I zZq>0n(E5AeFMS-(5RNCZ8F&;|pQ7<}S&6YwACmiD)X;A7S(D)q{GWa)l%kvr7A4K{ z4#XqF*7azTO1y}w%At$WpG@6KiA{oruoEVKq-u?lW*`eRN@5?nlwzP^1PWRAx3c1$ za?GPtp0dlSe^oaGCqR;}s=-pA5|Ztna<-Bla55Q9*4{XW2~d(m=Ig|C<-Ih?>x9^q zA}JLv&s(|~34)U+vA7*qxR6{TrEO7^Um%TDNAx&-Gv?puf&^uet}#&+lRvRImaoR{ z8&#ty79FmnBCnfhW_y$eUAoLv7KTUTouzDP1Fsj zEz$(J+=EmihG&DnIOuS+0aR;K=@w_i62Jiyozad|&W^^ChEmesGC=CpPK~Fecb$E; z>nKNSf5~|~Sg$j`qHbo>0-Lq|WVPL`T*`)rT`sHF2QGZTxHn6XCH)r|oZmkf_x&H{ zkHMpBOux715Hf!Vy}6q`eojCA;Ex}S;O_GT{-r)=$$@m%DqETexf{<`gk!D{(@}R? zV>4A>PB(noTGOfiwDwXc0$jOvV#ZJ(1W#k;IS|$Hk`S4y&@n*l1F9Y}i@>Dor*3Qo z2}pc=IHPt5hqS`tXxo8f7}m2E5UY1?k;DgZYSaz^T#am5X4p{JAQ=wG*TtxEFrsFJ zav6231mkKzB6yI2aY^>R!b)yK zo5(TD=bf)!weoR0U%y6yglcp8cz6E^NX#y|B1F)+D3EA6qT;1id2ed7k4A0w@pICL zwOjR5FW9I@-Vd(h5~=Nqc6Ck6#^o%dyopRSjtiGAQ7FJ3|5gFv-SgqW6%?mIXNED|MR~+ z2-)HnkwOK6&$Le-pu8$y?Ze3-{`n)|wuk^w2c43CCflH^1|-6jkm>VyFMm)v{oD6g zfvu1J2tJ9obArIVxIVn#zW#7@!OR2l$$&Jyp2NihKizQDIXOJN=m@x!rS7_Vrv~qZ zWYS9?F|iQt(4@D>)RB8Gf21?sU0+{xRHO>PPp! z#@Gg~P|+a&J>XyYZx`W_J|$TJDD6pVTn3#=_75F@fG0xa3_0|azWes#i~y(%h*$8< zRO3`78l0IKcv2TTOg=x8@AAmXXB|()JRR-fqVhBF%F+oZS)qt-wjy)b==+^0O5a^- ztaj{h`vwnCAHI1WQLf-K$veHD?;33qinXV1npv#B$SR4H@+nPeMMSjyuH&us3oU1T zZZW@BgU4q%E3igQJL(PZsLeQ|mFKmWosVM24j^mk0HG5Z9?DIlFfmfF?b=|GiXn}O zn0ew6B`=u3yvWVM6avpiYNmi4l}!@JIwSZVZYIa;x5zk_b2^R*atOmpKn@JbV6aFS zz#C;G)d`kh#Q&RZlGg`^-zf8=T7gb^D1+-G1Uyw>Kbvuo6IwLC^?RMi4f_O^!gl<^ykP`7y{=LZqTH5qP*N803I4 zL@jz|+EL_)z%irpQpc_V2FPH$)VE9h7qQeg3^A--=hhR~x%I?#-kxXzfO5xxR+Nu8 z7c_Z1s8j<@1P)ekMqpV-P-OK5_#8+r9V$O*;JY4{9GrU7$s)_FywU?Fa1DFA&bRCQ zm-0&ASR#7uVmF_-*v%&{_Vz-DSqNG2BmpanlZJSiXQ>121pu!Q2@g z)+lUO7!X9X9n|D{pj?H%bmgUvN~CZ@(edqK-!Ar_@TI=tB6l0pTw(a6MUL~Hw8(K@ zdu|IP}Yh{24SxYPfSeNjnGcPtQ0pye85}e!yJF$h#JX9Bg$j`8tY0;q6HGyUZ)H5Ok z(yQ*Y2h~}7yLh*Y_m}7+uUNs}6IZbJ#1-70aojac;d{bQfbVirR3l}+fbt;T;3D$y zzsLse5wMm-gd!3E-gqhb%g`WnL)!?0s*mh>;j*aub_H)&@Xy)tb(&YM^Td_wJaOeZ z83S<*g9ALgX4aPe`>xwh97chMk0fc>f|WX1j8?EBEU93A*8h6}(-+KTF4VjhgEy9d*IrPs*lUP5T9T zbE>+ZOqH_h%43!YBGzb=5Pw9y%BvlDQnJ(hac&6*NBH)`JJ zC~;>R^T`7^sk>DsiCouR3cyAkX3x&Sy+=E+)hlz4QQGF7}V3AuEh5o{LNWhgt7-K4BK} zz0RVfL6NqD(!H0?QOmM8O9^#~A)S-%gQkuDAYQtZ|6!2>=4dFY)^A>UaV6zIWX*-- z!)qwISaoFE`isLmh_cQ`V^oY>^1L)bB)qR@P+ai&b3&ppf}5)rVw19_+4a{d zYf>@edSW=gx%jzJIBexkabq@Wh^;%X{kxq2*aEc~QDpg03?noMGD}8gnwbwuIFJU% zaHe6Ir$|_3z=?!VD{}=BSYZMOyYy}c(X`|e)znT$=oyTU*lwIqFpnMCr&2giIb^)m3wHL4v^+lO%geY50ELS7id%W*f{k$;yRN z+ACYr50^-&>nPJylRKrygNP1(EXd(Ml z6FN&7CvAx0T(mEq!%mt_t3~aK%PPMmnW$8fsx<3CGj(N`*onaqjlt73#}g^wx6{$A z&eP7pAc7cK=ino%p}z&wF(l(-7VNu{r|x!6jwW!8{_p=)8oUF#i6k>7IystUQ6Cp% zUIuNFjI43I1#qZRyLU|`n@}FYs+C#?Zx$`qyAjF0Mj`Hp-}yN+8dsncN;q4 z#YBLej^vF{VXj7(Z%s_gUsfyPd1P@x_hy9PV}aB@M= zX-G{3*))pgrJJEN`}752s8hd|J&oGt;-Mr(85EfkE~JOYl0tY2{ehbB=wZa{&*~}Y zFQx?N`Gs3VeWJ&H#MFf8t+}kR&PTI+yk!FurUgXP$9NukmOH6`B(DZb8W*FvGN&ig z1aguFTOGg!`>Gu{rk5#UC3Pj7&q|AYc%>oqgcDi?#37Dl2ZFVRp^HV{Wm4Bmjb47) zh+W1scM*4~6Nc&jh`dxHd>d1b(wUl=fON8G$P;2Lqz+V#(~@**Mny!EHyhz{v0(Kl z6!Q<6UR?4Bap(nedNfIA>CZa*V}8H1Ftl7W{fuJ(C9k zGD_e|L|-jf+L`5CbDrzj3voT=cn-zGLh)?MKhpJigQ4~v;rIS%ObSh!n4q$UDJPo7 zRZ$OueNN3B#gkW(^};EGhB8IAy&qxYxWn(a^AdK#~1U%Y!a={zQDoy?{#+4+ZvJy{PdC3t+(GH=Yo2 zrQT_!qM%&0a_7Y+6p^eXZFicua6*fFNJJUT7cxVrw@(C`QZ6ixX0yK71N{Sd{}2+u zrD&_)1BoTADe6WvhNfb!#Y!RoniCUGuXoD4!I*545Tfom!5=ba-OwbW&2c_0cj_2V z8`0FJ-q!s6V{D6aTIzT6Ti&#IGRYBMFdlZ7?=?ds=x$h7QOY7{is$pgu=g3)!>-aF zP2u~?O(PecQ?JHdmbD{BQ1IOItC5>jeks;IWZ}kYDe-M7A_a+oTh^P@%L(lUZwRT_ z(Mu>5Bvi={$nU90Ekv~dWXJNxmVoSGLaKvOE0YFhD3vOCveG1E;!|w{P|{ zs1^>aC01rjWoP$ha&MI69MnwkMS`LxWnH$Z{JQSR6+`7j+>wF-y~;XTWep*wrN9za zSbaQ6X(_NK>~3Kr*j?i6C`Xry=T*Z>#kdL^a}-a}QhEDA8)0>^C)z!*C){>NliPhd zmn@XUDNSLfm8UgEl!%xT(!7tuDF!XAhLY6Tf6z~mM~|w3AakuifN2^|n^2J1s1YEq z(3t31GMR7agcTW{`V-n?8j}R2x&S~KnQtV}X{Y=RRH5PsVIRW~sqMz8N4}E}lq1F_ z@lq55gS8Q4*KSfSDIk(;Tjf@sO&m!100l2A@h#yxQ7nj?J9b3s0FDW=>&UVoY+!gb zUe=cYvEZ4_flJe{H=V8arn9v@4hYpO!i4%1D$+o~ z9y(cS#i78Y1gV`J`+(J(m=A#1$50RmGE5m4aRJN<0kZZmwSI)zN>cON+4^;7Yjb4O zur{5o)~2(yeP|uR05PN)Q9A8IXwQjkfWbWnOvEltdegQ=h!{I*V8lLT8bxAKBox7R z7s0M9Gk%2GYE*e}x3l$2X6u^NF%r`gfsg$;#p>>&zEJt%OHS=0sDgoj`9sIN2y*BkuP zXv%JZB-Ez{kk@jD;h#}beA6WFJ36s*EzQ;4_9@@edF_(DCCgdQrhPs9t?cc}S&inl zKCWtL>+k;nZR-e&vcb@XL=oj9RvX$bSGTQa%(I(Q8}btM*^T8Dcf-dwWi^_PZ~Zce zK%U_kfFv0vNOL@JXM{wwA;5`++yj7dLi&VgugOV@!_iZ5@)1V|-5X8cT5S$^LI7HcLXu6n4 zHW=N)EQun3>B}x1#@e?%cRSO*V5U6*D!O{EnH$bEbHll|0WuVlZQuh6ebiqFuzz|a zL>)u7H;Ak(2{E-u7AvHZXOIhl{|hX+fPv#b#G{{;bF`u1nwGoW)t)@pzLZWJ$)yY^ zuR%PdV1G)fLmalq=hpvTF;(FJBd_bOFjZYno7b7D$TeN->r+ONY@dq$RZ6CtK179L zkN^GT4e~g*WbXa(;`nW6++!tN8MTENEGL72N{lHbMK&ZQ^ziM=hrgZW3QV0x z%#%fz$n+fds}9Taqa$?`agI6H0G8Uyy=lqn=EN3D5$h*9pNJ9Y!_}t%?AJ%%bS&>B zPQPGcWKO|MW>T97*%~ASUh(rpox6$1elNq?H5fspUw??k`!r+r^5@;-Iit)`~7hA38NUvbXnTD8+~H`Vh}xY25!=ekqITlKqdvP zs8jzkmQu1fdLYJFJb(R*`B(HwK9HA@OyG#c2=45h59KXc9GIoJpeDxhRG&!ahf<9r ztqh&d8hrJUWU?W2^@69`qnbJU@SjM8iZHb@gP~Z`>6=qfp5y3?>x%_3J>aw_jH6A2 z^ltKd(6iD?P-&0i4Ad(~3s(1jVEXo4;H$u9enDANUK}b}y7);y!SQD#aVMl?CNHP* zOf;RYIhRD!7x`7v)t)j`0ZHvCrTa6!BGN@lym=1L!W)vMmQaF1dsTjm6u*Wu-F)?^ zq}M#>gf`N*k(MT7g*v)6GChn0DV%{mNom1hW)-`o+quH_Nm*A`dz0XgMI&O;*5>7f zwKbvyw!R-v0KFZ_<;puX$vPM2kA`;?r_WxeytfhJxw5yYgc>y%8$-TJ;MDigF!_N- zr%$fr>~0{@D4&MYh!HFLafvMco@d=(-nx$QDH`uNwsSbyd5C@Nu7 z;_6CJ)|s~@z234>pVa_KpN+EmYIAwaAt)~xQI3^DTgxHlR=3-IiJ@f`LfN#V!;{n9 z&V_nZ{Ek}@^*YIq)4d=wuz+vy4W`@>^m-A6LGle!1&%`rNZJm@c@9u_q!f8jn?KMG zr2266I7~2?hp;(vVLpjMP?Jl=Fcytay9B8fDoF|2S?P+4V_r;0a=2)-kweA`e(IX) zT-kq%ab6s(739>mR&0noi8KoiC8&ho_Go(+VW^Zo!<1fjd=GtbY>4;0F=w~uW2JIW z7db%+9BYP`3E2W_!15vFp(=mJNCSB{p6hDnyz2Y~ukZ)!uYa-s+GPPhK8;(KC*EJ_ zr1u`Z?(rXN^$e^h2mt^}K()WV*+qjtKhhg*Pra9DF_t%SG*j=;v^&#)P-b^1C*_FH zB|}Icsx3dAM4zZHcs2-1m8+Iz|e2yCT^ka!&4 z+iMSdEyn;i#w>*<2l{ZasMP?`md3b_5&rWDs~0_@Wg_ID5Ew)4T}q-+2{Ce(KCNt# zYI^pD!1(Ym)BK^%AWcZ%wixw8bL3^IsaG^R^2RBvX%O`lTqL(KUd+fwQWzdx9>Ken zTv^zIF!$vOQdiM)j`qZtwcV)qC$v<4LCTKk3MuTF?_PM^p@gC3E3NoemO_?-)kC#` zT;5{oQ;$A;yga!kBqbT_D51K#KEFhT6CcdK?=B9#gaZ8rN1h%ZogCbpoKnHm;;7=i zPY=%Ua@3Dr85|;H_Oe8H+x5X1TBca&^!6B ze{ud=JeHp1;NlzkhF~L3X?t{}0q6d;@Lca@sJ8e>Dwd0l8=H-^O%Km6{&aHoR>`gK zN-FF;y1QN$uKVHgePzPIaQtF}`Kgw2!9+$wtW^!kGBO4@2#n29Sp?m?*`U9C^A*RI zZfLK%At6cxy)5WD%l9b%T)M2Y@;Ub8D}x%R9N+1Fv+G*(a2sBr)HL_Y$2wP}d9r(y zU!dS1H6|`5dz3Pn)F&_AN)YL^AxE+FbK6?96|HYof41H^Y^w^?rXNevK$Uv4!lf9G zIzrW(yk>K?nXxwa;d_1?jFHh8BjgJ-roroH@(oT}kqU1XYR)M$7J!fwDeQ zS#K!qHqVKt#xdbOPxltz!1VmQ7^dIhpQT8xJ zVJR3)gVgc>ZZS+3C>Kzz0SjY-ytuNSK_%`5?z6&Mu%4@|5Zm>{estAp+Ejjv;vAL!QtpuqkQN4*tFXr7 zDHlc|@>9UV$+T1r=8vyTh&~y=MmUFJwW^-7@kW#y5y0qiGIQ-+Ie{c$geu@ORjJA8 zBI5J^{_nCah#=0GDdvUt)6}7=%lGmTn6sb%QX&lRD20XVaPEYjglN>SHPott~i{0_2QP#BG$?b_|eezZk+YF$DmiWLlnNUEqQ!w8-5IVF>VDwGs zGXT2pl1dV={Q=t350e zG{q3kGaTY3@iH4_NfsDRV~Aw?A__2`77CUUg9yq4(}=7PMNME%b+Z`4d-$R>wcvI` zPdj+tsfFm5^=qA4O`(@Y$;MW*!~Bw|)gFb3I-@v9BL}Rxwi6L+ZkGCxTScv$k$~7M zgkxtrnU_QmV75$@9T`pp*f}(tW1!2u>eRBC%YOqDW{at1I=_Ocwc1}u0cja!pSuDK zi_$SDepB%P5W2GVOmj7iGOYIi2pz?a)G@9*kB}x`f>CNJ8-V5R0Ak3`5c-U6r(o&# zU`v!5+oi&}@eq~?|FodaQPc~}s=?j_-9Qi|FJYQReIw}*sJqFUodMPH5YP%-3m{nN zPz4OwP322t#d{yM^c^rpyUKzyw8c&>xNNCCP~pj3MLe z&PFI>XGCU^Frp8`ZO(Km%q9d{O4#>m3m_oJDIVw$31{5+eQ!Ab48oBaz&bF`j}@Yc zkr!a@q45`;-*-%l5OZV?;)9R0IDNlwb$+M5eff{3u{axC5%P4?Mnt%?l)JlNij1R? z=S^U~^ubA|o^hIwBl!>9W^%=zew#k{Mt z^p~#EbUK$OHQV~_uLGP)GUx9K#(p(3x6T6Q>eBUjxyI_$#u}00%DQPTNv$5%UfdVL z2nfZYh$M0|Xo;bMI*Y)_2TFDkffxB0yGMqZJVhVf?X=`O_9Vl-hi>`fY#12VklZbU6n@6u${yOpIx~g*s(3A z>RJ$)U*j;CD+QL9-ZxJghV>-|%ALOLMZ-QeBOuady)LiV{H3_|u*#d#t58Ig-;}0L zH6Zlx^>4~!zF_(7cv^WW7Tz_?UzWdJvpO5`Abdm!kN@RI2FuKZ(HzFpO~zcUr) zDUuY-3?1`c;?l3@z5Z+uH$i3GTr)2~^9V!s=Hl|4E#GTIxf!23oV}!b zzL^u$ojKePVU!fth#^S?e9rZEJiP7qrjj4(1kqK@z})JkY}LOeZdwR!HJ#boNU?$1fG8d^!6Cu}pa4NQ z)O6A`%@U~Nf!7vNc_{bH0y_>o6j5d<^YoAgPGDC$aa5Mtf-&Rz#*&%ht}s)ql_0If z+q__=M1Y4yuIZb|TSIuncfG_(QPyceWXSXr^GmEn>&b&aJ>(U9XieI7j0I-fQ7E7=3ztk- z2H39rq&!##z*6RBgJrE8EU$)XZR5dO?Ev&fx--A~9=rOmtBDx72uNE0tBP&xmxoVo6zEuP^Fr)TB?Kq}F8(lgHf=QB?QDwo8pu>Eea%2miA9@qA zz4Wn5nR2Sqn-7z_B|C2iI&`vJ&w%jV%Y z5bTa4$*yxR9^(;}l1f5V7@6R#mG*P75!d=0b%>&eRu&(fJHQQldwlKP{Bd@12}}~q z{D~wh9$>k1*`Vq{S^Ko$cGi!FS`seS8dHrbA0zF3{qTdhHR7zf~yy~d{>v=PIh(JfnU$@@N1zFVdU znj0ou222P)kQ@rC3_{sow6Mu^Y>6 zusGJ9w}E~}3Yu2r*-RtxW5gIC&0_ilz_x(C7C zWf{&11I4zCXDqkKU(N-4gTl>5X2-~d#}FQJk+pPuWC#P1W|kS%u@AO`Xi79Cf&+g% z<~TuJx=`um9lSrjyoNa5+0pq&g{!6GzS7YeBNp9Rn6+{H2;aGxxFbyZQXRWpE%9i$7SH*TfT#-5`$0{Do9XJ&8*n9 zpoN@;5pW;y8)MH!eksa??*p-x02ZHk0ImhV?x)phZh4md+|%3)w$uElnda*gZ%OpI zJ09F4a4}n?*$Q7)JZp&V*?VRq;cWhMlV=U1R}56n44{d>{kOmU*N$iIyUvo!T{ucc z%8$tsCPJ3`sBoUjNL^l>mIJtWXx+k3XN+d2Ja&sG0;{EuhKPZqHp9Tv`&0C)ChIFB&sL8gl}Rr&-Q?=V4( zc$97z<%i(uu#(EXTgLlo`UZo&JcmJ`T)Ar!IjyQHj1L@GGsO6kXu$wDUHAyNb> zrBS9j(^r^E)(+N7QCdXuL1e~@@H&O02^fSHzltH+EINNg0wmXu$ulX3zx$m|ci{GK zpwaaiArUZ6i3fQR({VMuLU-plzbFmgUGMged?=9Hz^fH9umGVCO%HrGnj_^jnGdBp z5W0;Cg;i*U*|?Y!?YC1UTqEab0oPON^L*|uCLm$_uOJQ5L*nV7^LjLarpx`~PF`cc zF`wJf=(aCqN7;{~bvfNvF6O^tEf!YYOR3A1w*iC76$m5B6~e3D*IZ$8cB-^t?o%th zreV5J65`z2hZCaTf-Z}onL#7C(sGcp#UC_mX;PbWbW<;JRV_ z7M&!Im$$PPtyNSvIQA3y@Vw;sv3V&fO&&bzTrnSM9x?PZ0l|8bXpDK-ikLCtz3Rrs~DYu;y9tpJt>4vo!9(eQQ-V<}uToMn;L-Ud7@nX$uIX*vN zx#bb(-e~_b77yfB_Ay=P|1--5S~}}Eiw@#6A~!asY%oh&jdhGdw)t=W^WXlDzx|)8 z{g81I{ig&Keu^BU>=Al)U1-rNtGIkxBboKLkX0LWzPTIS-}Ofi)w&EhAoq78`msiU zc)5;Z9lL{Vo9zlIno?4HfyO*X;gx=aBUj`#Do3_=xNHFyR~rcn|b{81-z;<2OrX=*5xxy%C2rbyBUeyq#y4hw!)L3bVRt*<2FLPr3E^pJ;G_)D?Unvt&e_+nbDuV?|nBb-yeV zsQN|8<2qGLqXGH5xMUUY3)yw-&12>)0~Uj+R_Ybr5BO!PmEx#uX!+z_r2#mRpb#yl z+4;abbw=n~va@JwF2p>Ml*nkqXK{5lD~2pr$RT~oQ1Guz!Y9ecWTIrc&zoscZk_&| z_wzsd%yD%z=U_k8IA(pCx2Y6$W8PEE`41q^jAPZ<2OpIt=fc98{gx~%4#^_Sozqkj zh=p@686k~WRFEz$TO@B@XvfEhL;SxX8bgYsKXAaj^YP^Ud*Wk-ZTnd4mETmavR`;I ze%z0YZk|?$0r&XVb$xREIPOV}M}7k`!61_y0@@?F;bxjsldL8ZACnx2yXqH+eF~O0 z9E?aGA;(Mc$ucn{4FU1Fp*w2kp45laii}JsM^um0yHhPF35$lwPx-V_rh1IJUB03L z0ZP%9;Ru4a1!IVzVe%Ois=U5cYO( zmYu?y-gI!(rJp)NRm{hCLbWui%8jWPs8zR(Wet-YnY;4{8N#fkkAAFNq!V1|&?9MJyJir(Nof0t1r0i=Bf3bKf&YX+ zH^r*oFbIQZ9TNj=5cac=iPz{}Y&0hJMq{!;CARBjkRD5rv`n3l%TiE+9hl@rcy{Dt z;TVAxpkUhtiG&A%KOm*3dw zb%-dlR((M+tS%{qDc%hZXUW(~E_F?IT`J`SIysL?x%OeHy(ET;|Zc|Vprd3 zSK*ex(Je*EC_>5|G+ibmA-ypvn=6%)VKC*l#A_9!D6v^ezPhLD5H&|P{le5;^7=fbx z25;^(=WhSRjQ4ctCzK8BGTHqsXYXg@5K+#wMBOiga5+csRCd?$P-*-emkhiG{*3er zxWJOV6W!l@v=$u4k>_IxE@Ek9#rLb#yQ%&8d2?W%WT|QY5$AnV8coTy&LZT9$L6&M zR7k-E0&JZs8k)i==J7x|`h55>l?OibNR?ZggG#85#6fu8(zKrRBpo|Wlc*~6&UBRY zI4eMO!Q6jrPSIhzCGmAJOQ>oAQD-pfBdtdPK13{~c)FZJY~7Q@M+)uVp#Gsw?d6$Nf>f z{|;b!5>OJP*3nuWFuZ>dZ$4qJ~_@~^f|FVJ-CU$ zzI>NXf9P9w@csS6+%+cOIS&QXn>L6;i8+ILuba7dJfA?cukzz^b%fofRMs}!7Ui-Q zCS%ke-$iC40$;u_R?sk%-Y+-${nE_5Ph+oAPnC6JhlG5@~m>CAt zTty7q(J~^`)*?D;gWWbZVPV4Rw{c(@#tlprsy&zLX;<1N{(4+h1#}-ma4?D>bd_ac zLguX*fnEgqTi{x-GHV4(9`WP7*PILWBzfw~k4V0&Z(hIuaC{A_x_67eZVrx4PtHo8 zkxRcmxhfnDX1lX9Wb_N)Tpb>q?XD7-RbTSWPd~iBM*bgAfaTxhH^*l{697Q);R1Ah z$2aF6&yFvz-kn^uB>GeBL)!o7cmpll4F~~iDJ$wZ0&5uoFOP8rT=nRfJ{K<{zdt!c zM-;EFdP09jXI;NL|8RZt0nc+IYwFh13~F430#q#9e62AQ%cmAV5UQ=0$LP3cq$SK} zTMH^e+h}tkIoWG2w}(Z&XW1KFRV}Z>^MlK)!1VQO)9@oy zqYB51K*pScMG;lPfgcc4bCSe?ZHdH3Yz)CUfV6!#HX;PWE8B4x0QG8bv>kivwqv)L zUH`J}c)Rm%ci!#J`}e)`1}k^oV8fj^*l_0!+6N{qfzp)-syc`?WG?Ex0yl-owP^;# z3}7a4l3Af02EG$PX-8Pdlb{@i!W03j2$E`G0!)#?Gw-~}FNWKlcf0fcoOj;se)^U7 zQ-9@t>TkH8`Wx=2?e)qmq@H~X@+Mvc%%~0gsEhCd*l=Rg0L@C`8Ho##6G67FA+YPZ zAx=mfVS+O;i_7)1V0*S5*9f0`KQ*@d>2^Qe?x(-P{Z!}Snz&|V{;lO+s*Z2Hm#X92 za}fk?2KBZ{qnP;Ou!OB@sYvsrAI5%{Zo&#LUD z4ysrE&4d+|9%mm8SNMpz?;JeU_TuAVOARq>& zA@B_Z3>=Ob*qP%8ppS{Z_SWkXUcm~sTkm%3-EO_xt#?xaMm3tuN4^A+=5H(Im8>FK z;GNnSAr0<7zUneg8u}r<0gAl~uwz}L^gIx1ex61IHRk^DE5;df3yb6iy6K0qW!TYC z|A)G+d9t96=nx^K0wc_L@b8P#vofd0WskSONY1itvqyY0_mRAHR99*zR-gkcY5 zJToRLsx1^>#Gs?`oKX0VAEu0cKyhwalh?%mEYl0VGQfAc%C@WQxqzF6RnrEySvtIZ zlrW0S(56?GDG>Y%EQACt%XKMb6VdWY!Gnl5nIGZ(76lNe6Oe{MX>;cJz;jj?8Rc|L z>np=Hw~K7M$es(MS6ns0W{a#nLN5waCv&i(5QcZ4@)9_Kk*0v>QAX59@t_CJ11}V$ z*yBV-<~blt16vu?VjvV*;WP+BALpB28KAdaV?V_j`!Sc!7sIiNYr)@a>1+VTg5X$` z*hmS*b`)o+gR)uQ^sN*Etav{`oWKi6hs-B|G9w94OAvvI1F8lh2{Tbvx(e_=yvx2a z7Hhk7{@$04w{q!tn=Ku0v!&y;f9u$g9gd;-VR{*;q+CQI;241{$4?F45gF8;lLYnY zj*FG$g|QI=fMYlz3fK)dam(yR(EP(|%YELZ18Qw^yL5h4OUGTgbllCBj=R~?X;0Rc zrEwBFP}(!B3~_}>Bnd?iLcEClBy>p(50MNfhR7w}HNgFbPU<1Zmq0ZeNQ=0#bWn8s z+z4QB-MZVQ^Q&4q&dR0ZY_@cq&6ZAkPQ4_uy$BI)FQHg>Vt8iag9Q!9BP9nUl z1iiW)_?VXnp@mtLK)=Vu>Ii~VKzteHdvWTAh6{;UfcZ?6>7}3;5@G0th##aCMJte8 zfDyxa-lbzLN&Re>&fodcu~se}YqO|J2i#WW323tL_~$mb&B{p?F;`_}n&EuEEE24gcUgRvQw z!Dx?Vz?;Pk;k|f%=0}-jVLd=k&PJs;vOEH%jR;IyL zW$CzfXg#y9V^(3}+okjOzI0Y16pYOf3dUv#1*1JeLD(RAHo;PDAvcPuir6;%#6;K` zp=j`lgAm8DEtIvzS>!}GG=xFt8&Tv4&j-tRSXnx@?>={g4NGSmCiqn>o#!bFmFu8& zRcPt(_Jk#X4hYOV8?sSgb4QpO_4Xl9a&82cC=Z^G3k@k>WQe$B7MS@W(@hiDbyk3v zQcz*;gE8lZ=4O>i?#4x-&$VurE0dnElJdp!OR51{TlDjzBNM))`lYr%e~s0Q#Vx?x zY{%F@dJZI>NL{%>?4yi4BDidvgfXPNA<2<}kr^~bkpU1B*qLF)l6C~bBw3I}5rDb& zSKl$}Y8kgX#&*Z}W$zfPi}0Y?N`-C7-B|TxJn!{Wk_nzqWkKGmBPzvt1ftlptQEz1 z-kxo&EY72AQ;`f`yj-Jqbw>ibR1`CSyfmvcb5lC9Ewbn-FVZMi;jMazu4#r#)p)uN z>!45ts2f?jE400-;7zG7@9gY#WIHp|fM|K+Iadpj2BAE6nja-Eg(?%O@RCBq9TXZt zDmOo5Jcrui^ll+{%mQvGtHV&{2lezfsQe?nw@4RYb);G@#LTITDa8q=5$P#X#ZyGB zd_d_<567`0GZb-Aj)a5GsFn-zjjSX*6rWVP>1w(?(_GeXaS^)gqWTYAL(+-qE0UWQ z+?BLSqyXWF^pcB}W^^=)4acZynN43&M?w#pLI!eD&}v_f+*{VLM=yCy`eOa<7T3qkE89Yr0)yT}c}Gb(M0oXoxLKZNvX^Ex61z zJrfwA(l5&vw7z4#1#LEA{8znFZs?U#Z$+;=t-b&4cy0>dSCn{x4G5SO1t4V33@brl zav1pr{tqM}34oQ*PY#XP7C^OF5tPlGIIF;skxv27{t{oP_eEhV`LZt>)1G4^ZxaAb zkY*|JIgWrlq6a(P4&&HuO$@TiG_Em=8?4=hVY<>fcw%&Dh0BHs=d74;!P6#O@U#iH z0sh?q^w6;#VE|Z#QFI-j*usIUva<-?fNM&e1;za?uT+h=z&^EP!U`8ZOgO$XPj^@gzA@&6u-&|x7x)N^KLwk z=Vn(|-h(wkv%S>+R&gs0aw|={$*r`wg40~(Rvzl>mOaV}wMfnmp_Znj zub|9Mo|7^;;YrTOXva_QN1zbFnn#1-Uz3zN(QvSjJ-tIG;nR_#>;TKj@E#8AhcVm- z@-KDc`L?go>lnZRWXj1`R`(|Um~Xce?h~ zr)a!*GNbc?M&k3JuVxTagS;#}@0Ge!fi0&)BIzBc>+G;xdQ4*JaO1@*7EkAoos&$G z!N4Mx#3`C>MeDqaA&+&@@zmp~I7;|8{#)|DkQ1ar7TwOJGXD9wutKIF5t`1K>?JPH zz-SH{bj>G-+dpEVh)l98iAQGlA#xwY0px4|oyIV4PEN#wIsWrwL}{cyQQMrD@4v<$ z`<3l(1^>ya6I&9Xth|I`K%qWf%YcH=QG$@yJM3H-{9%;|rF7vx=$qzzC`;5MYn?pP zXd@Zlm@YLNNf7E^J2!MjgHf~MjP`sZU>Y~54gtL53DDD!l{742yB7R42=1W32TEtC z*#_(&vxqbyNKvjGIxey%C|W234IES|_+hn3o4nOVd&XLw{>a~>1$zeey&WL;L5&1Q ziXCRIWrOe67od?$qHsf?L2kpWGg#W&wq|qk%WN#-Y8=hRI<6IGvPl-kuYUr2+C<-&+CdEF|Vt~+lAn+tDavK~q9t>TS^q0mTnKx+K8_ziYCMV3@ zuQL9Hf!`n>wJvqE$t-Dfe+<*vaF*bV4QC0?XwT)~MlPfZAU_YE8QiWG_=kKS(zCAb zqJk+2ttbPE0w~!yav@La#6jemmgfPA1ilBaGD{o?aGB3IORC2BbIy|GB=;v-QMm4>60u8-jQ%k4o27nK%JTW~YvbT)qotPz4U^_8?EQ3wc#^=1%OSaq6 z5oI&ZblLDVRz#rz(_a~dzF3fEA8XR=C6E&k9xw|e6*WZ=i=ok}002gCxK@%SQ$$gL zM#!LAVZ<#xUDpDla+!pj0)~ZH+xPPxMJ|cfd%^iVE%Hs*J=6{pluy}Y>HLr1&quR= zqLcT~M)?W0Ro%P083V$>Ne&DGK}0s2x_F^Y1Wj5@lAT2pck?b9(jyH<58PXNrZ6RV zkAmf&hLYKvIl4bZ#4Nruiix)yCgKS9E!?fgodDf>gx?p>S~eRc`!-pZb)7`mBD&D9 z1B8ws3mKOY<7lErbBB>`b0iX`V5RR9?1#ep5yjv2qX{PHF5X`9w~q#c`H-GZ?BoV8 zM^WhrBDy{}IeOnYxj59VB}(}zMWm6Ah|&kdhiM!*?0unf?ncpnu*k zeN|`8SzWV{U4fDJ?c|UOv;T(QFD+$}s^_&2mVv<0wcN<`V{n;9Az0>MwLx|WB1y2P zAWQGXRB~Xa2%?4H146}X3BU)v=kL??t^=zYT`W#YlezH-1 zx}!s8$a-goj;c63@OI4NBiA}S_NERGnJ-_<>Djt4TNmc%?!xGvRTD)%J+BIQA_ebc z$t985jlq0M(4C#CV{rZP@`C2ih@dq&Bxr)c#Wf!#@{2k?>(jog9&SegY*cHxpn^W{ z6G}?{XL%ZD62~S%Z99C_qW|RVL(_&SWWIpNBK@Lg@TH`p7e9rJO_$f2jLs;HiYs<%E#BFl{SId?-1&a1ByG5r7j%i8iz;ic=QTDMOj9U zkYjtq5e>2%-y-TXH?_be9Rq~spo9Y?xuJtfp-_~qXqU*=ou&R*vJKlBs=ZM+jj(;g z8_1$~-j61|`9NBywLdMJq`;pvBEemnwPw9ZTDWA{_&h|~URhS-e)K5u1`%&Fc<&}N zxfPd8%oeBQI+OZUJiepXAwd=hyD;l^4hS8459sueOn@2oZ8BT1=@3?Uw{*>zVBfoZ zF9tpg@lx;Oiy!s|c#8q}yxY0$&-Yh0q`K$4TiLqc-XN1&gT15g zaDNFvpT6oGq?29-pu0qKli8vn;O@&^{Q7_w>n@*opnIb87K7Ef9*z3TuwXV^Fi)-^ge`W#47bif{>MFQ#3H=*kHCiz)Cvs_=Iz?QPOV#ZND38K{c%GB#)Fx)8Oc9->(>V!w%-pzKwB zbQj{F>h`Lo@&e^X(fquNu$l%~4Luqx^an6da$yK8BsxKRC+Gr<<6t1z2fY6|0JFAf4?8}W$>$wF$Ncs||YqzApY*YC|Bd5WU#(R>PEROgf&LbXAVGpDLeuA~0_ zYm#@BH3+G)`0HjzKKG?74ggr6z)vx4XSldZ?9lo0&pz!s)X#WPsvl<}xf_lEZbyR- zN-fRHH8_<-!o9uWTuST?$oIe`co32hLRE?AlIO0jub`nt$vp0lN$}949c~XNN3bg8 zW67o2@7?ot??fMJx;a)2{ULRn;oR`{KxgDC&-2?``Wl^%%?dM)PQ2#=oSly(uT0oF zW^KU3(i9%wJ^rS%s3+2@#d={uBAVO605;l`IG3jOlERo03XZnCJHXvAJx~UK*&>%P zMwFr&a=)M4lFBh&Xu}9)GM}geBI-)47bzvo$+<3-+`z4tJy2*?hVGuK06-s0efqDR zLkV)kQ3DKB`W-pITl64m+sLMA|s~ zZXs|exxezgqbOo1prTq%8y9lQV_ulFec&q6HhPPhJbLVx_hlS$Eeoz-wt!osyh3P5 z*=b~(50y<9{m~5a&X`hf`}ZBA>v!D_3CGj+tIa|!x6Tz&Tmz+x$aq-#_j`IfEHnfw zj*cvRGsQ5<8Y?o4b#yD6)4Zn%Nmkf=!fT^g5J<0R`SPK_CT~Rbz*TE4aP<=QB38ui zw>>KO0)Y&ypN8;>I<$4|)ZJVLJCu8{Uvv*v#7OjMvm_aPLR__QUSn+Jx;N+?M(OA_ z8txtPSxaFOc`=GS|KXOtp?fseIJxUlpnjVgC$||0+El$=>rdTSO;y`Xhqun&0P6|O zNe6XFX`DH!DV!Kw#4+V+kQhNlG;}GU`vu^g09|Bay3jC6!B=dc$|VwRxw^>=%>YHy zRUTDyRk=1LTvoHYnbpDtP!I%&oFG#S1wVm&v1554PmVD`!9C~IWpLJ1B(%b>eH|Xa zW)c(2+iZ?_o6Qk#1KcnYvYrJ}zr=-fqK)8094A)bgeZ|m2nW#$%oyQAV*~(VL@cZX zMJ^$ZbJD<1vdSE>ZPzi)=bIx-@r7UZ99bQT!M24_Op{2(UlyO2S>1v?nnF!M7gGvK z)CFDA!)PhsLPq}WWCRnYLxKDeXDKEr`BBQu(qH81F7m5zjY-7zIK268H6%kt&SR=^ zmiLs!C=@@7lE*#x?ZiX2%U0E%d}T<#4E`OZX!QMXsopI)NbH_V(;r@od`nL-_4X)> zLk5434GeiA8G_`SChP=oxivQ>Z&O~64L`M`sp5)t*$rJn7iJSr^$Y55JOBMZ|1X^* zv;_UJH=R9}8bPg2DP}kXt5=V|qQkYVQR?0N+9>2ja8dFDAj~r5Q{Ge9X*z=`#2yZN zJY1~<%aYl&_^8Xep@BhxC%L`r;B8K10(9UTn0RCwp=VDo92F4iy!b&(MmTLUnUDGY z6XfS!@(G9RU%*#@y+LnB86Tr&z-gbZ>f#GA$wb1H@x>lKJiPu^Hx#$NBP)7O6A3DZ zLpGlx2Wk?j=PNvj>Um;#K+mhbCMT!R-D-gMCPV^JozcaD{TDF!^Cz6j2fw0hH@Tsz zY1;pz){ze`V@9K4O&1*W`vvEf#z?sJuCUOvFtHE`3;@;d68c$TaxINwdSR5 zt|m%58x7xJhPL5JQ@5+NFzlwXkHuT%#%?P3C>_(j)B{{o3AB>}7c9Ysh|J6Y-;VD? z@4^B5cno_X4pJMn*0#$<_nC=8YG^Vg4i55-N~s6xuYG?>qb&F$dY~ntB@nR%iHW?N z9RSc~fVL>IQI8UW>livWVbomfad>`kdBrsnO!=d$J>Hz0Er0p+x{$@^!}GJVm*1Y@32t5=TwfEv^ZCW~ z$?3_T)rGJAbaj1v3exA}%WDks+0psOT|Cvz>yz`-_HBrk%QkktOX`d3C}4NcL$Zl; zegC|_*h`=2x0&AhUDfo~@2X~d56#ZtY?%;15h0WuZ0Mm_!k~&dDAz@%hyTL|+eV36 z1`S9!Y9e!jh_cF1xQ9Adc~=GFnr)c&M!TxB&Wrci~-_HUT$ASTTZPEtQ4@X6$-CXc?fz ziKr8l(|xKAfRrt`Q@k|A9_0lPM2jk$m+OR!u2!! zZUhj;007lHPN@=@2l3LG5<*bPZk0S1lpRJuCYukdCE<-v(X~TwsT}-jCdLaMMgYa` zC`Le$!_lPAWw?blze02j*r4~oD>BMOCVYuIXABTlj&18ipCZ&M{HCK=^m7RNoBo^$ zHZ|_!O`r1fg4lzEq5-|Uc#DWpzen#Ct~H1T{GhioOi4^Y2=xpT1qR5w=RqbVMMj}P zO8OoTVu~tikluIr&k(U$u0%kVnaV44dnY7cAOR)=!W}V=g&@_ngYE_Vq zRigj}G?5PldJf=a02zQn_(*M&(Lv&|+@mlePW~QYAq9R&`3VaUBZ)^6apHuEyA{`a3gHS)zq2tjfgkb0B?V%X-6zwTRe33Vz$YDI1QkZUQ1&I3TDuWzrZ3!Q;gUJ|MQ5<`Y{b$`L1ih!iNMCzIF`PX*pqjikyOP@GW3$<*Dk zK~+lq|8Lagg-5`Bbf$UIdH6YX_vXClxScQb1oCBZ) zQ^RhEb0*y#7vPZskn|%j+hd;2+<92Dy=iF*Q_O#w)78oe&7pr;XcS?Gozg7JsK92* za4~cj)eQY{L^Q2U`+>@#FmN`<5%&2v0(GEHLlx!p2+MHG*X3P?utEw^LNGD9Oe`>r z@+#%MQ*l8^fav*0t;ZmFE zxN455)`%?jU4{>=i99b3-!Hy*T7;i#&uqyXyZRbT8~YCS(OTNr`|m!-k3Up_6abs_ zM}LsNl`s^Qcz8U2gdvd70u$UE0LX4189$=K;+5i%Vi&Eh8?mWwWL*S3^xB5dcdw~G zxoK--=~i505Zx9 zlGs!mo`zI0zAACwY$kn0vDi)E9yEcB#tGy;VFI~Nm_Tm(xMJ#|BF8Wi@@u`saGjKD z_rpkpwi9ABVldx=8xHUf-%JCrhyrgBL(0z zm_W`GCQy4?Tu2-_4%o3lpG>eZ=op!?hax9|!ZyqlOc~1!L7Iz_xEMuVp(Fe_aZuv~ z0s%8I;_3vl9RIm@ApF1Z?{)&Mm624X9%$*^WP(oUi5S(lcl{gah9Uc(i%G6IZize> z-n6}nb_TD59PPjbz3)bQoX zOYOw~z3=hu_NYxLwD)l?M2eJji?yQ^80^-btUdIppKW2cbLV z53?g=El>QIoAU$;A=EQ&NkDp5Y2MYS0sOh(5jO7|`izt46H5lw!Tat#ed zYP-yE1GF%TgnAUUeXPFom!78!W&wAZ6<;@w-lB-5JxUTIQukH0e*k-`$N)@0v%g+% zGLz~^R5LAGtKPd4E&&{sy7b8QORhbl59RzJU~=MJY}-^T)#_R=xuNIZXWAu$nF9@ zN(FJcPb6J_oY&pyV&#XZKUaZ4h1%}QXX{B&YN1alqfi)F<~riKXS$AVE~}3%0*&$n zOf-t2%ck{Y(`xRK*V^e)EI>H-B&W@U6})Ds1E+d&E?+#Qq!Z~HbaFw4j{pqkoK`eg zi(?Kr4t4c7OqN=~eRWEaXZmqqHv$2rORwk#QH=8Rh<|AlWlZPU(-pa z1k5INK3Nk`BrU)+xJUStYL%%#QOLuwlSEuxEhXPo6j~8JsnSHsUByDyU}7my3WJ(T zYI%?9M)^#pszB^22`Smh(({h$ShI$ZEJ}S(A zhe+1Olq2BcY$;`rlvrs+JroNrrp8}&bQIpNqjs^CXYO3 za252Qi|zsk7N+yX+Y692?4O>T9=~MzE>$p~pOx7RCRAMyIw(B!ot$l$hFNbipps@5 z-XpGx^1(9|pDi3r%ZCnT>A$*TCC$g;E(e%QgYisqWF(_oOlqNBB3X5+Pn7q^DIc|) zmn|M4;j(MmWvFU%P9?81raMaueaU$WQSQNioA)sB@IslBV&E~oV1_8ly45S|j5ytN zq6iM;6v@G<{GOdQN(G@Us&xL5$~T7Tqx@LBHPh~uZ%vn=MWfdzul648iI{I(<4@ZF z?I4`cl{=H#H?c%dP7g4y7v~o?M)&j@e^&-OU$IwIWuv+ZZ%NFv7qr}|zLY8|dgHt< zem>-ioOjo!?-fZ~`m$+sclC^5Cx13W29N7?<&#lmF0J>1-K5zAQ+WiXnZR>46@{~) zf!KeX=>#PTI`YwfAy_Ap^a>|4nS_oyujjW@s3y{0fSn7Z1~27hIuDThB8IW+%{nPBW}OE$e$kNK)$E;Ir4!WZeNks@#{gpNGPG7 z@=hs7vas#t+z2nGv~}W1#}sr?*OZsqM|z#E_#!8m)Kxt}=N$DL`6RprwT|TMDcnJ3 z&gsTi!_nx6e6I61f=CnfHX+IFrh*b!WZ9s=g*ggK?^CRaJ5U_<eqDvw>lxy4xiSmjCQbE{&I{V+t?XD)|bLx4o_w)6owJ{0yu}@gA(_`c~ z<;gThpLzkBx1B3twiwzc!p!eyZl+8KFclxl0%S zSFB<1Z_a*CUrKvHZX9j%&vjkeD)CHurQm{kJ-Wvy%%B%^rI(AaTqV=I+%IVP{xZ9v zX%9cd+4TK~AHE5%CjOuQczbmD?(8x&U;puRel-8%Fe|VcE~%c{lnq@S`zs&PEPTqx z)d%*9KP{;mmfhGJc4Hd_4QmeGpqjvS5M}X@ut9V#vJhcGxqyWn2x$7iQ;0xa;y@nC zuoF^kkBkWNmx1t+Gr^Zex!xNL?gZ2gHqqlD!S{7(W1}p2iK24o%tJXSG4Tvh0#Qzp zh2XJ<)>RUjNpmEIf-vtjsJ0$sdj$69Lx}1eULGHx-AFJg5UDMZvtL*?RV;dXdwhL^ z7UK8ot2NT{K)@bcTmapAqye#b%;U48gG=@4{OtPR@cL>q$)9$`O^5G}k3PI7cr5OK zf`fz0)0*lg^ybm=*)^W+YN^_(3bf7JP6;+xB;@RO`w@VAcu5p-svjm(rpmpPh(8M zyUb~RfjXPKK%GrqpzTo)kog7OFBA_FCrhCS4IW=8Ec!OypH`B(NM)c5I)JDmlK&$UdM@XFF6RYi-Xx!7zHJ8h0q$@I=wxK1(koYmobG@DFxxQOw)emPKmTkKNiKS$(`}t}IO*YA*Fn_pR^a_mZd5gnjbi zRwbWII{VG6u3Z16-!4f+-sq|_MnrP_{loA2{QH9>ICc6;ZlY8Zz!s$-Rm=M>qLkpO zySx!uHV)HRVv|8#=*gpBQMhU$d(U1DP<`S9B=d~Ba%~LWikq=RXBvt}Nm0l3o zMV|3bVdtJ}6^=!HjN_>oHk2H#${f5bE-^B>&N2mx;|3H7u9R6h_a|YVO+5?lZk`2p zlu{4o zi&|~EU-Fd*^bC;P!gUs?P!=yYZ$e)Zg+?`6yyjSxG-&lw#4(KTBsvnfMEUUAcr4@6 z@tJ!wqUd5!uM?iG&@g3Clw(1D^)+gBpt*ttuT=`i2;`}JhN3^27;k8f)}GzG2wI6; z5T2bT@iyczLeMf0$56w)>K@YR2QW0Ytl#VWqt~TH9Bg!yUeD~ND@X7E3oJelfSgrb zeRy42N*lW1=JOk?&1UD+DHg8PA7;O8kg2ew6xcf2rk`mqDQq@)PV=_bmk!zP2KB>+ zm-i!QZ4e*da8~=0A=`%7z{x|ng2R9?AV3vAFJO;z?8J4W97S>!LnoIPY3!#WXCZn7 zK4nFAg68+q`a|9=a=aDdxOh{P(BEEMTw}e2%or{^s+b{UC}PKPLtFVyjI;v6t6Aq3X-$$3K01E34K= zzBoOo?%?><8R0@!9sR9YkIs(?BQ!Eh{>WGFkKgMch6(dEtyL_9^P|_&JCy98q5j&N%mUM9(F>^q2qmKYH(g)-VU^V-jlGvZ;pR zivxv`>#_jFPjc!^K~jkCaME}uHJDmzHzuovko9LeimkO(kowZvMV(!JY;JNcv?8T? zQZh5eXRHViwCIbJV@&WRkDh3{ysABFI3}gQq(G!4qKM;5RDCEMV_K@S;o0;v$;0uG zc#T@ALngj6`rLPOCA_$#b|Y&0`&ot2Q+YzMN_jYW+=>g1y2E=5g}kW9G8Ys> zxW`UnA#) za%(8r-0W5HZ$CRHx&P@Rf9d+e+w{$!EUQVlMB-G|mfc2dwF%SOjU)x}Mw#`6+3O>- zSF>wVJF-3U9WsjCwlZQzQQ=tpE4SU1?_c#5*2G$az2#LD(eK&|G_lu@aB37#FS ztRs&@YwScDGJR`rxCO=W#zxmaZ&MbrwP(M{+@rXyxkqtZbC2ToFq_Z~yui=kst6)G z1Av+#lxKTx5(BGo!LnfGwuKZqnE#`_!GA>t-!W|CY#>~U!q&DPwzl>5%DxV~M1@m$HW}i=ckRIP z?E>|TAjrrg6P`(O*Ah`6#}}?H7ZOHCC502=@&rL_)qI&KvOSUeeOOz!gT1Z)P5Ck# z%spsr?m=sF?;uhfL`mxV4pDyC!JspaeIIIC$4ji7DIC^X1)ey{Ez6ZTo~qTwBQFnXz6_Z(D~|RC|M%<5NMT5`v3sqJ-D_>^?JFt^ zKZ>0QuRRIl+zW(YF~|cqwS?FnND=dO4r2j3S?Hxz6kE78NdP;fm4`5s*NDgo)kuWr zp^d#(QF(u3-`n^j`Y@Z#+-+^C&Ar@-Z`KZ{~lWzRPAqcUl{|)7sG6;|3In9XmoA zAP?|HK;@tZj(kL(ATko9jz<_i(wY*1hoGY9g!_tFo~XQ21Q63KJ1@-kejM9-L;u=+ zmX^8vTY!&8Qae)1=Fh%g1(t@J^ZDlIhJ3y>+dJ%;f2GuBHJ^XHNY8grk>wt0bfgj( z=tS4X&|8`cO#q-!ruNCF9yR9iXHRqFu21Ca`zg!S#wNLbAVj}?op>fsE;lnaN`X3) z+%I#&vnm-8Gaw%+x&=~wGXeuTo-y=RjK3v|U}6|cm+2#Q;vWvaX~~{C(c)~vaCehI z|68S%g?@H8y}Y`ePE6)}daJ+I&j=LZGLfvluD8**n3Or{s^6#w64JU`VfPd4!y%#@ z6|v=tdY)vk-;<|@5)vvrEt{s(?le-hXV~qa61>&ql|KvEbu_=-rF)boBd=``1Pfza z!g}W*gI+b-l7h`9CWCK=NBR_y!PSixP(v~+7fwmt=JN`3QH%u33$C}MhNbMP8k=Q` zOO*A<63Zjd!({X|WA=Pf?$pJ>{k6Z1JWl3#|oPKytwS@LZ5hHXssH=fmq`h6=WY?7GRRtx1msKi7Q z-f*iMJezzzZuNr~4sE~RQ;FpxryRJBRMAu14`9$u{0x;cD-AqPpuZumukDbG$A_?E zf`56ErHX(^J(@?BhFX+PvO!*F0v)ulLWZs>QXfll$gx>VHy*O00wiT)jrAJ*>>XMSFgf_EPiCzy zucaqOj#92%7GhM_KhU$4kR2c14*S$p3)Zl{er|jMl+o);>88u+n%6J1CU7`SubyLREYx<+Yl;UDjX}RF#>F4X^I) zohEIr*5k?iOer53Y8F}+p^3hhspl2=7?&}GsLQ=&zB_u-`JC1x{3h=Z|?KUd+JU!eXs%uM$dK?uYg|o{yzG^es>FyfletHKT5U^=dx7Bj&5Q zp?}zf{gTsq)p_-T5;6BbEZMwBoUSb@(UpiB*B`G9$7xeJV^a`u<2(7WDWJIJtoE5w zCkD*oCuk3)WJf6$$r2dcd@4npoW%#{4fu;9g~SnF99dSL#>CzO5Cc%UEz`BxAv@c6 z1r@NH@=Q5VF0##J$YT32%Oz<-O|U1)*_dif0-{mi7txy4W|?%QWHl-Od~{u+u%n}~ zd2HQ#SwwsHBZ~qGvExKYZ9S?R3Q?=F?Gy#8u*h-FB}7q3+?lcgA5qmD0>3Qq6tzcS zqugJ?BB;p@{-!OWtGe0dv32fc5giI$q&(Owz({0HnlU3dIT;2%=YbC)l}BN75=77< zJAvye5y}mePvpviE*bsQqHYmHydvjeEyAf+y8NasVzVd8w()2(D2BgWO^Jr+QA4@~ zH3-e~VO-J8$E=2U$VNt+OE&x_U-VmP_q=Sk7TLL3(tgw)p$5}aZ2*=3ov!uOsx#)k zi~ZRnpI6<^^Tdy6~9_Z#rJPKBv)#pZqM#^!MvM`|3j=AZ*dwu%6x(QvaF}=}} zGB=kJF3|-C5_KrU3&_N4u9p7fUvLG1`%Cy^UO23uQy;_gd{XMRSG9q73DG4T{pkPM zG-`x=rejkjl6)STh(~!Z>2!31riNT*QFTXRVN$Sc6tB42xIdZdPX14eqB&PW7b9Vj zrB2qti2kJ1)Hx^v6Ef?{p{C+SRvwuvp^JzcJ4;ABTP^YBho0insz>c^y4c zBBG*|tcaOa@s{eTr2Imj>EOu+bBwOhS$hjZQ=5|v&@Y-PXl03vm41YBaCn55Xb+iP z#qXay`LU`ZHb(h*Skg%~6Ed8l`9Qr>UOaQUvx?a20Q)-^H=H@_=Q z2_X(%0s7cXjT3fr#Zmj+(u2?nT}W7v_!bZI-SC%}G8H{rnH9I}uUfUyVx!ffoyOSP z#h^N+DF7$XMGz#4aZ)Tv70VX2?7byaD^MkUHZ zpe`}|HnCJ=t0;?r734_~y5ujdI9I6+&y*z+y)H3V5}L~>0wG0k<4&Gxo3V5rY%CA9 zI7vIlOgn)Bo%`8@b3dEtz%a%(g{?s7+oA2HY+eGlK%^E_Nt|af^$zeh$)*6jpfF>^ zU`h(e#|W4%zs|Lhk0RIT;9EWkbI=xApF1oj|kR|PehOlC#H1GN`F46Op zT@OycdsJy{UhAecRi&w8*GbvF=yqPG2=YzJtoJ>-|bz zn?C5t=$uRP;3@8?c4Txa!F2Fse#=t9t`o{j2?jjIfBc7k{0A71PriTkmQVj^9M{Xf z01kr}>G^b?zSs6(>flSynhu$c$lnaa)4SBjJh(a#!J<6a-*Z3>1uj8A)F)wT>iw;Yw*! z0%2fXZ!8Q=u61!~@;8wP`rAkZmnL1E>J33(;`W9P1oR>3`obm*%p&D;WAJpAOHbvB#9oq&Mcri6P}JGm5#y$n;)UhoS~h^Ui`seW8+LXRY&{ z)xNllKMA_TOo2>y@=Q7y^Yhf;gs$XvW!n80uKc6EUo-b5HzpV$!HetUF4Y>Jz*Iy< z8=_&NB%0>eFl&_Y8|JtJZh@HI@a8Z7@jtn`^h#eKL&d-JW=q$HTe%g-`rqcu)-3y3 z2En))6ZMlctPcDciFX~}sG>#s^xL__c-4Um!&FMhRxBKve|czbtNmb=F$W`&t;UPG z(?1W>u{<&%bHPZVG}@bSvPe@;{ujQGdz%i*n=^k7v@*H`JB)&_;EU+@Od^Rt4W}2= z)U4^|xY}t#M5vS5H(KzNQ+_0(GiDAon&O%@TC0eQf~jIA9gP=S>(!d80+PBj3ZjlA zhCxqZ-pjY3|0ZZyPI4*sHvVX@E|&s|8?v=5 z<7&!b1M1}=q`YaIND3Z;xVCVqHC$^=#AchQ%-Zk%3GE#<3Ai@0^lcbrr@LXa-}Xc7 zktHMmLnlpL@blCOI)OxmlERJ1N@qowN2Dsf0%2EJ5ZH64DOM4}iX7?~-jZPm(B64i z!*C8AYgfZ~z>)PD2^hPNtlfQN?G6DQ$95_9^rFN`s7erL82EuGA_bY7MbI23b`aw` zXOzZ~02b}uB=8*2Eol_hKr2B^!rFSkk#)2)>i=~{c1v_-a3n)GGF@+Ii7PVd^}nsg zVZuWv*pN&1H{cpCW{uVN?D4<0Ul;jmQ)KaTa=5R`uvT6~DMb zX%9y;T@B+2e{-ZAAngq4Td3|&yE(?On$-}lB_g$}hG z^otdQ*N(HS6-4n>^=R<|M@?^sWNTySa7@zjPqOq z7mtq2hw`9rOrBAa#65uFGx2%tmr9Otx!_# zmq3adn-n|SdEo#oiSyRC+^`pelQ*a{M>*tI&UcfAe#k1x4poJ6OaBiay{wA6HfB!cYds3c8 z8O-2XuGN(PbhnGKxUHmE+-4~jw^4{??d&VkflX1U#uM6CA{Lf{*)N7W+$MD7lO&5> zDJ#z-WCwA9Jj?gu!j2Lj)rd5=$d#p*Ym+`Bffg($%Seg(PPpPB**g^?`gQCl1;Pl$ zz;pt|2}rIY3_>UKT-*1lL=1`zuJg&ejM64rP5qh#6KBfHUr z?5-wccQv8*u2_+}3577@LEmAi|)0=DHDKXg*d0zL(zem3}tg5#m>vy zhz_S7J(+>o47g7M%s&hVgspoO$+eX3epzadZHnFpfR@y4nhTg(VDV+gN55X&bgMV^ z;>z+Thqmj6&rCnJ@WN{6Y?HXN?@`pbj?Lj7$-TNwZp`Pc9^88V3Nx}{aA78H5i%Wx zS>hEaJ)qX?c|@KdN=Ti=M|MSd;7deyfypk60G|klrnl@x5vGd5)c~Y-e;tU~%e9D( zFDNR_11E+GQ~~Z+a3cD`6;yz512DB_{`&Ua^Ph+^zCC|;c7Cw<$48M)Hc`lylFT*L zqg`e)=0bA$?9TPP8Yjs@14V-9otxlD^z9$-cp!S7ffkw7&rk`AQmttgh%X@ zP*Q-|QJ2Hw6GM-aQn}*?wL0Ly1;yA2x3o3Np_mfF#XU& zQuB5@56RXt>C3?o!Nh^?yF_EqXXVkWKdm?dw!OHet2qL*pVvDA$LAXHekkDoj90n78(_sFE@D~vM5{CEW$()sAo=l!+aL?z`B1Rg-2#_qKZ|BGAhh2J5 zTf$!R+g*Fs8IiZ^RKj-}L7<;w?SX%q_N9GQ)>`xjYVBcf7u2|QjC3LzA*8I8B(SCI znI_g=W|o0|KcBeQ_eweNNq*he*BC4h^ZIt%mkfwR$+-8Qlk3}m>b+E=M0aputZTj7 zwR(K_h?u12|6IN}&*oO%E+p1)a!1@&V>2z#TibOp>^2Tw*I9q<>`@-AEXZ|TW_t_b=#=Gv={?UNsIyoQD3tXr$QW}fAZ_xBe&E#-PGG0(j@0SS zmU(}9R+Smuf%cSiQVnPgWp zfD8xlPkk*AM9uP*jWiT6uEC(`nosE?!}aPcu5|y-D)2{_({yr?t}?u)30e(a$wo zd*rWwM9v<`df~AkaV01rBUM`dD4*H;8aAf#yLar34-Nda{N^|pJZ<^M!pG;l`vPgM z{(cY6OXo}g6baU;_3Zq9`sY8FJ>D=MuREfKA^P&=OKGZtpO!}*KPv~}&~)u~DABo` zUpuSSbiMAx#`J|gxS1^}+Pbl}gI07E&AN-5NX^&0?jP+<+~(^~XpfVP(ul-)3?F3emMsgbkLNmyLCkmqKK$0p+P)@)Nw4Mjzif&hs5&| zhRF8du(AL}Bq~RZA%t-R@G}wA0H#Zq{^DR?ML>-MDz#F8JMduU7{KyKx@?i%Rep&b zM$**+hu&X(B%4ALVaJgSJC0;~l?`E1h*C>LP^p*934sTZ%ZPylSM(Q~OV1%ym}YTU z0CPb{qeuYX<(P+ zhkcO5?w?$&K4d8vstevnT zdzFjB%#n-1g7N$c)wzbZVx!(dXCG&$G1RUjYm$_PFARA0+vmq;2g~eX>sjyhx8^Zky(#tS*OqJW^}Dm@ z<{tP1VSBFr>cv|giFE75h3MSBdw=oj&8vT#-AsAVTL$#?>yPj0M16&IDDn@O&9|@K zy*a%&dv&7!!f0Ncp1nV#Tg=}dXyg3k?DX{QN9iiQ{^8ZPXJRUIqJfekt?GdWPXE<6 z`;l1vU26jY|IDm+)(P4=E4tlhMYsE`*dCt%C$s~dw2$6x;>U=o(ga4cduQtc>Jd&0eu zm@rc&WGDpMklWG9KypPBrF*I?wR&R{cpPgUCW)=}guY~;%HzH{dh%1r$H4(o;W(OP zQ;|~mN#dL4Qa1}f=tEP<#E%5Uk{j8l`4b8^tD#TTp)eRp?-AvCUG~DkMx=IiROg7+ zqLn<8K{aFABAB)I%*Hs@dpR21gFbwPxk7$CD%;u`#B%Tq>}^TvxJ^Nw)0hpD^Vho; z>)xu5cHHDvG#uWZLlj6Zh)*}Pfqo<82OgXXxq^UHEaU?=f~{nk?34{yp+vIPLiUd| zc8a=doCy8LB1DMfG~^U3s(#TFQonyNaA9x zs-LVGdfj9mK9)5Tzu<$Qz|RiWcH*PdHZ^n1+F>_?2)h|X`|PMk=@QkG@Xr%j1bv$Y z@Jo7O3PWiQYRry&J0bCn)T5t4VW3gna03)J7J@%o(^0@d4SbCaekg# z#myVfX)k6eb$Rgl_eUl-jNABxRO{`PC```zDN0Sv;iV?WC}EI7PoLi*t4PhAAvbvP zd?+M%q+CC%8P1+A5qeaUBp1H5e-fGwO}r+Qa)xiNO7WJy-;?s>|5SJLzCC|_=pLR7 zL^DziFlqM}7*dBcn;wyhy;nbC)md2w+_y zb=5h#J)xg_%?&Lk@cr`+=w(ZvPnqToYMQzSYv(b z@#QF~nq0H*Xe4c(-A5B?Gg*bLP*0}?NlMvH1L0G18ST245B{d+O4^1K+Iwg0PEgZ= zD2&*a9}-ZC0B+_G(OK{+!0QBZ({hDl4k{Ge${;KVyv$P}&?CZP%ift|#ewW$y09oX zaSO@eW%^doQhr*vv7n-IALUagg+UrtmoTN`C5>8^jdWFtl`(A!bUSv}F}1snY5M}{ z3`td1Y<8iHqfbc?^<6enu%2(l_~23{1P8N*KkS{*#eC_^4CbX0~^8inI2LcegGDPBzkK-?THa6+X-5vVh#dWt^F zaVUL~T!rU3NqtNOTKK?Yx)QJcb;fjixOv>qa{^g-43|VN^$@Vq%p=e1e_yBDyYPUk zT+8@+PLnhDdU&};uXl(Q^z}M~sI6S~pl6=3mn`UMh?B?pWA*R7bpluG@17meI){yR zO2v+@*VbN@CQVxqrJ&GjtmD(={pn~jCk=9~ay2h(KkS|UG7)xRyfnF(QGuEoz8HSh z&SWti3=Xxd#+ZT_Dat4IJXbsAuy4`p9jUU2toPsk+n4^Qeov}7#{d1l&&!nP6&$&V zwEL1KB!D>(%Qd$rr9-b}$NNRef2Ekm^b7Iy+{C-Th(O1UFl2u0}qngpz3;BoOu@dHw@I}1 zcdN0!L#F69VP`i=E&pa;-3yQ`Oav+~C`~-MN ziR4ur$S4%M8>A^hVK9hlCW;1)6F#(w*2oI>Ci)O2+DyRhb~jPCyNR~nPdmhLMaMQs z(kuu9YI}TmQONZ9g*Jk`u=4<_ZD{`jWg~72wR=JGgQ+xwJTj=6s23w}`>-a87aQ(P zbT<>-yyxf-kIL8=F+w7MuutiJxEleqFx!rehpbX{EsfbK+}PG4FbzA8vdNXrhmLbf z5LG_i6i*<$av2(+#_uVa*TT??B+tkv;NyjE?{ z8n)fG;C*Y6`zAz3qw3Yh7pYnKYTIe;V{IB?jA)eaGT*~8h>V#7tPpi6iU`fR$8qwy~{cOUzpG~xn)!FFQSz7PR5zZyper->56W}%| zd|Ux)cH<0maq1LcOA#0LV=xb%#S)A-T4Jx*1X!Ynwuu_}{BO%9{KhzZbT5;L?qw0} zS=LhIdUCj*5vRZqs98)Dt{vp_p`FBdj9`Zm`UwlfrEU1w;4dwb%y)1c;a+otWxhOL zh`u@+L<_93?f$J8#P)#ia=v)A1r{HC6Gzjc>M*6FBe2@8)kyW2pMge_|C zZMmW63jD%=G5M|>te1vsZ1LW{7smBO5BW%4;obJNc=@GfI}?b5(N5`|aYgk7`tQ77 zT8TkQltV4CccDw`&j``*7`Jv9VBnRX5~1gv3?~fj%wqe(@T=8cATrCxhT0q^u50MVuJU+tH`qE5@XhlyOB8O4PjW83BOLI$Z_T^Y*LfK5e3E#8 zF8QXCCdUv`bt$kJ0T$7}2Y_)TgCfj$lYVK&hwUs_Sc!Xvm3Z-nUYmVb-b3DvgCcCTVwQEh?c9`?3su{9K=-F28WX56tw$yS-kc&13I>ov$=9q2Jtdf8%J5HK2y zYn37fRXJlgEQzGCoN8-4g_MqR40*SXx^HS<633XBcS#ChHxxW9?{}BZ>%8sdJvzLe zj&85+4i`q|*}}@$Vh_Bnk&YYUt<7!R9YbgMMxQ+2AJ>3I>GY1MQATV+sp^8}dim`f zVMMQH!#-ZvcX9sm;_6@Ddq3INx%X0qH!rjAzkE86zW-B}kAM90kN^CvI(C)Ue=97h zRXVB}m9!}xRdbs*=Al-OZS5t43&^4BC*r6g8*W-VmF;xoLqQBk$%S&Gh$EmXA=vQ3 z4|5Hr&GVSub)dqUM0g;)NN$}5MDVaXg-+|zb5v*8Rvx4llG3gvrK72GV=n>V&PH#Y zbkhS6a-6?EJ$Xe;6UvA63>WKF=hu~C2y4XR-)lzjz7G52WCEHYSY8JM_Bt4F2gXA( zQn}}6kdC3uD*3~ z(dMC-&BxUuF}@z@U_&Iv*MXXbNQ`N5U9tC9vCN}Njl<-&k)^dDvaF%i=9nJ`(o`!c(d`97=+=1A}i z4tV}!B(}y%H9kXbc22VODj1%H)ir|VLV&x zADT~Z1dUav^vYA3l3_kGx4o4Fdba9_X*6G(uY@`+}NhBF9!J#R7gDL7{;vbl@fdBs@_OTk:=xI`l{O>Tafzr^ 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 z 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ê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%0D7D&=`9U@L~AyI4q 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

>&fDgOn|_tiTK0*!Dr!<^ZehhE!UBi3_NAgp3&I zr&p(|ZBTstX}bQ<(>0H1Zvpr=e1Kh~3EVtcjR#Ct;{lVkJ1W98J!ETXJGEmyOhw`c zV1a;DU_%%dvOplb1`nZ)WKl#^$QC4a5Za^9VZ}P8Q=6=op&P4G_rb@+?Ti)S#UH_B zRVCS&O$UdaVBe%&>@jp*Mq_>N@@%P`nI+Q89rn_Kp&5y8!ILAHmdpmQ0_y5 z#C@8jKXzNS!(8A+7$QNwCPEI!vdH{IzzU`*>{K86f1tYt;}aB(QR!+q zF?gWTSfs)*od{Kmgnmqe+FV6ONVlHm>QBQ^ZD%*My$4KG?*S9FyDCQzdN4IHPfa{> z?54s&U`hw;p$S(Xf)~gz0_X%@A?&aQGL6s*_hA`?x&cliJ6TRd8PKRlt@d_7ReMuz z*{ZW){{$!MW7?N%=9l|``Q<)fesvc>H8Dqg3m9;l1c0C^ZJ!)Bw6C?9&=?M5Q&o3Vwdft{B;nVzjq{;PwjFA0+jF8aR4X$(o zsR+a+Q=wx*IVm2hXQw_SbX^}WRvS!=P>KM8ifuK;HW^`?Q6w3lmG1#w2q*cJC=n#9->srYf;9|m zHlg1PXAOEp`$C9Y0$nbHNR%*Z(OE8dBrReBkLITmLPDY}R@l|@YWAW50wve69Vdmj zF=*b8h!RW69XDNIP_S^Z{_;xkCH^!+EUTvQiSRmbj7s z{BHmp(G+gvxz?o3Bp=x*oa7jcoj)-Ir8%5@G<2�z^|c>13@HI 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�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%0As|=bOd6SBtQ>mbn#T3q9(t7l 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 z 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

iAaroS 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

Q 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

o#5)ar74}K@Q0<#D<(yC(FdYsafS$oqy1eyN zwWS-kXRLMTw1qULE1aKUf+oB3ZbDE^4{pm-+f>B5NY)!fzOP3Zr|)Lh;q|0WPpk4a zf38lD=`v@g#R`IE$?I@;cQ=TZ_i*{eFvC(2AzR$bbLc3!d&OQndS6kQQBV+WEU%d_DS|ZBWwksroWMRkxx^8*lF}gkvRerQ<&ieC=_ZIYb!f z`!Q5-T(A#00PPrX_*emSq0*sYdzKG)cN77=X=foNL6sdo4szMe)#Q2i6IDHGJe-IR zM9BEZO6(EV)iwga&O<^Qxu%o&!>*ix&(BWw51CZ+;PuJ*(WO$02O%Ik;}2dR?Vnxl zpPpVG>_hCmBcb5o{>45@dV5gf3fzASk&9K$kJJ5wZ|K6ibNVmUsgXhO7w?YFz%=;! z^`&~CO+nIjudb$0siVFiEmg4 z$9O^Pxq+WV5ZuZ@e;wHd;^NrD%^*X%RkXS@d!G4uC-bnJ8u2)pA1Ct{o6MU5qo`H3 zpp4fa2H|L)d?2vO?I44`0XXU zVN9!XF5Zj7QI;*)K@;-9Lfjj87}x?*=9v|t|7+qJSpko}o6za&T4G2CIiBETO&Jj(BEMW{dDQ=ewm0J`dOP~GGGaRZ$>|!MKc0^LHkX4`dB`ch~uQ*@g z9tx{`cb&3F78kx^-%K*XNtFm_aKoISuW+13(LMiNyn?rs1GJO?K^RRX#U5X&9SI}x zUG5+6$XC%(b4H#5M0Vhx61NOI#mqqh-|T|@8su@1SO-SIWWoYSB-6F4JyT`JRVvv- z-(5r2Nzn%3O+O>|1YS6rli!S;sz}f0&%>|O=4dn!u#6U8QFYioCc0TS+(&t!NL568 z@mO(-+Dbqs0u4^4QfGO+#0X_DP`~BlPK$hnN+O%WP{J;d335g|8lze6R^ueG@FpTH zIJ7b8%&|WXzG+evg3EGrBY)wPcF;_Xm*g#9AzsZ77PgL0Im`0&mA@tmR%|+ys_8E!sjg(y6&*&ct;q((J zKIK@@JdcF&z%CCSiM-TGg>!E*Un&Z4J(J^Nr5b78B!2U3dAraxCyt_X84$`n2kvg- zKEK~R5BTH-ij+pt4(61ZYv)Z1@va(NDL4DdqEFN$TuQ$3ji^f` zlBKqT8zM)ZA>2dqfknSC>LXiD^oCoE3x%3kq=;UykDT#wd8-s7w*f7;*+=}65>H3* zyu~xTa=EKp_?(QIS1@XRwI_S=Zax8b)n44mOWdKHRxq6wBm?dJ^oJEGGP(Hk8@f1% zo1g~`_D>@|?;n=)f@*5$4sAb6qAUPx8s1zSCgJB9A{8If<|zmPAXnyE!O-%;Dsmd4F@a-mC(Z0^ z;&O26{2NJtx|6<>xQMR_qSEwz=$k51h-s_rSUAcUM{Wkkl|l{&ZkE zu3^Q9wt&$ihVDZ~!VkXX22KL%V6YJk10%3q%Ljw70hSibv&fCBtSo_Lc!=K&Kkr<& z>bxwEbNO*Df7!X*%9h}}51q^IL+7%)MR3&szqez!;6$)6W!)H_T;!`k^3XDnzKwm> z1tVl;0m2SFQBYGRt{)>XlOXXeO6;b&Y<}LkY?dh)ALsJpT>h+cc{5M^59#go{Y==f zl88`mWvck5tE&QX=6YQpn>nePT(!^p->M?3zFOut0yWQ7e|(QaB{{0Z$uJJJe`zF; z)GK1tW*6-&nYZSwk-JKPI&(H5av3DcSYf%vm(Kj8loFbz5r8hLlyJ(PVvZRO&6a$5G$3~MvYmdN{CV(BIs>&VA%n)cv zfrsYlmvecic3+BMRO<+z0He!ZPdfS+gZr)ixiqlpD>Nif?i6_^;*3{VQuugc!AO!j zZ3&(zMqwueUtW9cV=SL#aQE zhk;F+H3;?FP~}D{s2u=6NMHodcDxkm1>XnRIY=tOH)Yxxj9;(L%>Xeb4`Q7^ zi73I22~OG=*{nD)yQq2spkfk3TG0n_l4Um7waSF8aIiPE+(T`_wOEy)Z6sJ*j|6M$ zkA=~!MVmtKr#pJG24#36xv4~QjC*? zfSsk0;DYeVTJ=1d4<89Q;rMm!r9ZfluzUV?|MdLT$wk|cFt;8O=GH@^JC6t8#6}E- zEo%rRHX>(9Z5!0I_@Cz{F^I|lLCio29s9&mi{mkP0X-)H)X+8{8(Q^XxgPWzR&&VB zPjyBQ|8Ryx2kcA2*=Mw!5q2Wyxox_%igW?Kdf0Y+$}cw^pALxBoif|*FI3S@Olb?Y zEXcNcjlk;Y|NFoCV%SKgppaM`G%$Ybmp+t}h+v{}3H>KCL_jIFM@0CPkV8A`)BH`f zatA6@%*xy8lrm4m9@j)0vnha3I;D^3Z$XYv!C6k=?yFcJr=E{l_fo|jg|b#&DapB% z9fnXPN3Z#$po)~#vWw~LW8Y=fraQ*e%yuN9O2bj4x+InWv$FgXwOvV(Lb&26&4`XM zit23~65Eb8UvCAn!5PSvm1hpC%9^<9(=U>C>um^ozu ztq5Jiq-`Z6IpT!ERs0{ZGQPtL`TVts&s1;7b*&4lpPv?F! z2i60)-&PirwWMzicXyA10KghVwgbT)a7ZQ~GtVp&e#r<#BQPDnOalH9@L$@#L7Jaf zsKHzUh`R}cG>n}ZH@#&eJYvQ zlgRa&Jg03=&SsBhv#ahC_6;1ZCZJWo+@6ilUGHyjVn=rsyGP3nwLQcjB(UYSqa~Hl zPtzXq4KM?$%=9S(PXFa}dL_Oz$qm&B=kyZ!wz(+iQ0aY6XUA9fEw}%~@>Qm2kJ51G zi>CvnMFX&$q8X`*0^Nys;zm(AY0!>#0thvL5JW(9TD%^Ffy0VC8;x_a@Kh2=x!qCg zE3AXF@%>KjOT%tb%BzNlWW;jiJ@Ij|Q-eNX4)5+H8EnfF^(FW{%fk{Y>o@rj>aZZU zJlnb)Kd1X=5Vw9I&$KPeXn>6iv)O?gT$R&o=&uWQwanE^b6udT{h{tkY#`%Ip&Npn zBdotk;M?G7ijxT8tT4kW4#Fr9`#A=5*Pz*)Af=GBDuzbpL63LnRHa{r7P$<(t!Vo; zodBycV{Go53CISc&~?C7?z`ABAk603F<>z+^iRC5F|LN-R@W?n#@JL5lUma^KPFLV z8mz4@tKmZi>+m6ib=X~}$PTT<^xV(_`#CaYi9Xayf;c9HKxE2+!pwGk2;D%M2sZ{0 zNQvw$4ibaTt8O)^#(^{r2(drwV5J1P;Bl~i85}_nZ0lZKJM8R-jMeTouoWa=yS0e{ zKFBf;%=~}?MV=QyM$!cZvJ=}Ny0gH13p*L?{!l(QViVl(P-BPy;;Rzix)@%o0!`+_ z6)ssC$MAkvWA#TeFt;(UVRaxPi9hZrLU#u}!`U5ip}?>zOzbr`tuWael?Jbw+4)g=;T=(FshIJK5YhUo51dsUqQVv*iIEIblZjx*%#f15 zzx>*r+*d>VH|Zp&>0_6O_(Pn5qv-zbnB3;4KRrA?KX|==39^#?n52}tqK$7wZjlECucxdM8Y;3&EHGtcSIhjg`{UdE{Jljh+=I-=~nz=5v-;YO5L5LS!c7{kE0F_ zBYMYx_|PZ~A^e_1tEbCovC||jstc~hXSKd6>)cm7RpLLJP%aje*-|-rtcWlsMPMo8 ziQwVfQj$=h%jJmum_R$E=ebZlI1icc@>)bpi_t;pS9Ben2Np_^T)mpI6qN+eu^8aA z7Je8}PJu`w;7QN~F~g&zUWuZ021o*ey;KLzprJy_qZ>;08d#CzC8V*6>fq#YH=}4i zJZ3Y=G835w`BgKKt=3~kV@ie?d}{aMc+v$2k*I~jep1Fro2qqVjg~j6Zz4I`kgYaF>#7q@c z+%Ee2Shzm_$ehfoX|WP?!)PI30vePbIR}vd0}P(wWrvr*X_-(A^;iDT`+Sy2xdjcW zayaWQ>hr3m=&8`}ID;42N`mQND2i}eieKZMI?oetSjev28|6e0S1(2ytvQOCM_#x8 zqRcxLj&A1U)8OqvMlF+DE3QtdbBV~CTPVB&mV|*t1F^^HMH*uU@`a zDwcl(cNy)wiq}lC=6Dlch*}J{U|T^xeN(`_Xi8@;kqj8m^OB~M)@QjrxQY0|;lLbJ z7a)`p%ETm0e*w^&9txiS$ZsB;<;Gj8u^xZW} zVbkoHN*gM>rS|?%yYZOTo&H5nel-_@vKuw3NqecRUk=uj<+R*{4LY6!2|227v z-yHqTlai;4Y%=n66?WO4dc`i&H}1~vcGXZu7-DxX8c(A=NtWA_;s!U#)7(Ay*WZoa zk3Q)IcPL=K_X^+slxO8D*p{?JG1N^>%VXJ{h-O(LSvZNM_}5Fae~3vAm@49N***~BK zuf*Ao6>9*%8t&K4D>uIv5FuXvw|mkud-UKg9{F3Uz`I**MY8QC-F8K!a} z$xA7lV`QYI@_ko4JZ9xJd&FjovH(@=Q-8)?vCmzz+@|gzS~N7AFu$=_S;?HCQ=srd zj~M=|u!DEuXd!czBdH-9$Kop&GQ?8l5F4hkQ~HHV(e%o^w(=LLKGn6MI^OWq;Q>9812nnvrOh5z~0n_ubr=;>66qmyhafwVkYu@ocv-bz{KX zcC%YYig}(!qcvgujZ!1dO8972GI0Mrtq6aySNW+i^53dM;;iw4u5tEUI%^!EYoF^L zWU`?_53af_2x4He;PG^01JNcs@Bl9hOdAm=*U$VReENl|(E55-4><}tHgqK)(NElGWoFG_{ z;-=9ax)Pr|LGFN2O7)7^3)yz2Rh6B5ERn5nShPP24y<6ZQ5+H>cHile?2o((cwrAG zPufz1mZzk)O&KGle+fl4nJRR4pbQGB`gjzP^BD>Qi|LiHSX4<$@Rk|LeQE)RT+3-D z=|Zjf5@=MFE)iqlzL^#&6a23{TY?Uve_l3Q4b6Ae56^*^CPb``7Uc? z&{j^45TX&<4d=U~$*y7)+R<4sQQE7B2{3C&(QBpD2Tdq+r^$)J2GZq%QDe5t=h5bk z=zxca_6^CQkjLgZ#^%nkqO@t*sMbE*B5|$f~On-%vB%)JxU{%u)06w;my|7(4cepiq@ep7( zY)jC^@`ghQa(f_ajDV9jkR$9yvC%a|w*T(p)zRAv3fvxD;tmAMKvMX?tXrZ8%*&{! z;N}-TLrN}weRBjF$IIh41zn>2^6l~A(TV&(bt-5LOFuCYV@ufY9e5(2AD_HAx;Q&N z;C~&Q9UW2=?Y}^&7jR^F@-Dmxi0x9 zb@#<9l9i;R=eMKxtQ`eGPcXZZ8XHy+XxHE?f70a7XsGtUvR51$U{FOw^ZO9-hzXsV zmlO?Rm-#jHMepG{B!)`;a>|t&vqToq{bR;+$QO_bC;ywB7}xpl;&NuzfgHUdg~1U& zx)nVxrAi3kk{q%pM1wK^i4N;h&KL37qKZnSa!hKgu}T{d$2B=fM8KMguP)W?+4-wetz=}Hk2#-2(J9fZ^DFK?(kO}3o`{Z1ck!q*Bq8Rrwd#s7TBVY zjv}sz6g1{`3AE1qTCV8e(D7F{3L!B|B_?(J75aq|JxHya0~ZUlFJr|x5;HvuW^drhWEGOcyND=l}l?KzPR~; z?u?;v096Llx6;@EJ(=Mn4IAleNVUt5oSy>vZo=jW{WysM=e6RPjHCp(`8f2fXjo0# zw|vtW60*Hw6w%V$Y8273-fk2XYkSEk%8U%De~v*kXc>~roy0JaU4nC)VPvke>QHa& zown2(%i6YgtZjS8+9HFVlH|dh5FjbuOz@Si1EqC3yqZaZ`~{zcHtYbls_TZH388f) zC}eJCQZFU7ECtI#_c&Yi&RUZd8#5ulho8T9+WKbOuGzL{I(H1Z0jW!g<>MsbQ3n|T z=yq*57Ka8nISmKSMNBQn9mcV50)(GIv~vi#CCX<_QnS`Erf;sKk9=CkR4t|lbhBl3 z$@sQ+gaA=O0ELjT$W)Fzw$PH(!otoPwJ{?biq{m?AS+N+MWNCuMV2+4`8(O%AT`b& zLBlC3MXinK(g;hqpgt7Nw$y4I64H{%xeBnV@*E&;1aO(ZVNq&q-6XxEj{r82#sTO5 ztM6qN{g41K65@ea1*g3BlvXvON22YiUM2w|DM3A2dgw^yy^;y0XuoENJ_%QTFYjAn z%J18-D(_(oy*pOG-sB#G2T&8l+a$b5M$)!6*kK;#X*9rAE_cmj{Tg%T1?G%l?a4sz zU{lqe+0vM*KfMmGpl5V}S*V#;rE)2U0@}Cp(hJ(!Z7NILCEvYfuW7iu^G*XMr5Hi1p*gg`<8BUzmWd?943a6+)a7BA(UcFK39=Kd`OO$fA?4*rlxo#80pjsk&a~ zBjT3fzsO-TLs*w7%L_g@`>Vk-b$)tu0JjY^x{f|8Yph6e9lv~wS6)8fzqp{hvy;<{ z<2T2@${$rXiK%QM(`CJZNx+c7juy7a$@Wm{x?i5GI9_BD+5^)69jZ zK1IHA3>r)5DP%DSbZrp%#3nIYR41x!3=y6Q9w+MKMEyNX)I4PKC8n#n)pRwtny%f6 zBr}TG`tYt~Ljbt~H;XbG^CHRMoAmKxWCRu{Xgzo^lh89V;*&Ii76Da9;Dv5lovy|( z2;hi$oUV`4_4hMfbzF7332Stju*O!0R%5F}tI_??nz%O53322gOc46;enObccc60; zLe>PQb7SbbMixWYI))`|4CBZOfXhIv!KWi?%@8&n%d=Nl`#xpDI?M)aK2F%j3HwD3 zt=;D8R)O{4D%LmqR_&8A?C+Z*0p|#s| z-RjV4Y;|Zgx*u9iD>WP#zZMb@aM*?F0Z98I%1Vld4#apfc!{y6hBo3gn4qB%x>kgZ zmW2O6j#~9G*`8|ze&9V$*T?DlhjVDPI(eC_tsIYy89NDGisM9Sqj? zBxDazgYSb78ftKmfWusX+?H!2g6biBgJ2H)PX-*qNac_1&`zx?f*GbVlJ$q?<3xR& zsDCJj)~?gl+-ka-TTR#Qht|}yLZr((k?ms-g(n!H*2K3mKQXYWCXn(3tT2W;ePRQf zWI~t;a3>E8^dMt`NkHjwx;{?VALF5Qvn>7H^qM8h+yE_@-jGsWp-SIy zv6k*IWKnvfT@lL&b}1jMG2KBGSWS62QG(!L2~0`nk9gESE^=*j7C)-wHLQ7n@9#&< zZa-zJD}H7FjdRiK?K4{`NY;~ZO!))^)X(n_Rw{XAN)mu~L|J(>xuRsro&NbPF`MEq zr$~TUUICSkyvY2VmqC%Vj)vCD$1ySX^fVr zwg6|vLSKU4~FUd;=Nb|vu+l1}g<7iql2l}fC_iZcsYts~;KCK~Q3 zr<%&EiHCMcI`Mhi5yLUdHsA()vEr{>i~!W{^>+KGGo3hl4UI=BWfae*q9%#t+67gS z)eP_rA>|Se=rPWGjgaqb$YAvlYMwH>)TugR$ z%N?^zOuJBqNBTj$C@H&PR7@ay0QTPQWRx*Cks|Oq0%g1qCSQ~qU#=Yr=WWQc-;LCj z%|&gU*HJ7H#NMT5bcfuIhUZ$9$%=d1IVHVKSr%TW6pMW%doSiYTP0kyk%O4t8nxnv zrS;PE)`%51KhT|5(jvVaifbb*1`Tt>%-{wzL$FBtnGKy{lZ*_2K`jRf84e^Up-1c* zUIelrIQ7$-ka^(XKI1d8gXB9!$?%B+*kLBvK#Ty|2*heM#NRUCNKD(wvZTTA$t@OG z04HaMm;3Jyk56)bi3577ZaH<^#4L z8$*P45`=zY^hpw7Xpw#F8p!&Ck(#jcDu4ubXw=6IIg{PbJ8t|s&(t6JxEXG%L)SVC zhTD#t?)q~!5axiDr(hdGTmf0RQ8={yIE9}i!x1ZjYZDxV@M{{Olln1Gb4H4eifL^U zFYKp|n=az$!5{6oS#SA+S#~gk*#gsy4oqXYy*5vweEvYcyGfL|<;lnhevTdi3zRRsTu)F-~s_`e5`CtY{=#;~uTzb9?)=qHH7ICZ~Z?*(6quTAWV{ z`g#_-nQQhPeIv<#h)_jdw~>;qQq98$3lgm-1uE8$(y9Xw;DGrOD!OMfjQ6DayNl^= zF-|sHs;y7A+N-A{sPLtI^WPCU~{GAHgr`C+zl(VGwIB=bR?v_(ov;NY{7 znQadtZ)|{+&x<|V1GmGnawT#$Y++xm$9_5_1w0-AIfJ`qpMZc68WvfU_yUg zj`P2cx!(WwFMszRt2H224D3w&pxjfr{h$6rU-p<{tII3ae^S~N??wp*C%$BQ3pzFS zfQ9UFdD>R=-~IRfgX<|V%(3&1dDr4n!-;QhPR#TeHMyQ;TXFx>|N3A4^FP4X#Xhv% zY#OsB+7oc_L1HZT7BUG7@$aWxxu3Mw?jQf}{<#?W?VbXjY z5&4a{3U7&1P^>^!w)y}v8j@M9)CNKdkl+$`EPHwBZB&@P8v`7M=UkBhE<}sq7Qd3d zz&!VvmbDA0c#<20f)mL968KzWQgXr)^Yk9nRUYs>Wh526=>* zv=?R{2Xk^}vf)WpmcFgY@=OX3^OJNW8WQA)<~zubODzdEXhvh4eh}s$O&>@qV^0gE z3YrAe0HD@MNzkjO-j&F(%Jp9LJ9iLZ!BAq#LK#n@eZdAgH=|hTBOaMh2V&t({sb|T zRn;-Jw7ej96IBhTusP|{Rr`{wDTcYn^ho%C87 zPU*Zp?zEy?ZPh5+nR=z&>Ru}_u|Yy&<#LI4F&`!vQ2E}7eX`jyCo>0}WIXNi$F$Z^ zyGg$Gu~u9BwbZ6C!b$IN{O?sIe_pAqTxw@l9P6^0*?wqeA0Xw>-lT3sHDISe)?hN!k9NqfL-8YMk19#WPf1w~DiSo5`rj1S?sP5YT`#%0PZeY8Z|aC6Q@E zPa`195|MZF6XNV;H&GK^^Vt!cNALq(?4N&gUZP=c>Wk0mJ*u-HZ_AA?;}c|)YsaT$ zZ9hKo%+})*&vXYv1CRxUEiim0;SiZbhK)m}O)0#{uLmv!uq~t>xgfBC>(T~p+Coww z9FxFEN62HS;8K7OH#bO*jrh}Vlga~k1SFHT|>UqxH zwT-o+Vs?wD7#CYV&avH&2=O(afwW>OSyyM_{1&?$Z1>X|m!7T*geJoU%#$&xV=_5looRhb?V1JFXMwIRiHC8F9@ioDEl1AWG+e1a zoFcohpLW4_4J-5(&(UeK)Hcy_1h=meDT9Sb-oBLo8qe-;X&?<)H&?Zl`gyK6F6AW9 z8?k;and@8=*h~b7*vW-X0g4EfamlC&Id!SBV0qG^O;q0Tc|9_iciDVqAV=w}qsu8i z_}kNSmNp{;SsB=T6ZFcm8OG*p8MESht<8%{wKleHqr>!eJQZQqxwyakS4&omN8B7I zVaTUm3;8*C=EDN)QSOSJ-fcLVDO&F0 zzFCd?^lqmzcz{BwBPQDq)`a>`Oj)ZB@|Ma!}W z6c;JhXCrCQB;A#mNs`O6Glb^@9-H@G;2cdg;onqjr4qs6GH{e5bzkkcdf371fIG5H zxvzS0<nksCo!1*@o(1VERUk^k6Sa zt<1vN&JNsm^AuV%Y)>){c&Az6M}GVcNMrSjQujG{wGSbi+TH5M(%tK9-nWPXKmVoN zeRxFI&bL7}w;0R~cz})iIt|9$??FhXsaJk)S$#S5P8XURpRxfC;6nS0y#k=O7CN(L z&Rbi|d25R~-#so00(}!8Dj#5z0J>eEcuNu=r_>0#&w$PNLqKP6?z3GR*}FzEwC%(; z!RHucHe@VnOvs*XxI=42tKxCaKhF6d*PL%bGX1Wmy}8A-H@BGf-6QSL=kU7{Qh3^MMRyCxY1*5=TB|v|i_K(y4 z$2RSolb0So@z*>5mhW*I8Qh16!4GcD8HCu;`cr`5;BSiGiz9ITJ*!GDP`LHloPvDW z@>-mNROo>eTJ;R_kK!j#@8%FF!wu+g59Hx^9wYQUX@YuisJ>i$3B!?2!f33H;*`#E zjSL$0wl%yjRK7ueQR(e`iK?J4O-5JOi*g~!=;nr9JIH#%3&wO^BRXa|WS|V_cvc1k z{1ox={yrfS`EQhrl+G7&*ygAkJ2R#;t*mm3^XoMoCFy||OA^JyM`WX`<&3_Te%((< z0Bp50TZZd?w!w(4i(cS&IWb8cf7%$z~e2TAJ>xx^P!GnhMq&et(_mn@Q zI&?Z5-#3)8dX}%nG&icnc^#sdtx|Dq88Yi9;$>T&%%;b>XFd29Kx7b<3Q}m1C1zqp zo->TVjFUj-0LpMloO(E$M}`M2ZAfByprSKz5cdI!iwT)lRgE*P)#&9~{M-8AP`5Z%V;;BMN!@jv^NY0pj8Ce+V82U;rgr--KK_1 z8S=n>TJ8Jlx!N78`|5LX{;F;3W;B~k$-i9RM9W36*Lcz|e{e3mCi<>-DaTr7a{=IF zyj@KevIzl&x9GvoKAPpuXzgbf0W>90Hky;R8IWO+tCh^zB8rt#Je9?6xkkOb-tfnd zms};3MbQ}9)~_j78KI)|<1GP(2pV)dMUT=}*#Azzqs0gA^t4-E+^{#4qmfLA%mk?-!8erIc7Q2@b9i{Y%BE zPdc`JSOAmSYY92UW@qqbaXKfw?-LbVbp(o&8b((arAsV0DG78XK#=1bj^BEG&C&+H6 z2kZDPUEH6QSCgM#(KQK%C@fdqnh6HZrbJ<4E81+)a6!#@e&1c=Q)piXFY(|S2gCXY zx5&#gL(joR5h2kHs<)wS+a{$}_<<7-hfre$G7Hk5U`=6KpL{|6Gjt6pl!}C%Hf^bL;Ndve-9LP$!q3wGK5>4Bk4JD_*^Vjc=E^uyt zRs8L8|M1Q6+Xe^1H^(3!c@FXsJE7u^&f7cya^l5KdU5>r<@wI~?&^HZua*N` z^LrrA2S;!BpT9o3JUl+9s+Yz+0FgMF(VKDG0G4o`^r8?PNjE+Jel}Fab#?m;s^A1}-CT2!=s5 zksn4Rv1HekdA)TL*jzh-Z6<{IW17H^6Yq0RJgar$IS-w9&O;|&clunM{2-SGhCUyW zxHNR4I5IGxZA>d5M-3>)+eknQuwABrGuz}608PCgq%loX$EZy_g!+d5HWP1iPMle% zyMCN_f3Oqp6Heps_=S_{DCEQBU^y8fH!MH@Bder1lW)4S=JDpW-FzSnb_NgfIVTYv z34RXeq$d~}PggSjiJ9pr2VCy-FUWCQ{Lp{$hO*nPQk?3LOhteE_Ba2E%%O8Z3!Wl# z1|O94MT2|R1eo3Q;RADMu3%_?`@j5e{la@)s^xT6@?ooLzz3jkf+e=AMXK4ZGN9kU zD})oR2&@yDjrp>O+X}E(9Bu#YU;h4o@BcUaM!4f=Y4(?Y`s?L%@#p$*g<==WL6q)= z^VetB-@ng}Z|>v{s>X9*<6`PP`@8(K};&`uoT)D-oA9p&N5=c_M=_c=o ziS&a!fX_!R8nV>hr}uNpnoJX1yP2fqCy3`y<@m32{>Z67{tI@`GdOrTk+U9F#zccY z`|svyPG=x~6QE=npoga8%v5r^+zOo-lBb9TZMs*0jWgi-0CVP3J&HRglrkq-@lX*o zA*Xifw!)!3Z#JAzX&(OeH~*v^CL+;}s*XoDBRc#~Aa9TFp=>%;s0vz)vJ7l%$cIFz z0=^jZ^^lqcyx=&fyNMUGhN$1I`2O@8+{M9!gTz! z4~t|s8UZ(acHkEO055Eb?z%PhZK+)eohbxZ%x;Oc@U#J`zz{+5DX z?H}yF-nIHe(~@7PTTa!g**|dlmLVnfpA_LUbQotR4$cQh16nHj%9ukFRa)pAjw_S| zpWN;BeYs^A!Aolvy{@_nX`nZgHWrW5A}8KJ@xfkmZYwV)6fNb&m8K2q_VwvY!5hy@ zge_n1zI%K8-Tv7j-Tq>TfvI|Ihvr^1B4=;mkBtFxhEGcRhdiNzb*4&XBU+V;s@?RT ze0_SP-dJyd{R9JvhhzU@IvrO=M?RfirW4{3gk!aTFdp4TQ{urnfWr(w?-WC;{~QD9 znmkt0OO)wcd)>hdPT*Zkqy-IGo#|bE>7jDest&^<^&Qq3kVfqEkwLlqNZGaV$@x=S z;Hq0+Q&(}yZ9Xi*#k2Mmsk##r5TaO*+VAO=7Ng3Sj!w>-J})1+HRGZ>tm}qGo;h56 z3oYsNjwhp2%3j$UorcIgJ)I`?9tH_QUm5A;itN*xCTy7T7!LZy?i>TQ|25tKm+0tr zp?iQDda-6UJi&tSlp_=!+j)^r2b~(b<;J|hx99Y!H@a|6@@}z{k9PUdda=KAt}>F= z`@q)}+SxUZc4OzX{@h*uUP7$HTHR$dz;3FW93%^~-HlnwpHgZUm-=eY>|q=4O|AUQ`RW-8!J&CUgFh%=vZ)L6^t7 z%VGh<3l=%YwmiiBL(2q+!1G3nJm{Wa?IN`wXb8jh zVkboMKA54L*gyue8E5X$F_H+l7)1La<_L284BB%p1g9eW6%4ibPkkPk(8UNo_dFoc z<;QvOBc2CFJKL%AX*+`RX*+_m1(h1GLzYW0BOC0vAQLd0%mq+83kiJ$PjG6ICXD0z zZk!DRioJpsFZPF!kiqs5*Jc6~Vhqpu>{J8Z<4pLm%!KtW-Xt9?E|YNb9<^RvQ&3|L zkJPUfZEM@r8pz7yIJ?u%*lO5(xzUw-juL-yR`=V|+5H!1efpR_y%VR@y(@i$mBV=) z5y#EYxIO z__G)1XY7n|)-xfInoY?8No=OT7xQm)z~0eRe5x_>1ZVZdX|GZxa(9VZfrc*S3E9X? z9$gX2X*m%xt|GWrBEo9{ORJCHJ)k$i-4o7=cis@U6w{uGpK7k|Cy$1HL{r*wlO@$Q zV$2uLzTn@AG!IpttzY7-5~nSb-%6=?k8)k4+tlsVh`faNKMu;+7c&QUpR1(zz0#oX zfR}1}cuUAsYcY+esTyn5-QC?lF;`Jp@zn_4x(R(Y;-5c@Z<4>fobP_bm`9EPot%X< z;8NrSy5N0^VIoKx+$T3BbM2LX@#{QfJ2o$Fgh*5!?f>t>ggPb%T8Xg$Vy{dwg z=>~?o0u6W8g@OG|VPLy%)(#V!bgn?#Z(7939ynk#3IQL14l}eJjE2ozWn8uFt&KZs zcMJgaw%1A+wRLYBTlaSNQ%sl{peq~N5Ddc!ei$PaAVNGHti%r3mxfTkhU6c*&_qMR zGJ@hCV(ft2K&%tV`$Ya+O$0zvuKyW(o2ZI+4ev+a+pSU;!M43^2HW|tDf(6n#Z{^6k z9^x_F)Z5lBr40OddV4b(VQFRIfQftx+(Gf%s*r%S-Ym7+&C=!US-%$PfIC_F+3VKo zKFl6CKu3uFQQUgvsf&|~Q}rk)jz>ryAj+Hm3jpF5&6tAXqXxq!{wB}5c(ODr6>q|dQe=;%mFK*)mE| z4n-*wa#3i3@&)fFasDF3Ln>5V_$o9V+m-ZIhRnmJM8loD)3V5uB>fcgb7l#GW2NNn8&ewU65>XEAj za`ljT9NvzYif%EbRZNbY#s0AFuBL1;qb&;?mravtG+TCzlvoIfts5k6tKu{JfH)-x z;K@~$oKW&pE;d8O(87%DsJ<$# zVC-W~MZ#WCcGY68$+f{E$cy=%2t%g#ToR=)F~!_mh{8F?n%Dfvy`($Uiai&qML0`n zk0S2wYvK6I>wzK|HB?E>!XKEuAIq5_D;@`Y?9GUH(qDxtsf|7!jFS`EO{S_L zRE_dHcFiHaU{Rd~rNG^!H|fv%Co{@%2gHJ^s8Zjj?|;^Ng@SeA5@`{1-jv)l zQ{=aW0&C#>t~sRw(hHZS6(i~zNbsl5J(==-}0#aN&l@#Wh%yb=AA^j zy*o{DSbt+Z6Onv1CI1waUFv8mq9>KfJ5*KmCn)GEl^cn@=e{-o3JFkta5A0W_ta1r zbl%%v3J7SvX-C)hZS4s<*FArSOwNSH@t(BuF1PaTw`%3+;1}vvR4bMFOA$G`057@fbt%Peg^YssQ_un9m&Cf#Y%8b^vwomEC>#;1hkZVU>1wVs}Qe z0eGnc_!%tZeS`ZM)L@nwyUEaR3dTGvD6-9=zAbyod1&7|5AFNzDI7^OM8cwLq{9%d z8^;Wg2a%B)l>?P2^naMaOJ^fr1A>J}mWuq;A5y@}AXX!fASjhcobSUH+^X;0O?_|F zQ#gKh)|v!qK!#o+b_{D!z+yjqE}H44PG=56sX3N_6oqQ6yQ1S<4N%lh>3K zchK2Xm4aU#rr#BVvH3$rb0w2Qv>Ym@LwTwY0v^f9N1RzI{)-lRakPK&?(FDXko~Iq zy9dt?%AkF&8r}@o8thmU#Q84N;*%-&0Kv5JSMCxnh=G?X;Q+@s!U601M@n9&6#vzf z7Az}2gZVwWNx2vqO`xp6?PY;7D(V^$Z)VR>qGXf%J3f0Af$>}+3Xe`T^}2i~I`Os4 z4CMx-67xW|iWL(pI5D8;sITI}pXPd?T-M30!nc?5Cxiw>c(Xw)o)2}UKgE-hAAU>3 z8Jt$sXCs(LI{ty&k@5Y4tbP7h9Mhp>-s_#79e=xj@JnWfugo|(IWx;X{8@^SU-K#cjJ{WM~hd>2o0K~BL9j*QD2>jR!PfRPfQyQN-LK; ztE}cL<6do@?;^6=@^I^6=xd5c!z<7S-TRnSsq|^676*Sn-OWbAgqFXeJ(_I{Hfc9G z9l6EZ1aX{o+~RGU%cZl9Wqi$3-IJTmfZ~;Yn1L)DzQYuxQZ8cHz6S|S!-SSMQl(8q zy$oQ$K!i5L@dK+t%l`x5+HRc#)`RAN^`JS>{e)mU7)mBihd$ir zh6RG{3?6il??$ofhlYu`KNRAT&S_&B_-P2idOO3Ba)@If4uUn(KZ6K*?cw7b_z};6 z-wpS4>ohPQG!4uLO@r@><8{2W0L4M1i; zHa!R}gAgNfi5k-n>O2>=5g5VaH285(gWoL|Gquumaf<^AtU6f8s-5st;WlW#UUM1@ z*=u68ISum5ZB7FMPwe*JP3Yt~$6Eq))KOx5>J_4M-fdbbN zx!mPEO6TH=NKgaeq^gH#Y~vEiO4oFqHGw2zN1VXUAr)A>Q-a{ic||HlgaT53FQXAo0ctZhRJVSzC^L^4FJErfW1nwPh55ffIw_n^53};4U zBx5>n-*6eH;r$IA>(qPk&(jd~!k-H~%^AX{@K$K~CcjBBQpVU2-cK=DK zZO>^;AVXWJlhs<4ba}@(@eA*$(28Wb%cXmnX_Ml}wZ4U?Y7rv5nDY(@iG{Qe@@v`0 zLe3@$wyLW;bbwCP@Y2Wg%A*G;7o%`Dyc5qGwHz&5kP6h&U0B0LH_ID3!-J_imWIg{ z$h8_aeS!jr_n$bsK71EAGs;&zEwzx6L04!UeS)nu6ySLOuK$`&_cVwUs9%j1dXbtp zv_@T{ds2?@mwf=*HKCP|;j>cynO)`CAzBF>M$^K*qn|k>A@+k@h#gP-Dg{3R>$9_G zV4+<`bLBokZ=H)XU{?{Fi&$XDWFqV!$fdGD5wXy3IxV)2{(eL}CA<9-iutmT`rFa~ zcn!ck;q;ZFS*bmrbC=z-6X%9#SZYmql%Wj9su5Z1mB=L)nG#CoZZttG98fS-7zjDg zywcG;qVI@mg^5JRnYacN`m`$1IO6BtsFnnz0qz(U=)(pvS>%p3?jvpr@O`jG zp<)^?eTW*Yv|i0?U!r#xe_()D04-#>PgzJ1enjQYdt35QDsE%?+6lOQ(j@oUi9=3F zL2B=1sSc%4U8+K53slb0TgF__jRVFp_fQ23jquwyJ z+ytb92x0rs0y1nXv!T0#_#hGMmi-TotB6h4DhR@AMP=yzlqLIU+NHWcAd{u&1R?W=4Dw@txv5Jn!@P zD3JheBGQ39k8DecgV6OIC00CeP82C4!GxbF+BUl{k}n?|>Wd^pc;)#WGInX#CWE2VRp43dbG8vina8=RTVS_@ z@m>It4FzqUkZ$4qc63W@I-F)=dshJ6BdYc!{`gBys)&~sW%<&gO6bwTgvZHJ@3q*N zycRp&WOw4&Gf@mF+zPUi(<~aYF-Wx{OH2JtHz(%^&iUyYm3H!GcaZ}XY(i-cjii9t z8e5gD2b`DX(QZk3hPFEfL6WHRNEj4C|9UPdi(S)Bt!#Q@ z?}d}W*X?M3b!%eG>hdyHTZ5t2W*xrLCV63P7UHWM-1!9@K;i`qBsQq=Ro(YPP-dH^ z0n|TUv>K@K@q4U+5&eSwy+UXpgJ*($$n^Ak^E4dno$phR$G8wynJ?MpeD;n}xm=ZA0c$04Y3zd3q$aQw}%X@DR80&lb$?}Klw%{`;fMD31a zL@BC*6qEtB=`gfW?H3T^I|kf_R&)T1QBhsR3F$)pAVDG%bVJ%p(-RhK3ywwKxEZ zcI=prd?4W%Lx7QeC2(cz&DHV3d-GitMyPF{-B#lK;?Fd`uuIijr z_&f`Z?CcEikBm2y1C;ah654M#IH3PW*}yk%j!=b*+r_^qz&@Ez?~u_nhl8Itd`H&o zUUWH0ux!SB3B-!m!U^K=Sxo%rr2aKyuL1Wrl(-p9+bbrA#N(E}%7|(Ph$@G0L(-EO#3-wF1%o=Z$6#455f+aXM&4Ag zX3OrKU1o&YG5}181H?_&d^OBxj{R`+xMzmv?H(~lbCJc9FGX^1 z<-ZZ9j(`2je=B<6MiwknGlEVy1GG{*I71#n2Z$%axe~oc-w7scipP>G+B)B5H;5W9 z!DpO<4J9~HJe#DO&nJl}>dLu}xm)t7E>0upA%<$P7Oe}{m2#1#*#ZzE?jWSc8fWv7 zo(ET>@3Lvh9klau4ZnDF#N(Or@DE<#sb9#c7=lbYj3`&aPJDGTidjSRH%y25Z}*4uNdTPSWD+e1-73cz~3S zUQSODA0WbVCXM1a#jpS6zfRXSO&`%p-eq!x=?hv%v={91@QOPZ@dD`{BY(%)7S5be zvW~THsYEZv9npHq!UV!$Pu**o+8Ml{`boUbuOm4CBAb}xAs|KzKVv_e(n=K=F~N2D zmGpdEIz2^D^T}C8BRwcbdKZHlV*lfZPqz#T+jC%Hit*GKGR^0jv*|!?a|VA4?7kCQO=pfgl0xk8<=W>%{AwV@P7I z<~gRcS$OjwSPPQFRg`v-P3(w{;`zF**Ip!x2yf5}A9p;b(2wj5G`Sf0cO&{QnkHe( z0JCQx|7qxF>Z@n+66pr^yy1j>W=~HyQu*5nru*(8zZS7*i$A_%EWdc$@t4P>s6eyo za*Y5THWHHH9EpM;V)DAJ~Qj6O-d$6GH=(MFZ@9&k)f?saJQAVM}A;M`dkRwHiQ;R_0M)(y$Q*Ocg09JymFp!I@ zP(GaP=y^ObL2(FnRJp@@i{i^cj*R$ipR$oOu~W`G+0ncJaWnN-)8GZ|QWrv|M9vnv z0><#w(VN4;;k*6QZ%?Qrd9M=-`-UD@MtPsnvuE>9-iEY+#4Ct9V3xg=_)2nJP-m2% zIqR*$!~Xnm|NZ$P4S#91KTCJX=V_k{*#ds?rwfqx?ecl_O}>SXLqg9FyFt z3_}j(T;NqN$Oh9crHgS%H9!2>4rU9xE!(m-B~iroOGy!w^u3 z&N`x(1Gxw{Y#QfJ`z`HP@`^;mBl&yMS1sF(nYA&-BLjtzP}K}=bk`AfdrxL!GHwgD zzUH!;MVMRCR4&+e+5Hh)0B(=jB?Rq6!8&}3Z3&0c0c{zF*09Xm4(7g$N}tbU z>4E}@um|#gB)+?d1F8Pjhy*bxvA^qRER;TgpA>Kg1_ zdIa(w2n^fk4Gfj3K(L;z0a1mSLU72Y>iGI&jMX}2H(~!4LI(`5U4lSH zG+ocGC7zcRiY5uJ8IJQW3SwgU^A&`aL{p+#eG*Nf496ht>4dBu0sUr>CBOK6cF|U( z#05L)hQ7+LbOxg*he8x?Nw|hE17TYa1;dh$t6sAu$ZTCWW3iGyW%rvb3inBM#gn9P zw~zy!5jSLZ_=<*R1jD27a6Uu=$(70uD#_aw4&*0E4_Z_^Y0MvaB=RB2dy|YGp8|QQ z7e}DefPvyG$sHwmxIpqGVYhiSBG-82Xc)jg>uE>^DdqubUQswMyS2PmiHDj@9#~

%E%ls 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�juL^N{HbA|F$4j7oPONlm14Ovkn(hy{9S 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ⅅ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

CXgoWIcN1JcP6T2XH}F7!809Ti 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

BqQ4<_+EV4h7C3=nNG2Pe1n!r7iO|5 z&lbZ93$lz`N@SsF3+`&7TFYij>XlOhyzGUd)bo@)GD;Xd1(y%;i4(zB2)dJbxz5>e zK?BF#Mv#e@G&bkKn3HpbC}irM>~aRZuW}~oAl?~fOu~c{XGWWTM)4Cax&JX( zDC*UKRBxG|SdrmUOfbD9Em-DLn%wS;ry{5|Wys$+5e?TKQD3VpUvv^z%((Vq~V>H+N)H zAyr(ktX}6@y`$F0zS(=8 zjOt;6Ra>;J$L0pT$G1}9PHIt?t=qX{eYSR(raJ8}r~mkO)7}a8D%WXgxWSkj&!<~i zNpBo?7l_tiTZf*z-T7Y)Ro(MZ$HCC>RC(vrQ86^{)cVC6Sg5%opwDF@wYb z_)>>=Y51BSp_G$=C<5=`Al2;dYLgwsB^$i08^>w10p~6@;M~Oq+AATWSQnXKgh_M& z#8R~Fpye`#YZHXNN!@7Rc}yZ?<6tLf<4MfaA$iikL@)?f%!|r4V8h+_9@Yj>#rJ+d zHeff}fPEJmu>#TNvY^m+9H^;*RTM(55$J``oEy7(f>OO3L*3XhP~31x zfCv&w@OBf|qTIoz4OqVO;5OiXUu~e-QKnfRgz$f&Ar^+1WFxHA`+T8tq1YB1Zs-|N z8>i8rX9Nl8>H_eK{588z!M1g5ffSY><&-Ve>c=`I7K=vxA z?P9rmty ziL~6i_1gr*4Dcv)n|c|5`hhn|w9y{%Wkq1oLf^1updB8s7F1%B6uBROzeDKjf%Y1O z+q9HnwjDg(JFzYjgs>TpE|0}xI>$?ORP!|-Y+o80DZuh&q`sXddTOB^KT>a#Pl4%k z23~!qxToJ^$+7o6mK=MxUUJv;J@i4jNg8U76X{6bc|P)XE+C+YAHaQpQZM`pbf|#q z9?S?alsZ$-(~%!WjxR1RxoH`O_pnRu?9%=B{aJFYcFDDSExC5DCD%HTQUn7>Rxm2e`p^u;g}`%l^At^6f}~ z#rp-k32G%y7Ap`y@zYK5O1(%=SaSCGYx0pRoW1uKIPhCF=(?*X-?4t7994Y0*C!NF=GlP>A2 zgp+QYOU~Es{fb;MY8XCb<~4lshNP}ATCywJ^pfFwmCW9c zSQvhq!gI`6wYihz3~fgA&Bt5SY4Z3E8tvI)Rr+;z| zuf98YI7W8(Meyxya#QxF(i#2u^5%VGDc;yth&MLfsDOfjj9>C(I(drTn-jXawJ*A8 z$?#EXi(|#R6g5+=p)gk7wDt4V52L_KA`j}smJfv(4W2OmM&TW<&iCjGZU$f0D2k4dv2twe(r4p1J}cVax+kLStL z!j}YHC{#_C$po&<70w$Rrtb?*W!2Z4=u*81Qm#aoR@dNPyyfA_DMX>BVLhKwLbX;A z#!&*9s*`r3YeZyAGXdg=prA8iWnC~WD{-hM?|YNwI8>rafgEQ#%M>xC5WXpjEgSxV z`gBn*CJ7YMXuSnXkJ499Abdi9_6zt`mH?H5!EIFB$0S=so}>IpC!~JhD)Ad@6a^eJfKZy2rpL0gG~29) zgxKIGKdOFUv zyH#60;S++9EQz{ynk68MkC7fe4@hXI-}^PKHcle@TDU1C<-LEh)Up zvki~9>|qyE9I_=a%D3Ljo@FhfqVi51K@S_dH-Q~4_yOK~@sIz&yOaYfx#%xT7hM@& zLBo7tZHcge=ha#W0I8v>Wf*0MdN-!(jr5N9YVv66_iCT1bg()4jnOHTMlQUTln7JI zAh09!Z#ih~0$Gy>TyzYXR*zGfXb^S$*tASHcDzy>3irD|IMi?*@!+@-K!!0Wra&YM zXWov&I8AI1ni;NvuCxl@rjA7t94bdAhu}-e{(6am=d%r}l$N>^wQKTy^0diJ%~~@x z?_{Rtoy^qiV5T5U0G0+cPpGhiI~dFF#MFw2)T*J8IEw3`-z^}1VFyaWkpT{d1So7G zA44@f_)g4J|7@lYYNoZKwx)h3Gu7{8rg{f6g#s){%kZlMgqV)rf7I{LD+dyGp#Brv zM2P_9eQ*NDzL6#=&}0_r9fx>kdzw}fwMO@!?pWT#nyFsyy!>pY4;FfUSfm5Eu~(BR zm_o&8U5sMY(pd9lDeTmnNvwo{&Z!DJX)y9Zcdv1#8$4F@ZC~gI025L045FK&j=|gA zc*cPgcJJ}-8?B72Qi3sLbV}zFhFc;ZEym+ z+-pc7Flnt&AQTM8D@=@#CCvKbRAaJdgjV4;K&sLy(gH%1pcQwh!k5)0P+8?R6b(^w z(I?4=3{~*XjO=tM)mOqCTfgqN(KPevkqeUjn2}#|AgJG7Xon0>J zjQ9dT&5-sfTE*QcYNrvRN=ycj?3rFKPYPc)?Jg`1B5Q*%3-KJ!+)!3!!C^VNK|`q= z(Ia8!oYC3B!5a#*7CvU!I#7qZ;q8K8^k}}MFq}a|AXn}2TKA6kUJrJ9Acb9Fk@)iI ze7=Bn5SV_p3(hgt=Urc76$H+66`C;#>MHWGHz&t`4kY?;viyjZ3!>NOh^m&8*XL|< zZ;@?4cvfKn?SrL5VJoJ&t_hKZX>Mq(6$2JVmF(_&rsxm+V zy$3G0lJ)Bv4?*xImj}hP%RU>CQ48`etxZ_eK;BiC+O2t5zN@Vc)bG$f5(-1WdeLi7jw7q2&|g2nm>(fDMJ}yAHhyRGp$2uXifpmkRi%2H(_raQ4yC z%R2_8u4@^Q1Gxc!)-Bt>xj{L`3~{Q|;$I8;yu-`!q5L<$ilPLm`k7ZoiBHG&PWw{v&U z1{H5&ZVRr<90aE)Y5vx(4irHiWXI?DyIbwllttgDDck(EahT zTJsbwVf4aYalTf6Uvij9p>&s;Y^oh*o9E4|tkb+af0~b30s*}|?`6l6b0(kkmKfoX z)`lV?a>LKiHMl_aWR_4tY{Owbke;^@WsMa{D)YvQ5+B213hH}cHO&S10!Ym#QhZ?1 zdv*?|l%Xb1OdLe=ZuLR$XFoej$df__IEjDuvo8pp(|a-eFa(GfUKaVza)?|aG5H9r zHZO2r<^Iw5hphG~<8#hg`0w=?GV`KxImd}S;=f<3F$VV@y`p-? zM=?K`Ea+<>8M(j?a!iu704)p~h1diiNYqDVnb}1j^mcasMMCvu~1q9#m1C1Rl~GNY8Lh;SZyd{8+h%+l+uAAEv4e}=2A-Ur7@ zNa_Zg&=Iqohf;x!7DOnN?V}z?IA;|915_HHqw2`i+e7LBpDsr+E1$}J!Hw9+Y%E#i z78wDVj6{`)hu1+Ck8=o)cT+OQU3N(FJzQy~Hx+Jmnq8)=qM1fYg#n$o#GRef{9FVE z%33~@4e9qNgOmKh9e|pLCmEI0b0h`k{a>G+sK5CYQx~wW@6|bIFFZ0YM)TEcy+ycQ zy=yI?bG7+qdrbCC-XBws>$F-l$mnqUP|DM`;yXnM=p1F|WGlqe8Z<$z@&nfL>Q*~t z@A6$62zIya+8)gp+Qcgf-W&@dm#4Xj>w!EEa6Qz3{LnPOMUwi^MFsgz2xu0*at)B{ zd5HlL2rJo8n*k(BdqD2I-03Vr?GZWRoD!tl^(gWa!?bapo(^x?G4vp*aMqtfvrftF zogbb38XEOmWbw9(-F0;FTL*bo6Ri%fuXcB<((Z0mS_i8#Qv-Ey2ycLp(E(u-(4b*# zBqo}`T|`=e9{Z4TL=p(y-bfd?#AFE*BlgS`Og#2?V^!{t$f|&+sK*p*M3h;Ns@P&0 zyzAYpig#_FRRr&sk7rI8*`WdWsDW;<*p4hHU)iA-r=AUtToam{sRQ{eM+dtudcz{@ z3#!ksgghpz0>hm3;8vyo{#sQFwCHex<%_PS!@**_Sl8{W|9&G6p>M&9>br(rS-2Z7 z>U29_L9cHtE^-z^W}Ei@3%GpH?t`)-dQ1?n-ooFT4$EnXh7pQHM4;~xW^$w+c4IVX zbB+MzG)3<75-}huK(Pwe=#%6lxSg29huhZS8dCtXvr~0eJ2@O}HI>Q*8&3D`*bBa) z2G~w4u<=y3L*w$j_SV*|4M47P*QU;)!m5@4Zamb0#;Tz7X;A}Ool9N$hlt7bZQ_OP z_|5f>)Y(?wek)T;NAim|Kz-)vmp44dTgg9Lcmh%9;yqX4x zTmlJ0AXaM&qgo!kGlqqVB27ouFzfK_I`WB!-Wp-~W7v9U3O8wtIE?cUPf{pIBWOTQs z_QbNjjxaX~Y%L}_Vg$Zu=z(MC2H^KebLa3;~={A6UcK#K=jy_hc|JI$0=Uec(M||JGeJMPp_y&FfChWE0nIoEN^oE#)WJh(aIdLW@%XHvaC=DNHd>R9YPZ3H zstS`qAVDRTD4wpKZB1Kk*6$%T(iAs4bt)dy-r=Wdf4{xZpwx8*EsqVSuklBp+gg9be*YCnS?u1J!6ss1%(_l32oerV;s;Jhu zbxb(BX_9Ap(4(3edI0BFW1(?= zl%*V;$2?X1vfyHu8hVFLUeVd5Tpa!Y0yhPD7>*KY@>KOO(g4AmpDtlfuA%mFW`Zh^ za(fRPdP3c|;Bxt5O+$8`pUdhp1w24yO`Hq>$%3JvdU(7{R83O)MWv29XFhGz>{oQZ=dY>mp9r<^^`5Pc7$hC;GiZfx5RS{fp#V24d#1ZnDb03r0p?4Ux zGirue?>FDTTbo)r6FJ^cRg&Qnpd%TVy(20*`N5T;WXG?W#EUf`PVr7-s02N~DDm|c zZ~j+2KAi^jzFv-!9Two3-3r73(6^7g&P$IKi_Q!TaYfx-f z5tHM7q>S7|E#8pif0%JG6jV4VD`T!MxR5S@6} z)5^!80{KLs;lStc)veC~cG!tgrN;o1L=mVH&RnrU&T+HF1Rf|{3pmf;Z!W@~oK?$@ z6+b+$A#UP05lo$8;mdvka_y?(5?N9!y|epm1EyKqr8l8lzWZQXZp3l7+X zn*ApV_7(45Ov%G(({oU>bA5K1J)hna*x+KA)W;akz}$*bV1KDiD%h!QJk1BGjUt+S zws)D&Rv$~W*}Zi{ks8F82kBn;>3XQ6FU5+6w$Q0j%q z{Uvb2G!2@0b+AOKZNn`UKwi$JM<4{3nL}lOCDE&wr8~Y_;v6@=AZQ zcfUc^jNEsa>S?4=8#AL$JqaA{6XSk&sd2x%)aY=RdI>r?kP<}-GzEQu=7fpsk? z70I=cku;6a1t(aTLJAc`ayk?^!NZ%lpyh;S_x&-EFQ+;G>Hqo97p#$X!4EE3X z+Ys@icr-`m=eIbo_mr}_N`esqTJT??8@mxX2#TRm-$RQz#j9a3%lhmoM}agWT@|x! z0hb46?zzf24ky=3#XijFBx;G;$*=Y)cLCDilWaHw(}xu9CX?bNGyffrxSEnIbDLkG z_=*J#Rp25X9R#cQP_znot38Oyn& z|BDxu)%18j>Y<`83-sJjUqg@bbC%78LEbiQN* z+?ge8g_IOy4!^#n0f-NfxZUz~LS1Z#R0U_37Sj(bN|!YR5uF{b{B07_vV6h)GBB-K zlK%V;zgbRK%`+c9d>BMCw6x$}pqG@C;^^j-?Pxw)Y)f+&aGv0TIJ4PA<)-}IZ~ki0 zaD2$MP^tz&KkdVU9Z=iDIjfQPHcw;wF8SH?cBFDgi~el5c)ko9PNk41IH0TCeZ`l# zM+t(e>DaRUYD&tfll^O2^IeANR3%QHv$b4EDx!Zb9f_*#gC7q43uOLk@+xaP*Ma)# zS8{9qrQ%XsrCuub>&Ws@H9}K)n78lA_G!V6+KB0LWMER6JTd@Wvnn8eQexxZN6e(lH3H@CBIzP*{T3~lKSq9OEs|nOkeE%j&1KS-h7vS&u2BgiwH}lAfjIRmcknOe6 zO=E1Wg=Lxs!7~Udwu!r$cU$bzljWcd60|6Y(p=Mv zZP2C}K*j<>5!#lihpy#;6D2i4pFxO*IELl=;6?-d!Z#vg9q|Z&nqe7kCsVYyfYb=^ zXx`TpU+tg3d^YW=wPn6Jr@cXh2Bh2m*{ko~QX82%I`j0@nvUMt*t)R!0|*2RPh>zW z$ue#5JDQ+zLFyf>S1}0pAkYb!E$BpeK2&y6aJE}P82WmMAU(!Yah+w)aD7E{*E+Me zm5&{O$a~w`r(5~vkat04;Mp{U`?Q!+`?18j8y{~3xpdV=JrRc@wyO$qWw;Uthj3^E zS2vvn3SSd^!#&kh%$(hBP}K1-D@wZ6j;nq0K#a#4!LwR&{Q8ojVgOq{2v^z-=~`nt zfYauZdQO)SNj;gU|081ppZHhQ?h?U~IoOk_XPIn06%S{w274hz@+ zFu2ORKLt~=Cri5<GoX`pJ>AB2$1Y{;F~Jm@y8 zzy1kVwP|}oDlmTf(3wcU$qWEDng-Bo3imex?k|nKIP@V$?U+C+#;8I@s9NHci)Iub zj6?>sCDz*o(Zp(c8;>^gUa!$LdP;g|SxM}H16)EnJjEE%5IlWBDZ^?s?gUjJ0XSQPO>Lz_}K6pm|G+*s_LwM=@5a~2pD)92U zFbF}$5)vX5E~YVLUn1h>y&0v_9vZi!8 zA!W*qo{Va5beu zi~yL?MF9zsY;{t2XM{PW6HbFM<+T+DF|z85KOm0cSjq#uv*i5=5r;YY-QWDjfBH}V zn&6aYYEry{d6}S@>#4s3F_OY+c>LLxVgJ)H#WJo&EUGahub@S z3#_hr$JVbnD~3dNfE=X}B`(Aq&`pgv9v)6eU;xJfk&kNXv`oWAhid8|`$tsDXmJ8; z($t+WjIqRlMxxK3dQI&zo6dK%rcHS^|BIHKpIHij6nRGe&X-` z`tN&LcUwR2Az-%8%t@I^dxq)(LX*zbf}j`sy$BV{31Jy#0YM6|IhXEpg+uPS$=$9v zU|%R7DhjUgX3M{x&?)z+9T~FI0wb6c$RWx5-xNkgrPzA^{I~z+|JvF4-CzG#r1Lks zWwZqO!>=(Hr=*Ry^8$i%S-V=AC^X5pswa;?9dl;CrTubwMe087y}e4faRK|H=s#24 zg-phql4c_AB(#g6cLfp;w+gQ@<4!i(hT$|rkcH!NlESIY6`bY+Q&Z+5kHZCD%irwm z{PW-b_y2+Xzf*BgiE0>JJYUJ=pmagh-!3L#eZ?7Wk^wE= z=QBb|e9Fm}6_wUTZs7>B&KUHbVcs~&c|GG6vE&wqCi%z9qIe?AX2MgDlG!XLY-zZt z-~P9BQNR7KVUI{-&bgD9yWLKv;=50#;=c1q(h1;$p-N|Fa+cuuJchL6U(GYM4p_sG zLJV*R2Xoaxg)OlMOCplxWJ|qTeL9{0=Kt*ON7J3eAccN&69{eBIRX13nT@9NXT#a$ z5GoCPi??u!H}zNe1 zZ;*m3zrXm6a`$?LSA`|0t?v!k=qGrVHu?>FH4I6nYU_{lqZ z?c(EVA*lEF!j4yEhdXe^G+VRL&YF#O*4&;KhKibri7YRW9HiYCK?KhR#i<@RF+A-M zmC?|Pz*i7iVHDYB>O`&?#(@?`aMSFth(aFMHtEnX=x_(a_sD8hRH;!;cb;^rD==O9+*s#Q@hQlRT?nsCnEQv*D~5gIDNTIU4B_O?#9Zora} z3^G1j^N+yMXtrm)ojvRA?76+8;RMj~LJOZq^5(h@AfAczau`F@JV8(x*sya8Ert;P zLM6fqk&Skt4@Q<0K`hQ$e>$5kp7Xl(*`9yj9SyHGY2VJ#(Aqf~S{Fyd@`z483Jny> zlEffOAe2Q6;b_AF_&P#jBZ&cpHB;Sze-QvC1}F(c4t&qWr)5WjI&8a{@>aZ

Y2|6JFSU%^ZRUcWIfw)>C~ z(tXGX*&(~88zp@lrjUl-Lw;)y)NFQ`At}QLk#S@@u^(AhYJpAN2_iuBV8MZAy$3f; zn7YVkubrxTU2hr2>3$K6)34iWe+DC@X$|ooGD7@^jF9a!c02%kk@F5*Z<;_!7}+@_ zx9)>a&4y+kSc-e(pGgusMjlTJ?rQ*m(=f!J0CooM^10g&oyQ#^UOfT+=QTn$2pMH} zQvf*yIWhZWUOYyqVr6_CwuWa_ULkK0TR3e-;vmETxmyZr&q9twKI3*~+mAXa6bgKnyh zs_os4KjM!0?JguW>q0HHi`$xcCzszN`Sn0Ma{t|EU%Pn=6K~~{JBC|Um$5S9vYZF& zZi<;W+oiy|yLwmF4BTc9%le15UX#HvURJfTz0ti>S<5$fv4$kIO+`EuYtTEtq2j<(X3O^J%iTJJ7Q zrXR!aC=T!vLpr8k&sDvUQV0IzFZT8TJ~j$hL~)V@gqYh@<*|h|t+Kd_5?=Nhh*o+* zDyr~=z9Hn!2XnpmfjOL4M6z23*(a|uQeTqZIEaaEB+8kr6Rair4ektehZGPrg9*x6 zG#1p@Wox5UT&1+W%7+O}CTKnP-j$uV*u5%DH^~HNs%SXz-N?Vy)1r_N#iz;8VfNnD zn0+bg=|V@BryR6?OY#({^Ub&r#}g7cqb}G7gPi>N4PA!ou9pa?uWH-?4vO@+plC@74k#geC{?b+bkb{R^|l+)xU7qJr-8IIoq z8C3mF1L6hA%T0keCY}{jOur?~U@4?3q}C!pu%$$fAegj_@Y!YXT;3X3Qz}+bAD;4bwM4!`w*eO*8AQt)xg1%PZ?w zG*(4|v1li;ir83~=b*KjWM-^#H%4inmE**Wh)Mx$m|yG6Q-e(h9$Q{>4)}&VXTVUi zV2UW-PTTet8$zl-XrcOD1ED|6@9)ni(#DGN9aoD2{P>^>#=+>gmHXPQV%`-U9W(Kv za&ytRxmT0p8;xuRdlbux#Hm;`6(NoZosHT@+WvT_R1IeIBrD&0J!VP_m)K93yyN*) zRzj=@GJ$T#w}2B)xcnMCoDQU7@8MkkIDMMqi#Tv)c}#`Ih;1NmmxXn2@4Kne?U;3S zzM5eq0oWO$9G~1MRJrIUOp2}AW&(B1vKNwYKZB&4e^JnL` z3lsU*@*xWDI>BGE=|nSKytJi@B@DRKlv4=s-n(qbG$f=w%R0e)CgNa)A!sTI=E-Dm zt1~&$o0L*h=Rb8o=V^MMXT6)Qpd_YLw#vc8gugpZwkI#&U0ghEnIlAsb$|?@{21eC z#?mL39^i;xLUcV6;H$J>@ZiCMy|fMSWz*Bm)R)B*O|E7OS-7xgvD`!(4jN1@-E`1c zD6Ju_cTF&NwRwjW+%@ss#%H!aY-CWS0?%AGbA6EI;ap*bu;7yz+Cx5kKVbNvJQe&t zLEyoG9D;-g^d3+I$)G}%c58=?o(G+WM@C+##a;CPMJ_2CJs+ox$g`3N&LBuhK)|mb zcLSx2*+{$pg;0kTvC{@LQbWx27|0?Ndv*vXQd{$R#Y`zWvQyT-bMh7yL>(?(!|`5`1Ip%P#3q6|0R)pVD7%xm4<}`W%y&pD z1wOz`wb>FvR?_z$eYSYN%$7gP*%HcZ`EWg+^#`AuTw0FzfLY@`VAgDp^Nj;P_M!5F z2xA0qRsh=Mt`GN>2N9QEKZ;#&vHGb^YKXp_x!{g){0zjrm}nN_6JdSUgcQYl+*z~4 z^7a#*HCtm~f0;+yc@1%Pjc(1C@OV=C3+q>w*|+Q}pU5)%RbWGdcFYNVFDpM2YK{D%7})BXB21E6-Cgwf|Gc`VM^a5 z4%?D%#na?Mk|`>9Ag+edTs>uZ_P#ib#OolQA!d?~NBhjF%$mO{!_jDO5By5z*BQ^4dP`59j*3NG)j|eaM^dGE%GYe`;_Pg$Nj556J|-bB|GA!@ znY)2`?X+-&EhKk$Bx5N8O);E;2jtdJzn-#(aJ0}BrxhBf;5nWk+A|oc??w{F@JKSB z9gyxo9(p&)z`x`OGbgCFOc zSxK&u>nPFLz!EW9=vvl}t2Xk&_<=VOE~d^xHe%Vf7)04nt`@Wt7$rB^I&q`5q`qI4 z>c!JK4(qyL@~r;ipj_iNx&`F1g$K6q2Gk$gF(|q8_{w->jb`pPy1$Kv9o#LCH<8O( zop2+6Z`ifA@h60huy84$(afuFT_14!8VhgL?OPjn_)m%eC9%|Y8bv-h^}*%kQ3=EI zvThc6a0)Tf?vqaIR-dX^W0#H$ZqLggE(C3eZs25)aS6g2Rx)tIrTij$-6-V{UPEry+qc4f*Zx|g^ec?B z4I`q7*BIo`JBp}K&aYO`Uz3x8ZhzJ3D+qb=+<%rmvOOb(l--I%x|I*35kXMws z6is*3@EX96I{S{jR*KCDnoeOT^sN?JPo|)Sc8C7e04@o?D$%2Rh|~|6n3_E2by|6ZX;|{J7&T21H=$rh`FV^o^On3=H9K)u60SIjdgH0VWFf@WF7O$w zJ?JnpNx&e(8!oW^5@LpS$Arf$vOQ$N!k}kKh9DDYbq*O1d(9|3i(%{$TucHF$q28A zOf?m0k}xDDwJSrfCO-EOx+5B09FQ>tM*6>NE%d-*Q>$%W?QuPGl@%_!nJh{?iwPKT z#aAj|f^A2;eDB2|72r$JzBi=zqb6R&C~ef|IW_NI#)@Hw*QzRES0V9XW8Kl52bt2i z{}<@iOdDZgTuAhJl1ZMX@1IG8hIL=Ww#Q#B+HAu%?KS9JtEML+gzVl zYDYd3j1;0W@wGeylaMbJmY@4vWrmeq^|ZPs44;n7E^t^GRhG)ws6Se`KIGVI+#bb2 zs-Pdw59WRBTlu4MLf6MCgoXU^8^XElOF(hjMD3C}ZliV{$yd z$2yxJsV~dquzgcJvHHO5ZcO*`Hy@VW2c=y!Ss827E}9?Oo@%+P-&rY&nd_rx~>2{Z-h`V2ux}|1Q z#rQu6_W?pirEqAc0-_P6>uLi1l)%Vf$d38GWa1ZFG#xxXZGy?z25ISR_X=-*tm(ET1h|a13_SwE;`sFH$P$D?F&PYO3uihjPYA2X4x&Jz#~O!LGa|NeiSEA#fT}flm9nDnTNcji2XX_f(urg)4K(0)WAX&xP;4^HGtDhVo&k7v zg3UIUBL)v~faddf%EK5>k36PipA&)m<4VS`4y6&v>!}plV}0(4!f^#^p9)T%Z4pdzwoG0 zPuC0uC73U&x^N>7&S25J64Jzp;WjAq8uStQ{)MNCmNMMKAF}zJMvS zsfObyt$m*K5kcvdE?u}qh_17;1ylqe2fLb4p--?m$XtD29kJJ7vrNcii93z%yy(bl z)09V%n9<|BxIg~=e_?@BDAA#`8`9gJoPF}7^YKTQabPk|mhZ*+lnxH_Vsk>vUxYR3 z56@q;E{>0|c>N&Xq9^S6q%Wv@rBt3)N78Gwq#&Cc#!l-F`0a%O^gjQl zrI}oNv|X9XclsUQ4<<7*|DH_-pGfEl5(Jjd%02o1?Cn#4!ISF|B8^w~)i2};t!#79 z@sj_wa5Bj^=7ThEJ2Nk%d_|be5yBl7IDW&@adSJL8KK(hNEa5+m>!ANxxlt>BVn3_ zqybW;q@M+aE!S|eol6SPh>1pHtOzd!2QR@?85JdpKM!VC-&N3vWTF#wJ-8JnSajN_ z3};(H1Uf?TfzP=S+I?}mDPhRepvQIei8O3ZJ}Nl>eC1(YgnY9)9H>x0YFH(X*9JV} zIh?21wgio(fE>oo$3@IvWen z|5mB5&0;!XqR*!&F>YELIqTchr| zJ42Rao?T5tV+K!keG(^!rh;j5&&vZ~;0s86o6D5=2{pMS&l6yw%%xy>p6Ol#LJQ1A z0jwBj6(&T{v&s#vsl=v1*SHThGrC-GR&W^bC7PRIw)UL}UGfulzWWQ?=!WRJx%Z&7 zakhxA*B{z3y1w-II)6ewVBfqsYG#B1uznLj-`Xk_SPEK=o3z$3t#4>Y)%BU7tx z-}+b(kdq^UmX@0Vh3MExKNeEdBm(Np3gV~-4SNuD_@0fFUn>N61Blt6dK3Yh*t2T9 zE>_TkUQ_*CV6QzFSZmJ(h2dK{7dSv=B8(X#-8UvY6MulSwF{~^HwNQKx`r3^`11X` z^KT2LwL16O3R1NqiuI*%B)8bh6 zZnMbVZ5G+vlM5p_3iW6o^haUhVGbc_J_9r*@F^aR*nVcYF^F%#I}ARPK6n9R_)wr7 z>j28)M^0@Pxk1niy+@oy_0#1qv*^!y7Hy7+-;EJ91xB*llYu`ItDp>sue@CISU3a? zSnCvld;3m<`rM}r$Pg?`%@r4T0+w`eyZuUvAz7#;zhl3tx%bR@i%*R43o zQDTJ-8j=8L^k37=M-`DbQ!TZmXHlI0AQUoBE(^LD8p(Hp(dAWYE#J=@T+kFXA`!VThO@gkvf*@7Z42DUwuf4(>b_+?d-D zU#vXQmN_FV2t(!8$=8m`U7bR+88ioEFr3^gb^+P{uP-mpww=ZDwmZHxH_Fa|=s*<&%vWt>xWK zrm{o2@AZaqu-Q&_8cD;QV2vMrKqr>XKX8<$X5- zO^<~vA}i|kVbeH)2N?vU;kr?ngll&Bqj!hr7qZ6+rZnH@!Jk|CvDnwk{kXBEze)(E zQPjA*``GF3K6bXJM+63x>Jxy0hEd`p;%fB(0gVFiC0cP8s5+!^x)?LSB&S{I--1Ni zff@xUHzOa)*miB~co5rt9WlHdh(K_m@&$ z-3%5Hu##GbmdNuV9oLX)GOE5y^oI?V?bz0eV4b1<_z0i3c+zj^Z5O&0p^Y(i<$|~# zd6Dw47Tv4`*m2L)S6tMSOdY13)(iMYhhwT3*Q)<*c28C)!C#K2)VjPkgeSjy4-J$b z_VoJQ9vncITi90Bd*5mDuPEDLo)J+ zlIOJ#m*@?ETsMTpp*$o>-3URe1hD-G`S92@5Z=I!5hD5B%WM!e z17fkru4^OfycgKJu8***k^9M8(&X%F!i z;&K+iSRqnDLffi$401zykJ~Z9zH2>t#{@gF+c!0IxMRocK(GR7nJ`6~Akqb+o&!TX zwc)eHXFFhD0N=CUL*iEC_lb>jGT=Y2qQ?QUlnM-q}Q-GED%YA!&RnPg74$pu(kUSat>&kL-XKIV6W=m8jj^qq7b zOD161x{UK!BX%#Q4vN_Lbzks7Tl8`qIhOvCn6uQ`mMhWbwFPUTnpOYX!GZ@O)v_PhqkU*T$^Ngwed&CqajJ3*W7 zzh~Zr4zoEbpU zq1#IWJ7jbmr*bfIK`S7z0Il|s&I`&q&~qVsb8(Yk_GjSi#E?y-^_s(XeGTUNZ}rH1 z1OWwXVMr~tlU^50I$0RG2;QWj_1eQGvNagxQM4{09cEJ2)hn54Iv5iV%FyiB+qY$DvIw`9eBb@bvaTaqid z8~`^qR?YGjZh;*0JyxPm1`3A?$a-foF6#&O##(ol;kEjH-4$m$y%me=%PdO`!V35x z?4z2F)|R9EfLhVOrOg;Sp{zKTcx)y>IH5ODEcjMZ>=y3^HEs;Q;} zf`McmsnpGj-z*qwH@Cb4fmziXa_w>3llas%PU2#&BpipLhYO% zxGOw5xcP2ha|(G#yF6rBax8B3AZxZ1B7Iqaw$Se-*6Z<;2$uR~YQF@!n!j zxhAGcQzP&9XXAZPcEdx$qc?Uguqf#Om6X=0yj}u}6-skO2CH)f2u_8j5~$0#r~&Xw zpF&4|CMnKQiWL#+^=!(E7vFLm7-sP{vq9&~jvgffcZBDPBvS=i_aVFYQ8Zh@}*>}#Jh!O8bfuws~OK`Zwk_FC2P^-D{t7a8yB|^O;ENq zPi(L38zg-r#Z1+p`;N_r^4R&yYm(YlBPHqG6kcbDuJz80lw0GaPLzzoRdRA zZ7K7a1*^is`>C8N&V`Xs=`D7%kUko-kku@EiFOYa6NZ{3rqlxbnM4gKUS#q&V313b z4~(ly$s)2z(d6Rb*}GxFmhwr^5YCE}1a;5E49PK_13n%aM{Kc61*DlOMMaOJ`KCpZ z7GhN=LfV0_^{=0Ibcw0mpU3|2_V0D@wk|YX=SWg>3N?kP z$-LrV-7|yLh{(Xwj&y*DF$_*m5FL^f1d_}vawHD1Ok8LCl9s#5zB`r9Em9(=t@Bt=`Yc1)w#rQ~%P7EPVqY@P;> zBEP$x=I*XBiRHET!#d9Q`mNgwBiN9?gky#giO7W~33OpzlK6fP`UlW9i*1UkB4THO zlpzHvm*>Foj}QeoeNyCUEIOIGZWnaY_G6P)*W`8aV#El$kjCt~i3=Q4AOAmCBO(W) zacwVdZ32(C zSM0f8iF4XogAp&jZ40pZb_HoOZd07|6#(&0@3HwH?MD1xu>FhjKdwC?YkYBGg9en8>Lpt_L}Niy@UH+~Zc25D~O{P;f-p6W;5mHsv(omYq+? zc~>74UA@IIxc<8A=eU2$A<=YWucO94Ys!8*~ zT}Nl#@Wj@;VK=mTDbJz=%HiDV_23dmUJLS{9AvjZHxc4R2&s@r3|23Y2f(3bKsUNR z9Od0b-EhxwT+3Q=i~C==8Cr|xE%2}zNbnLMV`I=B0_g|Dh6~m+9IfLxP1m6!+D&ct z6%tc02<(SwIifh`co1yCEC)L2Sh4W@lO0hZms`HL z41Mvcxq&O8FK)x0Il7br7dOo_W$W4Rj}yT+76};Qu0K41OZ=LMD{)h=_zB zdM=Oc6;*fwAPH`sq)!!t${JnYgN-^ha!$nkJXI(UU9@QAYkAqAv7*!2FF9(bq}kA!unEn4|KMoz{_lK~CTGig1@fyY*@^{(}_5r1Qg*eKLBXD*Qx4 z*);7)rSwx)f7eS8$BA+Ftr-B-?|1=y!$irTim@v++7YF`uda*)7?tUz!@#6{Bg)be zV48v3tq5Y=FngXbCt>IsD0%tj>VcE-R^NNZ76GgVjO5iSUMP9DVRz4OMzDn(&<004 zyeIGZ@5lGdP7>6T%FP?25JOucc)||GXcmeo$f~z!Le+{#S`>Wx^D78k$#bj61G&^P zqxHq)eL%`axl%U@m=-lwlT<#dX!>bc`W7vxr0E#9=-GwZoDnn@-5BCF+ge;4*us!x z=UYu&F%^1ngso6koFVQtAo}}B*177mTmOc3_85cv-`wWY1Aot$o_0Ox&z|xXf1s8b zzm~&YSjy$QZduw## zabU{B(x>ExAK(Lc-b?SX6tKf^*AbX2*ex(Z z=1O*|Plq*6=x>(8Vi1M@!Q4G{s_{abBU;p`^{y|6%(Uw5Syn)@fyB{Lo z$j$ILIX!OhMy$DUTi--k(Sy-d|z z7d_wFMi`;j=)czzn(5k>PXiz1G_Z!Ut>KaFofus>Eo|S(5@;=lt^=O745A2j5_yAC@b<;K_l-1f_2Qe(kv}Dc=uNoCts%3VU0&nt@^+tH-tMz& z2hlsvNsxe)ctH#<#4rX8VlM+@NoIM3UHjm*3Hu@9Qz`v~n0ttNGlYhxkQ?pvv!&T( zV}d>U?6QBEUBAq(e0I5wv&-FmcDcLHt{oikY2N}Q1oJTo9gyv;#nL4@Ps)HsHX3 zQ_f3xPm*XbMXu=Vjs8^f5Ny78SxtrGugTk8Twdwzo=^fr{h&_}8155-eEoEZsX%TN zGRa7oIrYBQ6Z&4{nt70UY>|-TOSG735r=)Ci6fByi$Xna>(u?@+kJIsr4GwfQxQKI zzp>De<}Xs|1zt|F;wxv@@Jd`=3v)euHb~!xfjvK&0zReRdDu!- z3PrOfFoQdcCB9MHq*bPVSRwf)3fpa)atuSz8;asP5) z^de+t&;xQmRIfYUs88{>Gok0g7w62V`E8OR3E%3BkpVgbRWTud?tMYZD(`3>!XJ#$P$veULoGOZ69MgfYF$~xm_sSXpCnlH!t_?K;OWvCsC$Q$uRwxc;BtS5d zmZs~UF==-q9&#Ma4Z5NDmOT#tv({znwDqQS{> zkw($pD~WLljV~xeUcLQpAC8QcabK)PTR0oFAa~=C*mQSI)uFA92oTmYb9?KkmU^4`@fYj`AAh}u)WL_G8pKC{| zrECrfdC-7z`|2pQpc*(UF4U|UODH~iYqa4&0^&7VGhE$IO}VuYK9l0&Y!wo60@ zwD;E}|5k%#RZV4=x)&UrnqfjLAOZH#{3a8ba`o5C4!Fc}MA8F^Sm|!~!rI zE>`)}2q3XKfFPuyI5`yMN z|7>r+Xj9mOhN^;QSFUtD(xHO;;(-Rpm0#4@MfjZ(*SNX7aXOEM7pwZ~-ibyv47AY< z1PyHF%}kQ5QZBpbJ8)}=v%JupL$>LRhM$RwGU zhVL-y!OA+GJZmisU+og1ay0?AK4)$^yyq|(jSCJBfdT1HCXv&ujpI+ohAOlO_YLT^ zz`#oJBIYEOjeO$30N$;^EgIE3WRc6=fDZDWV zg@#JTALpYVX?1do>8vbei?GyIB{kYrZ~1iVTl09`np!CPH^5(DF`INI^TMXuHS06j z1}q!wnEbV3V`|L)T6ypGzV9?eBnm1%Zid7Qpd^!S;P%5Fb+>&2XMs*c(nw&tkl2cS zXz&0@NI`+5*N>cFQFGh1=;*8GbJ`C#cM6N4V(xT7H`-4T_KLbbq;`?h0(d0c@_o-n zhNnQ)8s z9)_O_pK1ci4;Y!LYvZSSfAnnIeboMN>dAPI(I5VfD)p<{*x#W)c3`xRK%R#KKoIoV z1(uaSm>d%H=#ns`mpXQR(3teSIDvY-iyTS_(n-Q(6n2v!Sn3bU2_CyYoGxY2D!tgGjyl|%RPGSXVBPuY}IFt(VFxi$> zSbIr`DSy`@VprXSwZbb@b!wvE%cfMtoy!?BEsq6)juaH!m;|s;n7up0(OZO_6`WN_ zJC_p9%Bx_W?CAe@sRpYfDVW|6Wo zR4Jt5qw7;TMZ~M5!vy@3lWB-ncB*eKZERq031-CTN$+9t%_j?z!teQZg;=tFm!<)0 zpZx|kQAR!y)|D!%BQ0+Lb>0Ed1UMLuJnzj%;ZwgGnJ-ix!Qug!k8?dN8Q66^AAZa! z_ny(k^HK0&dHe8#&S5#vH z+l)W`m9=dNVH5|Z=#a5`2h#Yws90oS0**-Zn-SE>Qf1JYfyp;jtAB`x%_Ltr9V^Ss z2v{^a@Xf6te-wH376rsiu3 zDR#cyEaUCgS(Z)oujjZq!SDjslnr^r97rwU1~}@dlO#z8G}V=UTjYSm=r(ILyuCM> zKpqulI?%uhdpF-caCR0NNkKxw51W&D8})29>=n5_<-A=AM3e3mjg+g4Adoql79xFo z<5WT0CG!+}#8fNaJw!a44kspS);7C7YmI@;fr`|SN!I_jfAxp?c=qcRC$dY78Z%H2 z%>U}W9N+m59Pp9MPs5ia+)?n_3e^nCL9q%DORMKY6bc2p{VtTOz^wpIqa=n%6ohgkC#k8CfFH?seP<`y>}?W6 z7d`$liI!q3jJ+O?exV248dzFk$ z95T5jx@9Hx=MM6@tMDpYF)b_%g`YG7BxDh=Wz>&U`ERQj!7I{bE7KxZYj;VJ^~2P8 zPSz}T`-G`~f{;)e=4aeE<_x4^w%of2))L=5G3F{r7+cPDs!IX%whLc2=@WHpDL-m~lUr%CQQA-u!jZfx>f+f@p!{$tOJZ%!iuWstE!eY2kd+)@Fv)FcaaFXKZ}r z8|9teZoMAgh&9Ka`*t8XN<~-V8{Skq^At|_j#$6QU^;*)|M9?3nM%@=Ibx3bgDZ@7 z;l-Q`&qeKL_4#V6tHJnK|=&*}f4<4h#Evy~R*Arilh zMC1g0S>YNlP8%3}Z^yY|E!-O>kJLCaN*;8@jXcx0%mgwsm@2h-nlqa3Npqf$vA8_I zz`DeYYOtslF4a&Q;3fGn8;v@~{uzu9`U-~FXk(ct4~Ayd+QPD0e5BEyGPkXvA9pv| zM&4Eg3 z!){_&sf@7o1IgWTAi3L1#PuV$3u$RspWPl(jUb&G`p}6>oqit!DUKb76yv?X4J-@F zGpUuimWzOr59u=7kLv@8Dl?BgkZJ<2|H=l^7Jz1KC)a!@5U^G3a`oqBRko&Lo5jp!#5Fxybw8w!2RSX`^*9eln>|Sp$Kr03R%n&iBB+z%>>rCB}fR ze6^>=N23)~M+jwSh{@DVUp;^P)`DXIKdPl%RFYnA7+Ry5!cxs=kd9oH+9X?5Kp!+H z`eZYhDm{0uF*eO5YY}!Ra_8H@n?$-A+}0C z`ZOjs*np03Z)qIhev#BsOteDIfmEGdm)|S}sq5&B-p}$L{gJO6DcZv%i)Y2d((FcJ z4fmZGmI0Gu-4?zTIBe?NprjKmb@ioI&$5yLG_7Ld-S(*CK>gUy!E zDLlQ`2g~zk3yWy=qdTVdHWadsf=oWTG_Yb-7eG?d`SbC|zngg9_g+18B~bowygm>DUKC|W3kD>!8$+)Z!nw&JlUc9l zhJN7f#?-q};lr~@`2RdBjr*bNTaW9hYH~)MzI$=<@&^^2 z`Cx>r*Z8AKfts~hQ>%SYOI`evN|XAMLYql7zlQ7%r(zSkoM&%8747Y(qP>HJwP$-l z;@Yn0AZLlwwOj|3_ZZ)>Dgpb>TtD*Cup9MkM`mLTa23)BGb`@KZfz>sHpl=Te=6Ev zWh(wMtA56_%I&y~v&s#(o>gwR^{jGtk-6?Vc4pa-CXMWnIJgYSz)l}@(3zi5%nIYR z&mGROW5-6UBXd2k2kL0sio+Ulb?AZT8oFqYNL=lU&b9Y-XH^5q^jDr1f0BU5lu3hq z1Tv_yGZ6#--&8cD=)IS)J26IgJi1P3LyIF`Bl&GZf_zQtHr{{vz2V1` zgjnTDDzZ>1WnIleDXVYZoWrMt$cNkhjxB6!7S6~{ z_i6D{5Xw(MSpM|pe)PHZg#YDlZ|R;Um*11O#~1toFF-BJpD)bs#GkjGgAVY?tLF>< z79KMGdbIHKp$UQD!l9I#T}5DFL_48Noea#rbv=Aj*W+(Q5L}ovh$m+ke^>wQ3@s}01YA7h#g`N$dOk;!~onK1xqrZU~o!lvbqe6&uY>~>a{N>Vk zR>1$qYLvfM>siw0>5I6-X!3_a(D9W=D*Vb;qWUXaSe0$}4Z6w5zX?L`iD6(bHQ}zb zYSSE~H)ZSUBJq^0sgO3Nuj%kGditO~(nW3D;7~%h5nB*R+>WW{$r6h+*1P1Tri>68tPh?YdK@-;!Fk8Tx zUa1W4-AMbEfxxvy@awZ>%*x!94O73oEfVM=->rJqLAkwb;h9iLuAcD;~!Txs6m%}t|?<9bx?_Qls>^p1XAIpZs2RyJQ5VHD#?L%uw z&#;lLkvKuO57!LjN?eGS*sML9Bao}0@MET;fr_H< z!$H}N<0uM|b%sCPKEebc;`MM-*b&6OAiG`TE(9xRXgj+xgSu-cjwccM?w@Mn{4zKG zq+T7IH##7Y{ypesNND~k%2{vJvD0|D=G%d$IjBP#;RQ$m?ulUBD&Gza$zdS?(WL#+ z$`b}FQdNcQ!+5fk7kD@VC&8G66AYUdQTZ|v@Qr*<`QA=7jpCbC85Gyq3sCl_FETLB zA82dr7s3>sErS1x$S{9ZRpjV2lMXb-!A%If zUD4PuTmvt10{Z)g<5fL+zU}GiKKdMiXWq0@F=VzlUafp``FvGB=(roxA{^%t#hu;8 zoCv4sWYaj4U}ZIIF6$oIKK&Wa_}?XV3i8M~4H~@-kW0`qANT8XaLB4*0Vl9gPP%YxmJ% z?LIoTKhr_C%!V8^dLA;gz=HyW8TVs&N8HHval%6!JV>$t=~eKcxDN2fDI)21m_-Qq z1zz~486Eb|c66*a&4`|a0Xd!$RyxIBkmH%ES+?@ha<~o564qM8r_+G=gqIkeVgy7W zr8Gv;L_EI{qvrAW2^&)CwN9QU1G#m$gi>^^l#^ zq;2wDu|Ky34LBLK6&rY$2|`eSrOa)i@0s3l0&!dKCRfpjj(vR*D~O_AoxGfy6miMS zRvj+ZdukXEruULQo<}_}8<@rdjTKsHg52CE3iNKgf5&(QLIV|eWzj^yHBK<9+Xi(x z7#fX}kJ;^PNwQ7e6ZM(4bjqwMfuvrbsPfelay!k272n{Tjahx z+A__hjm)i*ql}Dfs*N7LH~&Un&Wm$xCM4%XVBmuIpcw)ew^U$}BpwNl@ke>X`FvD~ zSt8acZ3r^9eM2yix1_M7%_T?aSFKac9II74@1{}q&9v;vk!C?j3XNvb=M5`d75Ntp zQ;~?O6e9e;#O^XIVV*WvQ>ny>QA5f>t%j30AIbR$5dmE3228bf5k|QfMYkYEK-Rf5 z28b_iTgRV;ilW^*A<3`kTa7GE%kP=bG6s>qFbn17Ax} zk%4(eugf0JlbQxAYQVEgTf^JvqcbxC_yC$w->B+z99ue%nR-5se=jmfMg_8ZV|i1a zzPC{ES7(1mCB5-P*&3^qbrN}tros`NrnvK>C_VM@9aNw3Q#iExl1b2OI5 zNwo07;*-Cr=vq}j@Og$oK43gg)7qG_Dd9YPg5e4f9%{}s?1T88y$fBJ^gY)~wI`jd zxNa`QQz~qclf|4cQFe8OBKQCUq{8Zu3?_u-OPSw|2B2MPee+wUTxutOlapNJy`7UL zt}I#GlA~`n#ukn{q+HCBybokx2wWo4F_ibfRK78h$7nRCt zB5;dq zL`qbh;F=CR$%%pbfO_1wzB{3!nc5RYOj(*)HSwVafv;b-(PMA`T?i&AG3jS zuDr*SlBmo^X){-Lz-ImCQdns4y4T8j@=dugOvEf1*Y-5b#gF)Oec$2Y3^k0XrE(uu z^64Pad-JJyQOxHtxu$Q6`Dmu&WX|m4+SJ!uwV-@G(mgJw(pu4ETz;8A!vRl-QU>4= zp&-WanP#MCCXG}6k+NWzJxdk>8k#HTFq@}H3^ttf!!*Yo+$!a|m{zILZIzUfajx@N zb^7I;qh$Sj_whAcNUhwDH2uRI7b-a$hp9`NdiXt+FGjH;e?7meZ@R|JF$IoN}7tBpI zBE3>JLw_#Z%J!Mcm7D0+++A33Z&W^U)n^E;&opvyB+;9%hD#1coK~*0ec=UPyFmb|k|a+=KJ==WK_e~_P+ zw(v&QAKDSF_Lt%AOw|s*rQVwg7iq~$vhXbVrjNUUr1A25Rj6fR(t2T*y-V0 zM7^|_U4mqV4v}Im8ia}Tm+wzr9GhDhu&(6>=ufEVMN{mJ8MDt%-kly_o}V1azh2_; z4V}Jc?gaNIR)1X9p6f73x(b7x=UQg zU%XJ*;V%otFAK$=??TbYX<_?2E)@Qb3x&Uf+OE@$$diH$TWAeJ4KxOxGld3>*X0zX z@(bda-Ch`FAXEVjbmaMI;v*NS>)YXYM|n{(P(Ko_mL zvcy}0D3;yGn&%6*Vgs;fI)&G1S`Sq*-$>j%b9Jil<-}`EbBUWt&fGOE)4|~A(tM-* z?iG4-^$ zxU^QJB>Zb1ONGtwsKd#5$Z&EVGMu(YQYP?4#z`OlXQjO~LjntQmr@tLOJJZ}9y92o zj3F`!Y^Co*aRwmEI7~t7?56#oKAhmgaKc}P)AG{vr~}D<$Uw3mGLW_hU4s7!4CH`P z0y*bHorsx-eu``kKlH*bQVYRG6F5jijC)toD7r#4CY93s$=O%mwA`F;2}BJj$m zfbL?)V{V=m^s#<{g^anb2|ODRxLq7jVD#i5*ogt(-XL;CC}Q_dGoT*P?AbgT+=q+? z_aUQUd$Y&xTbbqB3B-$fFmkeP0wX6wLw8~QAnCFXTsKlNkxb?Suxo`5loFV^*@b9| z6URvnwcE1;Z%Kjf&tNocG+Z`&HNe204nCsKz(`NQ8YKBIEYX{u>sMwNgh zvMWgi=vhUzYs*Gg8X7}IG66!8RJjIC3WSo-$*k@JC8H4{ld0)AZRvvD#PlYy zx<)pcdQN!hn~O8O{hdJU7VjhSXeb&B0;U9sN}=RTvcxTV=D28gA z;U?fwDQOtZft03x-Fvf|6g8UWvTuW;9Ywo6pRtK>3IQuOt|H=eEuG2z(sLpkx zQLWW^NV2H%X*Kw9YZteLUP;>QbJ+~RcQe~R&Gsw1))pSybp+%>du`0 zCY}GG11*!ncU$AzA++u`!VWN$Y8Z8{e+LXHrqKr#YWx6WAaq}!9{%?H;`I39cW>9Q z@oXgHAu&PCvumcakdOZ@C-C1KEc~>R`=F6|XD#!=N_*wQTJD3UN46L5a$tG_%Vojp zbF*&RcQV9Vp*awPdl6E}8K7KHx5(m@Xb(7Dk|?qR7=>VF%;4IqorR%Yy+p(6+Ful? zRr{+-e$elST?d-M7Qkv@&x$knp|im5MwZ+4{8brdjZO8_;>@rb-6^)U`?#@oA2-{l zCSp7UkV%d~q7C`x$b}=9YR!I#^Q_fN9mu_P9a8*4V!QA6L+s*R9FpP0^*!J6`?YZs z1aR&>?zr))atnX%FqpfwpR*_agqh2terUESLq|e8Gc)wIYF3$-$RJ7 zgUTcVH&F=9=wd*8Nxa)V}jE|Ctj!G;5v z1D$w?O&JLQw=s?aFl7pjybU+ij>qDPwAYD{yZd$nXDsv2o}=Jrt=Aag$e>F=_c0$W zE0~m*XS^)OmO?JuR1N76$+d*uO%?-=sl$B{iTgB|&T%xOzy-56mX6P9t6G=m{N{R% zOkf-g9RvOgCr**>zAo-kl1WYPq!d{sgs|5GF%yeAibQ&Go~qZ-WDrW$MYcfpI9Q^Y zrG2_@{*Ww>5zh}oz><^CPBI)IV07tmSY&u}y|`PIQ~!j_AR48Tn01bYg!0lxX7`>s zl##GY4n}k4R!MT5%C_*O(eVDqEYAip+Ih@9p_vT{Yn3yEO->_qc70Fg0B8%*X6rb5|&e$UH3cx)|HJMzjn|HvX-C(6r#!t+a*75zM9MDxaI{nTYVp zf)3#y|Ng%Sk7=$=C;g~-KB@qim7gXzpXSM&&EMU6^25nnB<7x-Y74yn;raOqXma^@ zm25RC8=xFkMN^n2kf#pQ--8@g#Q?ExXG|B8*XJK5cYR+SQ*64J-VKgKDl<-^;%}j~ z;YHI$@^9Xt zb?lr`6O$e~%_!byE*SH~i)Iu*(_oYg=RCKX)V9`>n<#mA(I&mp{Bs+bW8fpcn?6EME~gQp}KJCp>Ev75O;c?+@hA zm|bBcO_$hjPfspaAHYw!8X#|s9t#XwCXu%<5+w)MUE(;M>z6m2(pQww1h?V=x6)>t z5m)*(2P-%3Ee`U{k2fCU^^r#P?Aj4s|Gdc#^2b)6#of+kVqBMTm(4&cQ`Blb1}&n2 z(`Bt4^g*6&BzM{3+3ms75FYZtj{-VQ*9I4E;=8ux^t#;?P%LnDMsC!{xjREBrtbkT z?PLi=1k(h%l9A)pIDmYdxFE|{4;O`Bbd*vXuVNK2=hA}- zwo*QOfpB(11AYbGAGgz8GfcW)Fig5%Fig5T3=`K&)7Xvx$U|%~g-W?)rH-BUGVmuu zWF4~WP8MgN3hKpy%cx`Eh7hQNf5+|P#L}$~6Wa^@M;|8LpZzfL8;42YJaCKz&I1QX zust{maxds-C+xYY19CawF~M32wkP2AoCMk~LDWrMKLux00?bP{M6QKp;|@E+VcQRC zIh>x2g6y3}$Ht>|?`JnUHUyxrJIUn!b_TYV88>l6Sim-8qR%U@)y~$oxEmTdJB!OJ z&(JD1gQptMmYVw+p$y+~>tPG=M>j2Zlml3^+|{SH5aWTY4H9=m$F?BPTEn#1zNL^g+NLf?3 z;`(3q-i5cqOLQUhDjMsZGD9aCYS?^~uM26;);l1pmnj3y9S>BPiqunh*A0*k+)4Pi zR11Yu*r*jBw{I`F0fuJ4IDIFI5x)u`L=zw)WI0ivoW3AX z!6FaPxE&#l7$GjK@_+&dX_EMf)fd3{0ZH`os49IMt zZF}7`ih6$N+g%7e`1ouD7i}~c0K0aK8v-=Wa`ym}9qiUU8)Y4w>Nfmn-Q%q7p6+hl z)7`Cmwx<$-@HTuEy%^>X_=+J!+RM7Y4j>~8XH*viB8a*s{Wt;xuCfWv# zVi1MwascDhTr+*^(}fcglE;zM2i|#X%Rw9iSps5p?BYmc?nW#8O_%3~Z!Zuk`fzgo zKra!ScQ}{~|Cb{m}eQIS=tH zH>71h&7DIG&(HEeso@Z1pqi|R8l1i(W?7CJ`-Y7(ZG(T&?v(;A&%oaegjLdRJ%_6B z80_UoWF94691^iZC<_{VQ?pwI`u1+^@H zp-}DoYA(N>&oMsu|OBo{nq(l|INVAo8FN`wg|T4y^dvd5AdIW=hhcMmXGJ3_b;*u8hHVdBB$!p zs1I+)YA2qvQHbxAgj*0#dPw#M+OCJNSJbzFNVXC3gq61#?}VGL3sSlz)9(vZ zEs909vUlKUmt_%b(KNIAUfQ+bYP5VLox3RrF6`X&`x@|0cBa3kGySbQ)8D!?w`cmo z=8t{Vj*#UV`9WaO=gJ=K!Z(ZKZ;DPs>U{)*+KxQW&#=+EA-dhhwi|~`yj$o@NQMNx z;1N61qaol}Kc&v})^w(~b!U28cjoqa%r+V%NoW~GDb8X@TF!vR?Ikeln6&D3!H%+@l?@ZWqmi?%mi9?y={0nvFW`BGX1H1`u%x6BL^i$XI%t4)On9~DUyO{O5z?W#8|Wge6`OOnv!RmD5YgtekQ zDyeEkrs#AG(^bwq@FtJMsZWU;fznBCB%IQR&rcSBhKq_cNLw3F-yX0>*V#u9>i{DP z|MRq6+^1ux{Fb&+G|{Vl5&DRJ5A&7CU(nMNL->S(0V*0I33Eu1>xFUu#ii}MHlAD{ABlnUBaj9+*j5HuF_wG^r?QnpP_`;O0G@%HAu%#4SK%@izBg#6ajaC+Zri)V9PXM)+lL?zb%Pd=x1rFRUj;^Du z$gZ4~*v|{hO{j~UecL)HF5?GWxdXr%LyUI*w@9KLj8@h}ba=qp{|rZ#n_I!3=Y5I> z?VE3(=2{#w@B1^8;W@u8^GVBs4|JIA%7p(lQYTjqv#kyXE1%pkdb7HWl~EjXrZ_a~ zbUoqn$LnYukAFmBE)Cq@T=B;R*1S3izQ0+}vm4m!B|R@?G-c^Fnx68;jt@CZ%Wl;6 zls7iIuUG6*A7qbec40R?vbDuzLBtadFZ|bn)*a3;ew;axjjTxrr~)qr9HH9>i-tqb zc49-650pYl2y=-ydW)P?a4v&4*WZarDA)wb>(;Gbq;+>ZRoRNFXK%{tqN& zLE<{fR=B}-l!^Y`cgN>Hd^mjb=EK?H(YL}xyCCFTvZB5^hHLFDh-crt0VM12?Chd_ zGX|{ddhp}?N@l zmUWTL4fi}`K)e7EIFRe5ZrtzLUTr`jMKkO@>VR4XhW>dEsEs+CS7R8W@B-gL>v0H) z$RcmEexY`Jlw5hseF&8ikTNU~u4z_eFl`Wl-&g1og~x`;_?iJhwPT(sj0#rs^TVSS zb-->!UGwb4OW41}?MzpM6lyP3zw>SWDtfcqCK<9S+b$NQCPx<4U}Dhx#wfUHm5@Y) zyrgV+JJl!x-T?c*O}jYD{ou5Z^+*Zy5090aY8UC9UZ?ePz-m&b>f-<=;{ z>}j?cPJFzZbL8utb2D;D?~p<J=d9aOce}3WL*MkJ25@E5rggp9o}3#zq0` zddAd3BX?HSwglR2PLWie`=zyzBF79*efkLw3O9nOyA{1w?f9qU?q&p-eHZLg4(=|? zKGvRk+Af&AKjVcrtr3I?DSO#3J3gi29VO=Ye8*&0@@#xXMNxPQfGPW!Ww$bz5a>oq zT$$vm%u$-eIAoaS(2Pqq0$M;3bFC+s98Zhc@r;8lGmPi)IAzsEwNCiPXCx(;I2mEk zpZNrfC(~t&#F}B38CO^J))0|qHQX= zy0@Ge<>X~HAjpAC7NbnI;Y^)Hnvz?-2i2uqrQ}3Xtd%pqq9Aw;ciU`Qo`g9NwXQFi z56PF|4xC8u(LAbCsq(#Zp3iX$$4+D@-c?ff=;C{$V@dJgT-y_k#(BgSKf%h`5$Yiv zsIzzdJ`oQ`7(o~p{2cTkH7HdG__r7D-Wsu5furA0a8YtKV@y!;isj@}QfuUzj?mmx z$z;;5eD<>(Ynkf=(J3D^cx_I~9`O$I17#ABP*TZZXzsv#{;_Y{> z)mVtuC%5&41_C4APnAu~7&tsPu=Etlx?E6{{|5vJvXQzJ;j$cS zcK)Prd_F63qgg(-zboog9vGYQAh#(GnsaQ{hUYJ2dofGHYwU@$>n2a!nmgCJ<<{1_ z*(bODbGLBFtuFv)uLJmLF+1Q30}|lq^&!Yl-@JR(xk*=f=uRW%t+x(4tyfx9-Bo7} zA-~D3eR8y|bkbR!a5JMBkye`%e!^zPvfi4k$NGWfLvKx{w@0-T*GIxwA6GKa!c)``;Bjagh9s-9;>hYj@zkyjBGA5D z!J`i%>tFdG3c|)g6!vx)L}71-K@@IJVFo&I$N?l)n51Cs{{Pu~v))LuEKTfL{1sl+ z5Hh06-D&%hGc`)a9+k0Gkdawg5I|fryGDA_>CW;c7|e%31A+ud0%m~eei0y{wamll zMS>nA=wY6w|4IJ=^-s`u&b_v{xtX~coe`PU#qLT5>27vyJ9h6`zVm%ZP{0(K<#MjLo=cQG;Bb9*x*>KG#ihfuoPNNLg-SG-|J9j9wRG3!#ypOctz=TdpCR{9v^Dd$Q(mO)7G5b#hF0 z#wW|0Un!TiI8_8Y#CA8Gi>!kYUoM%)D8IVW{_1;mpWyLv*5SeTGNMa-NS^ylV=kss z^#?n-Jb;RULD@A)Z*+D+H=o1x9c^u_fbVR>qAAts{5OclSY&>gb3X$yo83FbM1~sp<;IP~b5_vf z8v+hbRzJxF0+d8u_0gIlWtiZJo_c$~v!~Q;9apQqcXOcZyOtH*6Ss;PO*v3tLl5_b5xL z4gT=^HOW@0l_=ZkTwkrqzA>}Mk`yDv0(ssX;e`%C=p9G}gPTq+ikGZ(bNFicMCR1y zzEbwDiYB78I$o+rLaGd~%>QCQv*5-#vDs4YlU9N$+cn=FvE)=Y{mV?48JdskAAsE{ zYE^Q#-Y>vRGoR>cGS-!Y#dIfaYuC7GWLTgFPQK+82}0#%xgn=?3XE|@At z-@=%YhgfLNmeG2FB;fPs&j*i?+yV(77~L^VbW6y%?%q{Mk?Z%WK>P>Tuz_psKdzvnVgB>LfS4AGM4zKg^uXgU>lnm(nyc zxZ?aPIN|g6D`O8V@e>xV5f~F9@I*}LwqMBE_cp}n<&`F3ES>@_{t=Y{ zVBNFjlsj{fh@EGBMB~8=SSgjbh|g+HP)mHC!L z?nq3N%9IKGr)qvYdGcbO-zGfs+cbo-C)wBry7_GlyW++Zug@#0KXG5qhtf zIQMrLPjKETDfl8yC~26cTw~i%E6OXcYPy3!g*hQuw4mD3V(>>n>+-f@0&Vn7?&LRS z1+6ih{$0?$%}pB8f_kKSpiB9yt!R8Z6glNkq%k6I*r9uV5Hc))+2Qvv^aZ;2q30J9 zJPJZ=XhV(Alo~^>AhE%+U?swpsSlMlFrR@50376->}S3O^qx@_^fR5?v!CT*Vp(wu zXch?Qf>ad>#F$QE2^JNV-7r z@&G8Z=I$-#)Zw%zM~@j5A?I!~LX8}+q`+j#Y{ex5*Z%Qu{&wpuII`uyoUvaCVVe?4 zAtD_$r1bk#-C!J)FINx?1&|q(6YSCs2vb8r>ab*LMHir=1}qx?a+OO;GKuWgjgip2eeDrC@T-!RXI26zL_9$WG5~Yp$u3*T>6I3g zS)vV)*`BSm1?W*UF8gaDPJH@@m(L*evJwich8xbQXC1j}iC4j3*ZjB-l-_ENGGpLX zF8%S4A7z%H7bGT9z`|ftkP?-YN}_~SX_CyYkZtB`(0*{znv_CVVNw%k!s;)m1(R@( z)GVrmY}wHC0J-=|VRZ$*i_VPcpC%FE3bQmr4m8d7`IR2*k4TGu%|CSsM#JbBBdt!U z1h!jFsiii%5G1k}hflD4;6`hjP(W4D^El76b~xF+$dJ1#FRkCYh1yb&^(=q+H2vn_ zEemMqafSoHQ-{dA5ev5>(~6HdU1yXC4*Udja4pNe?aN{`5HaUx~QUr zarJ0V^$V{D%-;We%zQ#94V!<~e#2f~QX#K2Gtg~YHBA@#-O0smvWrEwiLkWCO4&0* z-`d#}n5qp14}SOV>2q`%yulBY4&Oc01H6|EIVFSK8l)%>>A@Do#(MW-ny@nTK6+cB zCc4yc-Yf3tWuCM0M%j*+3`8XGFHPTEm9fm%kM!4Nxt*_-%<6__5!qCZ9xfHxlu=LU z-7C*8^a`N(Qkq`4OO$JS3_w)0s09pgs8jE`a13qr9`)KwarO1yQ*F1+^O+cn-i&2? zmD-Df3L}Sm#(F$Tfm+YH*@RtLEExJ*UM$oF-}Xv09I)pFWqTFZbvq2DayN!N?_H8+ zS3}w}b9;LBGH;)Fr$*++>+RabTr)sz3v)G(Y3!twH9fma8q;XxljQ*#+x52n*Hi4! z(yUj%LwA5$6aqhFi-5#l`~+$Z7J-U_BnsmsO(G8|7|TZco(1_2aK#~u=_AJiH5eo| zojBQ~U$H>b5^gcd?$fK6`{Si>FhqZ$8wEI%1j3#kbR!ZpSVgAohfoGh{jTBQnxhX1kk}4u) z!q~ur_-}CkzTLlXmG>=6Zhp2m&K!r$Z=BQDopk!TlTLSc66$Z33<5KCQWvipm>mMh z=75#Y0Ak&A%_wyPP;OeWFTouG;u+Hd5rl7=R^(!>+kE4E-+{APZJM#n+mrd(#7$Fa znz_Xbx8vQ~`Qmf{q<6KYqV@kyY^krer&ITVWZ z1__O%AzJysZxnzaHUwXmWk5;NBAaco?-FVE zT_WusJ<9N#JQuCv5FH|w*-xI&$Y!k|(e| zU(MtSsFp%>rg@u+L}0ksL2^HPnu3Cnq&5l>Nz&5}7#h|vVq*;hbP zTxL6M_O-U3_Lyk!w`7Tn(KMsn{E8`>!0jAMuyUbLJ)$EMz0g*21ToW{A}(0@E_2nqy%&X+;PGxe0&E6(_RZ+bS~WbJm7* zL;`gS6jx1XKBj(*e1?FGXA;0;!{XRMZjl~Wqg1wG-}Z6Yw|!jQBPZKNvfYSN6U-0- zWYxYATM%MIuEtHH0J4T)tw11UMhTW+#|t3SD4hULFSHOrRcp;~RxQ)N+sCz)jlY}6 zWi>u7>$Z=}y6xlY5nM=9rY3PfLq{ieJ@hT%f0pME$r0Kypr3@7$rAyf*uapRfDD8o zgj|FH41q6#>f?e$N^p0NOFInSevhl&$(hW@T85Et;VGlIKSDH~Aeyv*DnDoEW)%ps z%LE@m3j`UGd@T^08*Ig&Z| zfo_rK_8OJX@jIMjX+YgIoH?7+CmSsofd+r{>w`k7=soy@d2;L%%Yod^C{dR?evkCW4Ld`A=q+^CnY+P(oyJM zlhj8huii2Ov_>U4T@0Kh6dhY+>FH!ml}#ggu#PNI))wOZf(Q9JM9pC#ClORyU`8Xxm29e&JAmOx1GDEYtk4AYiT3~*6-K_ z=X~=tV8d@nR%D{gK8)ynA)@16Z`bE!xM5_OG3fLh*mM)R6Pj=soqOZ_EXG-%0l&R@n){fixsQ4FK$e+=h(;CAPkazO2oOOy zjtD{{0+fZ6LE1kVn-XueOsm;4 zt$kK?YoAr!>anW3AOUq;BX&dZ65DPPfgIFD`?d^yz#1g5kRbwONI1owl~@23qZpXl zNXh_a?4bx!!5CX1q@L`%RdX^%wz02e{&QczTkX)=#}2K1?6CU+4*na{@?tcQVCi;% zih#MP*U#b)Szxm4r@Low9842g9anPzC(foBGDsC1Beh9GrN zU8_y>LpjF%jQC}jt3{5;EykD00utRGfJGAz(!|uzn<%7#d*lB{& zDbUi;UIf_>5<5{Me6)0er_N0NbR$_EQ9~&P6L;}Ogp^A zvfOHk=028a?qi8&k3dCGm_pse5&%Y^5nanP0189zmF1!T%@SI1kR7wZxr~`G_&4MQ*uNcntj3`*uV9X6n#iL`zj#Szo z&1H$M7eZ%g4uYi<#S@PI^5PdU#uN31MtM!Bo=V@zqCiL^@V;8b?>pkaeu4e8ieJ9d z4Kq|sr~pc27R*h7L2XS-PtuYT=0zcmHq?-)bZU*aH12m(;yz8*u@>>vx3RVo)wo%A zjV)~%BW)SFYNT%RNI#@5fUKS4^j6{8Jz`&3uHUN>2alefoV@+;x{3zV2L$cz$8YF^ zIhEIPFc`SbW2j#KE6hlLO48IhbEb>ckH9`n&yBN7ag(gq#`?$9{psc>Id%UOUt=cA z_Q9iv2nYT4`kiw*aegviT&&Ok;C{F=&z^nz{11=)`TP0n*WW+>asE3p2)%Ko=w_+!c+8GDb}zv+c&kc=(g_8bdnD9hgT>pk>4+J@IpJl);hVq8&piU8m-V-xMrgqKMKf@5R{ z0e%Df2WTH{y^8JP^6TuZm@Upqu*VY)_ISeG`7#qQHX~6L1%4daKpO&@78(Lgu3_X$ z;p0ElpHN*geDs{A z7ocg^53+M#gE96Rtis>JD*QdH!tV|}g@~1zMk4Z(g!pm5l|%nCBuG}h96^}9hy2)ewUX5Gz7S)&fpp4092tWKT;4Rs86ZyBK;sTnn)fS9yG`Q6; zB*6jyEc5u1vK}#f)GT74?cU4_4S1Fri2-4n%}Ydz3Moa~g1+wKL|zZR2ZaAh9Z!?P zO;dCWu-Z3dxxYVBB1bh;Tc48TPudyMC%xSWaMTU*^pe%$CE5V8`(Nv3W}7u?FzC~` zN=ERV#-DC}+R0P4eGwxvst#0TwR>(mm&$89pL|n3sajnBN17PzoxuJbSf)4g83ip} zpiJ2Tdm_rWU&0m?dHu%Sk>i*jQPgz7^8i*rslN^!QtbK2VIgIOguN3)eq^BS)(SwI zWTjCEXb8~AV6~0~)EEo{S^juqwgn2^grD4Rwl!L3+v0$&A)D#Zy%{4 zHz(T@tq)aFyM9GvtAp>>(=+Pn;=P#oRL@&lL1(-j$KQ6OTXr;Qye4p)pk3v)uaA^W z<772nE}$87_^*GQKu>Nt5t`pnKalENRhoRNaoaAALwaPUX&POEw5tX4SiT_2UiUO}6)qf>SBb)t#F^eJnPvN_Hi4YR0YhG03Nh6@BA;OeiLPU`R0 zZS#bEoVFh?AUTGb|6CoHvQmZTR`X?LI$`vZ(U$P@I5XQYolv{LV8mSmgJ*A#kHKn8 z4g*h&%jz`qggRwDYFcz~)>6YJsgz#fWnF1mG);}oV~LHiY8c%4u$%PlOul?AnSwc! z`C^V;(n(vQKXa*oRvC|kz>&`45Q`j2DB*=Zb!so3$GD0$h5-WY@BvJ z)iI5Cek;nSKi(kq#iljP;RpZtH~;J4m|94SljA!=-l`-i*V?1N6S1 z-x6amJ^Z>G@j_0oz(qWn5+||ZtwwXllI~mi9u^m<`ymFDy3Or_wIeh8pcC4@6xB0Q zwQ@2S^-x(lZNr?m+9pcP60cvLAgJMWa9n0c7pt%9B8GedILs%2t%Vjt)~~I{OxoVC zqE^1_%2Hf&LY;j7n*Dn66xWSVOH`b-Lfj!Hb4DL* zyoxQK)UU8jh{qT^5#Y0O4iaS#XjawBEmM{xq^5%slh zCWZk30Z=ZuTY#`vt!^Sx@na}&Awf-1Q-GXx;23Fu;wH3qSk&jX0O2;}o!@_W`u>O0 zqgSs^A-VajW}~c?-Fg2M+hYHhZ*ZXU_C=Yr;qm>9TJd!z-CXgotXrA1bt{v$`kA!r zf%MFDT{y*V>U+YBqz#_p5UES_2Ls`r`Y=?FV06?b6okl&hX~H0{R)B=UsOyQJN7Jf zcpa3ZjC+&*xlEc$qoT3dovf`HBh_Z3&ETjVdvzz~_EXg3>;y!lA`UV83LC{c0|cf* zdJ!m%A|H%_NHHaG6oPdEXep3Q1gRywB=)^cy+J#qVo-3`mTfrqmi^f+dpE{KpXCY` zc~t;f%AeK+v!zN*Z7^%K1hcDZ$V$G`aKja+@Z*YfnN}>-7ATW32a4s3IF8R{vWE8!t?e`VM@b8T7YV{J zq_0+-6?g@xTBE3q00COY*q%D;uZUrw1moee1QQp;vaZI1Cu=^B?4D=^tO3cc5jPWt zLY{(tELW<-{-c0KIwu;yIzTrb_j$_tP~{UZqTzq~`@j9~21mH=j}#G~=(11|43~C# zHP_-+`4dui6u;!xIb=K&pVd;GN3!PdynNqEW0ub2-~68xbDvRAGk85Gx#>mInS3`{ zuGv~LMI^l&CvWIl^_d}DMxF&WMpAN%Rrc6%J_yR=<<3+3+y8p-y*6-=CCp|`=cA38 zBmZ1IDSI`LMuJJL2(`0Nr9D1r%n&m==d(FD33i;xKmL#Z;s5)8|Lx#?#r_NHDw%fe z2_D5Hl(v;fP`dC$Looo0%WtX!BL2r&v%-Ao<)EuP$9*0BiJrlVB;GFPw1J)+)eAS=v-M&cTI{}o|K>kz3E$bIxyIsWA7$+&Je~bGr+V{pDy3o- zxl~e?>rMU+K5IEcLL!zrTM1=0$~WPI`ZGH~Z5h-hvd97Dt0b1p(uQx#Vv{&}ZPF-P zm)-)3m;GKE;|ZrM>JO#!8Rd63&rx+g)?qZAM;h1n_RY!rmybT2ynI8@zGB?lRf~>E zUJx`U;_~WWAOnaz-T6?@Kuu|`?LC@m;pO|VGY?jG-JKF)wKuF^Can3MzW~$bPAuh1 zarxv--;r#0F!kgQdcIf|X|hTVtj1QFb71z)>KXdT4YLdFp50hH(?c|3pvvy%DR0~Y zdfMK&b6ganV^|<|M*}f9w2}Z_NoFDe$0vq;ADAH#0C|s)Dex@Umc+?V^ulf`1GG9x zcQfKQ%@9eft-#1@3yk~@SuAT4qwgW7mV)IE;gCUXR>(d;&n$&>yqlP*xHU%q$G2P59@oW1)f@fGiq2d377*t4ur#BOr5Qae4f&%0a#yAihoJ-=0y0fb;)UQ?chDLv zKv<2ur~$%tJ4GuUXh0z7lO#@JIKqG^T~x6&%RqOhb+?x0mU|HHEv+FB)STJuV`)|& zOS5`enjPDQz%+;{4fKFc3Zf1hh6X1x@j>jL5>u!VrkKQn#1w{ssV#6okN|uyIB4Ru zf+fP2q3_+Tr8!mP=3l&}bW7i!RMHL?Ke(_Dhfrsg`DKubnuvP>A73KsEKK*b!crjmIO@KPunl(FIADrImJc<;_e+$Grvy5gWwXJ8) z_?qWP*3tQ_s2z0YE_4w;5-e(>8agqJ0-uF+G+I1ot5HfBlLYyp{Nm|chqUQAfq(RN zND(4qMQth=kv1KWM8GAp|5KK2_Z zad~h-r2Z>PO^ZcwgbfPU^XxDi!z^hWq$X5O4B79;dJ3m>_maMd2&6Vqe6V7P1%gR( z=UhW5{z|BaK%MiFPnsEAV{JCYu?gUmP^OI*Kw9Ed{GZ*!uSzK@y1{fy@hrpuOD1xI z**IB`1ouO5&o8EQJ}jjfbGlwy9F^fO)%DG3Sb==mjM{Y%GS*+dxh0fe{sPaSy}*n1 zxkL&R6&9StueO0tSVY;0u3bNQ#@Cnmj&9`eI9AuEKFd3Bhiy5c%9^`1XlpV_zjBlL zVK;hNwf&)fMGwiCNAq+g=wNNIuF|Yh1#=-c zybvlAkC*V9%}3CL>Z&}b_M8^Ae^|S6T6YHM(nqULMl{n_?3(RQTX=kS@7vjRF(L_# zZje%ViRmIg5rM?VOwCvVFd(2|fWAK)T9t-pfIKPyT?Bjv;UnUVLBNMjl}*wL$3@ZJ zcB;NhTkuG<@h!n)zD$>V7jOQhRzR&rh<)-9W{X6%wTF>edl*@FG>Qq+NxakozQjO&0S%+* z7!Hu}3PVFCW(-1RlAi?T3P=?jEvIN`LMwS-qv0q@+zM9|y3LJknfN`8Y{OOj%P_KD zp84f@v>Y>1cZF)4G61+%r(DB7MceRAt)ISow{5Q-BsFG>*RH;9_u8LkM^vutGx%dq z8Eo)~k65Klc_XipbC~hBYLDaf6}cPiIPGxNcVA*n0;J7xZ#{WJ7mznjNO?`>GOKmY zO0IegcaNQGR5~VPJENt(JaUd_>%~%INs@U?btP;!ooMVxeqq!?z&D95P@^>H8!DzJ zSs@ru7%Ftbo#b--6zgW8_m!WTKCk8A0v;qP^JgSTa50%qHQ0juVo8ErnnR}M4B>50 z=hugqlO&nS(PYN=5r6{aHN&^&JP}UlhW*~`PS6RBmf|CwEnf$XuC8mT@Hx?B!Qv1L zN*WC;2TnDPPMQ@fyhUO!k__v_KrrqT$)a zsOVp)z77Hyk%i)f`2n)qm}IgUcxHTJC^lZQvRXW)q*0nZM?7JDYU)*BP|6w)u?S-z z=rLXCTRV>^E)fO(f}l~$DD0sFUy2## zI2yV*%hGvaOk@W)FNZ}+D3iEY9*U`?Sflg~<;^V^pvyZ!C-yQe$DNz64DCRY8~Rn# zr-g?)q~MY6#S{>zSX_~P&^2sAD$c-3z>uo*8x_VkoYOVTlPdK*Dyp37eI zb6DI#P`5eY=LqO9i>xD#yGJ_Zu{@;7ox&jccO*fT(mRUlH0oZVTh}A}qT{u@$qx6< z5BFZnPt9%~bC;1i`_B+?w}KE{J+_sK&;v`08F+>TnO9Vp!QBJ!c4+u+VuQ-Z1b082 zXS4!?5iJZJ81n$(94T+bcNx92FkFZV+J1g6aCtE5g$gr^O)V}WAKgv!gSoa<%f5u5k5UVet{z2;}0jiE%0dL zjP6Cx_=|BQ;F(m9(PMM=;nnu@*$v#Oc!^)e=WtcZn>~8}_UOsuBNQti9X3`m|2p$Emnv&6XJ<$eFSA3OW7`}QtbAAX&GGf*imR=to8fBC zoB1UgOQ?^sXIi`LSHJBl1ok7au@ear)x+G7N6L^ptlv#Lw@7;xCU}C9^;QFGHsCjB z^Eoi~TTQI{7fbg@U-n!xw`Ye_a~DfaY17WD9=OJAB#L4;2_h4+Gyu4y9#lC&Xn{r{ z543bv1bw8$m%fWGAwxoMA%Ku4Xuzx_4J*vfu1Bc9UKBabE|&X^IglxGz$tBjRvN1v zB*r`tsX-MU%szl`0Zka1U8}14!|#Dzsnl6}<%@>f_y4w#7m9dCC5Em`@u|2gzx}lp z{CYqS^0&Xv*Tu@(Ts^m6AMQ73{T0)woikjBsDNKsiDaF~d_Cve#iF2cywsgBI4YR&zyu}IJeNR4v zgM)%gHv3}Z9w5O{YRoy}!esN9CBBN2<^xX@1q6Cm2(oj=gj-x#r5d(8Kvi>^TvL?3 zxqrrx!OdY5eWT<|c?Bi94l5fTs7aK5Me<;NrG7({Kcclj^Sha_aQca%bDY&c`Boa3&|db3c>lPDt!gT;_SPuDF1+7vB?=)&xp-y zGRqU34>wxh{_fvr>1tWh3@%y1gQt928;?PLV$)0L!9PJp60RG5Sp(rmcM*Mn>JyNq z=_ZvvQs<020(;=J6SI-=(mG^`ul(XKMl~a#2Ht{nyAz%>+i}e zgGrn%#he9i5_vOr^7LDD$j>Yl=L>3aqf!6e|1toP^-|rZBGsl|UrZyom2^aAsEKG+ zT3&Mkk3^R-8#T;f*{+~1zbta%c=;}`rYxaM&=7HOEpD{fE=wVEA8gTAnw>;8Xtv;t z`Mm$P!K3vVH7#;Zjf|Ab23m!fgPZI58dN-V*4KzhZ~&+P!_W`tf|Pwkgh#oibHG3h zj#Ni2Wir8b$Zrr|E<40|=PVMJHLWmgf6w$SuKcAOYQBJf``-*Q`k%>iBUwAx*oPC- zh`$i0P<|6Z7R6}vG(a@8%2%-%Nkx@sR_g}tn{Kd759s^H=VDF`HJ4Xw$|pab;%xZ@ zN}XHTl&jB4NsCW!4#Y=HZTwo|1fNeX^_uddoqb-=pL$KH(O!dej}JXWhky=5X>1{>h%9$3 z4Fc8zIRG6Wg0TwIbX-oDN>6}s$h9Dt2J%B0IO!&bDloT1SagT+VeZzK;zQTK!NJQI zJ2pu4Ep(%SmJ!#CW(^^rq}msCTi4{!kUTwp{Nm}853eX>I(mEp!i%H#uZQ2geDt2i zE2*+{epbKe<_Xk^*W`t#^R?!qD_=v=P0_wxrM<#sEK<-yd;yrXxhsw%uY4Oq{6wEf z{`F>*Ms5x?Ft}pAu{LmJmHF}H@?tf=LQakgre&IoY(Y~Og?NU%)(6Wqx0%yoIcEVF zHR40)!O2{|By7Dtk zbirCTw9q3P4<0-%kHrz2MkvUch^8CW{ye}KKRi&5kfz*(>r-WCVQHXlC?OMqS@5Bw7B9B|o)=;M z#(^`whr+STCXaC*OsK)>I2x$1S`ktL?M{q!SjewA{t|<8qyT=aM2s@Wfp)5!R)`^h zH>8lP%f=MH3B@~5V=Ckzv$v}qm-s8&F{ek5NSlFcTG)-i$Zz*0m@F62&A(H<-ff%S(>p zXjUcoet8-kA3OuA&p`D#EelqKyj`ovcO5eP9x0&07M3pqV6Qy?B*(4g?ai~}C z5&j)b=F4U@#MThRR@>~iw^(f}exdbbwUv$Vm?fQR38PSP&DEdPk(xYm%N++}f4BaC z*+JQ<7^~imHrwQ#+FgxP?A+3swtMI9LN6dH6vFet^af8^7?23I;{@CWN$fdsDjXT3 zJs3J+K)D)11YYDP3A|(UpF5xv-;^l~T-$QIt!}k;zuE23n7#=nh|v(|dJ_H0F=#_^ zwXO%S1Nw(WWO(&{bFVtd?pyq`$g}RWw_qQ83-+W8*;sUs=@TPc*#9M_T%@_`0An6ZTy zM^NAJOg94h0O-BIPJ;-{8&ImYVSC0VHZMT)lyBcpZyk-TpQU+?ZF;8Z?qg~0K9=To z@1l}u*++X6bflrmXh54WkgzX^^?@umG%N!|L(=hL35oLnO?$R5Womgcf;>|Q_)OJt zGdaadTTN&D_V*%zZ4aJCIp`JAu3~GXYN@6h;D5dRQ?L zWkBozKAJ6pG(j`Gk7k}Y!2IU~2qfYF40*_sM9~*;+^qYITQ_?%_OUl(AA9Q|Yh$>C zW{AMs;iu5^4ijiKxq>G;-;5K}c8JYGLaT{@^_~nO83-FVdaS0xb-#e)X5DApI$E5u zPffwtr>0fBr0=W*QHjBTHSg0W9Q!RTI4h^R&tg0;}5ZW|eg5h@1;X1FMZno(d{ z$jJH7$`;V2cEDj2CI&TOm@z6_C_`9Vj+<*5{=Kp7<+$zcGfn67Xa;6Yl<%QqpN^F{ zbhTBM2Pt? zYm6kg5_e}Rq|S(?eo8mvKnbpLMSmbi5su1OB^-4)c>3oiAEq z1rQ=6i~@pPbc6y-4l-X-4F{wN9U~*gq`h7*Co#bNzEY;pV2qbMo*%!&a)pHv5P+T{Nw4D{iEyL)0fi3jQgm4#?q z8RaTQ)L;XvpDzd+cU=hD_}NfRJj6nGasSvx!{h`=jspY|uwO0wR!fl0L0;J9*{a(nokdlEH)VPm`;M z8<$hw?)}NDqMvE-;N)CPF!f2iBG;G*ad8uXDkW$h=;6J*a*Hh7>Cq7L?kD}1H~7NnTPe=srhoD-XFCRlxEm`if1%)X>!HYzGM_B-d2)z z();89QEHPS@(o(H)I7u>FbuT`*aLws{GeGuowE=Jx6?Ly8WneO*bdz*Z`#UNl->WG zEjHRmI@Grz8@fY(P3@jtIEOY)@ERmtqsejvimAg|&7zMmOZp*a7h59U*+|N=b_;no zJhvvCUkG|^-`#=o^9|414f9h&jZ*d^8bT#CQX@Y?y*GPGd$mv{wCvSAtsVL@5}*kJ zm1_!Q3&cJW3-u9*z4{I+#Aul@d=&ElvT_lKNDET22Ji_9I%1Ig-sGYWN&nd2Rs-2j z`c?VpWiombL0T-G7!^eU9tRJYF=VNN)HIN!_me%OUuz^nUO#>E^62#4tD_$tLBX}? zrFnhy2BFk;Y*pK1Qx}^xblz+?*|e}V)SJLBNfL+N4$t28c9_O(-VW2a&D+r4pJZPP!b>j58O0@e;6;LdQxJtonpP@|fa-wL z>W(G9?P}5I{_XfPdpq16Ylj#5LI1ad_Uiw3&|ck@qyWhU=tDrWEOMoABb20qFb&-h z?9GWE1OQD66tFB4vfN=5q2QSSWn?5iN(^3_R^ARIfQ`_<+qc82^32`89lzYSV|P|i zyQtc?n~`g`URC}qa)e#Bu4D{t>cX|$ri`IYBu-|t3e5Xr*v*V#{sn~{qbxt7qA=vg z7L~^pCc?4oL=^+>#+hoa2`#gQd{?HYt@_p$r7KpSAO}cEz&wk%EMm^zzkl<5 zpct5u4awl3K+OutzIa3Kq=TLILsSeFG8nWyG{Ri2FV& zhF&n!DZY&SvWHsr3)#I`aXYB=-E*O6MFtn7o=jxeGdg~*L!&wZ=Nwg*?ja)H3CHSJ zXhvLR;`6!uHuFwEMhi55#M7e(9p8cnuYn@Bk*1-2>#nQ~n=j8{^|VlSYUN9VmuvEcq_AP&j6<72j@M@$>n)wBp-f zdw^^&0MK>>H$=k4kq)Tsr6+M2ru%a!GYq>vn%l0%JG4P=!{OM;lFWTgv4@T+a8ibF zZD?*7(r{&Lc@edYNzb(`3w2V*vIUTz7P+EeV?ckRCG5xy)7XavR1|YT=n?wX-J0Tt zCvk6z9sE1_3F{NMLWlqYBv#oe+!Q`t(-sx(4dRz>_T!4Ln<~Fssvn9-UO8-%QsY~a zd~Ihpc4{?Ev9qcSe>-1ICrnhOyf9)USZ2xmiw#CNCJ*E+K7D;IKFR~m?8C>q7DN~} z=S=_jfNAWQoi|VV>r^?P{@?-mCdArDr7ID~aO_rG*20hjrCzX7Ufv?BN06&EXJrs* zTTpRTRri$Afj$g*K&HY1 z{;l0t;%t*_f73Uyce4CCcjm4%>!mv_D@2p^pE@n8Li0|YR{|iftyqBfmq>d9V8`MR zs?}lai#VZFod@kPns6Z%1?<1!7zp=cBL3i_zJaN`R95Py%A<*k4Fbl^#Gc2W|P7%e@4UD5l!V7uz#zII@Bk zuV+4aamRv=z;sn`2|sndBPd z_~3+!mITgVV#S5bh~1fI(Su-WO-2%8fEB3E|)?ZeYjG} zDHxpQ!*^NX{C5*pHl@$082t`K{e*E4JUJe}BQ{iZW?7Y-*w$C2bIh4VZ*;kolyj={ z>8srn>SKKmD0ClYcV>u5g2g|N5PjeL)JV5p-nq4AsX#i-YSq;Mk+(n0AMQwVlXQkO(5l&Oh~~2Jd9+eGf|_GGXEw`)R%2#%`0(p1sN&mW&>vjNf z_(!B++g|D=NJ4`x0vr^OtVU@Qv4V}>Ymn)IA%0`(2R8}e#=U0#okD?X5-fARJ1$he z5tO)goC0Al(->u;7%jOn3Vg$B5jyD}+Ws=O&x@xlL9vFql+>CNI6A2ua^o7dY6tJ*(eDmj_ZM|CB$O{ zzea5OD3YQ&WCh-4zlcSYK=$1|IWuIP1^2T*x!sP{g%CgfsAZm(>tuehE_|!)@3smN z;9)SVX2~2X{07bLl^%1`zfzy(HG!jiQQfF55S82&G0D*{6Z(aR9I3sTXpTQsp=E=VI16&eWH3}X$mR;p9IX_NRB6L9;OK6{ENrG>98F z28}xnGg)R{D4bTlPm47HZ9h)t(~>MB``40m7-^4e5Owvag^<3yzH_x_a-5Y$bgqH1 zqPU<(QeO$u5erJa5a&N(B#GD_FfVbT#Q@-&QQ-zs0`S%8Gzp@+)`|+WHR)T{9~wh1 z3mQj44};iHazbQ1+Z>e2PBQ{3a@vJ5Eww|wrhRi^QGO6*8KlA{31A9lbV4PvK@zng z!ML?B6#ho+G2@C+R>xBP14^MvWs2mb1}9&lMpt`2CpgT4@u^${%vm1^caL{rG8<$F z?0U1-kYhDP4IU_jG7-=(6!?-rnzOTq7}lEdp?Z7!CxPCrfBI=k zCA!NdTJpLry4JYV+77W7t8|-s%8uv|8lqt+vY5 z_6m1uwH2yrx9>rA6WSo}lAy`9p(KdzX*-DhFm}O?&_7e8V8UmxiAZK-UkK{umb&p5oqBe z00JfPd7+)vBLHBW+A;w!FtCK1ritN2QKe%Q&|1)bIlb&_SFvf+TfC)x-L0+kAz;6v zq{i$ZQ`XI)%?jdT zeHJ90j(^5gkNU4O%42kVy?gc1{>^ysfgH}I#1A#ItBy2E==}=~8ktVV)NVCd>9ZMN zD(0=emr?dnCf7)%9@3W5RNsnu8V*KAmpfMdCQ^T+#3-{b$>q!XkS=MX>ca zMRl8S-xsa_=+{B6kcvUny+$J(lUsJF_R*bsO9@9qX<}xhF2|~$h2cpj-|?yGV1Rh1 zp;IzMeQBY&yQe~>aEYqB5RDpqcsVY5la^dznyHi}$#zq2^+s*~;bp0-hnjz|o9+H4 z7ycnr<#Sgu1?78DxfYmlNadQd?)`va$@n~=1S36zdC+09DH~;dp8`sIXe04cdzlwfBL^q z-i=k#7z680u?G@KVo7y5)t4&e;`y4Ch{WvSxagBwlDqfmMrqzCrz8V%v0fSMEo+&C zh3ji_e$-El-OTYVJ7Kc4c!R^X^>x?M-R7ilxH&0wsN%Kk-?O0CHc~X5M~5#^mOp&+ z^!wxSWm4zS{h)V+Mh%Tr%^7cwzq=!*+o}Ck)&f;zl%vTi272vZnHFBHR~IjW*&m%J z7thYt-#wE*ES|sk)9)w#)2C-27O(&1VYY3>{d78@tDrUwm(1DigE}qXseF(|9)gxt z<5ch6LPXGE@9qqFu9Qe@20;=yATu_Rw+7P%_@g|q_V~zAh0?V^LEwkToPlr~*-#Hl zIQT(9)aV)&CjzZD$eg*oAS*k))S${l0E~C)f`JG)?4m@UsG8G)#K|ReYNW_tzQKKZ^{UV!-(jGiNxapzTXa_29vG>u-kywP_bg!VH4E5# z%>vz73+*(;YYVb+NKPSt4pJ}l(n;47Xh{IQd6f8;K=wR_22}|DrO1jAz$ za+8-8(Qbg2jqrXJxSs|7Y-fQxoC2)9rT}ZNDWH3{%7yYA2(4VS6`E$`rZy!Bz+>tF zsY>m&AfG3`A2b{~EMgvk4akNPoQDFMkcn5B0zAtIeEWV1xSs<4Jg0#Eyo{Y#5l^W9KU0=OcJPk_yT0UN$iA4B}ayp)e6n~xA6Wg z{Ico*yWYjUVpMal7`2BSg)0-ww=5@012={k9dvcP5d1z?8i5M{sHzl^s^^%Ik2Yq! zvPs~h0|S`v1dtfN8lyTckowlWYkL1Kere?dyM@V~-U-;p9`7Ue-s645-aYv2AvpwH z7zmM~LL5pgk+F$4lH?-M((Bs7S(dKG&Fi--ZykrqSg9BUuw<9r`lhh7i>&S8*$3f~p^8uWFe z`_KpisgdJnC=-ox(UB19gc>H@ek z`U@eN2K`Y;yrhyO(IBIYoCjjC7cL?Fi3qr@?*oJ<_Vx1;I#Td4DBx$ zBrFh;i_j?01P$NOqC%Tg+Ph8S7ieuzF`0g# zsX;{zx!Iob4t*Hd5FI5rI?A;G<1;y{zCs1~X9Ak5tF0Cl9ucIaXsMloTvcA8`FDb5 z+!Zx@YPpa^l5){NEB+|PFm55%yJgZw;{mCFP>U*)r^KtJ+W(49FjjpLr0D^U)~~sp z7QpkbRxJI2&h144)tbeM4$CS!`KSj6|Md5N``-_;o^bRe5Sfn>Xb= zc=q=AxGL215j0c8yqet|)&8KatSl%~zPwBb3jIA|fxZPM-+3azK0B4vr>!)!$WlZ^ zglg0gUA~jUe7|Aw5YEx`j4lU~l3_}?V?$VmE;Y}{wTuYtsU}? zp(oriM7>e?ROvf$w-7VaEw4!M1szT30-_@q4^lsHmOmSSj2MSp-md6{ zx;Lk^V_!Eg-?2NaEsX>;D?!cbMP`ylrsG3MhIBIt;2sBPxOUL04VhqI;2luIq^J|% zR=@=efsBneJU|m+8#w#=>AQ9)8C2MPB#GC+3AKC>!h|?+G~hs_z<^8u#BrqA)dPHd z^!n-hqdF%q_v30={yXydw?3sWpUK-{q}FYX)Vi&ac262ui4&l=DwTNdF53N}bd7*T zQYAU@P;n-`b#!-2B+dn(m;#27IK(Fg$R^kYl8TYq#3$z88|jxY(*Cja4m&k(Yp3RI z?X-J19R)UW#VM$)QS1SIMQoxU&=Vvu1nPAM^QIXS(>f7IrtohDHPrFY=pWl~-CA#`o{cWJ3A@pEsfcd_iZ8tHA9ee1T% zeh;P~5j&{I0jUX500Tl52?$b>Ac}$KOj1xWgONXs30570r9T8ws0I5q<2Xbb)VC|` zmzYcehB3l>BmJc=`>l3*+hyOn?XqumU-mr#0&_oh?7)ws5ISm30*Y`@TS4pssuxZS zCUaP5l7M5=b0N6`u3L#TdIC)|FEZS!duoDO;NDJu!OOneVyVV$mwn^5%f8Vg+J{S; z#*q_85K-|E%Yz6uO+mc|LN>_%q^KVGM9d1ldwl5lk%xkg5n>%fDMo z4XP5d_m+CQWxv%(Z@cUpw_Wy)?#n*ls)@i4Nbs0eU`g~*8PrMT5`Kp`-4ZWOT*E*E zC1iw5P>2C25Zf^X8UWs@v|MA^H+sKw{fo6xw#^n6PP3aGOwIHAFRrvb;J?)q6YrnU?+b z8gnAYk(^(zr)hp-%uy#7s=(a*{dRSEqiwrVUADOR9JE!JdEEJ;T4JtzjcVem_#?`w z<2JUEb@bxQJH-?f||Z5ci4M2KThPK`aWl;&&Owj z63U?PG;um>GD|KzpKsjw@z$9w2h)tF6NPn{MdSHgUtf>UW@}co(fce9#o5)A3dUnO z8=tQ(r^P8AG3{q@YPk0ACl`~i4_>NU)OZ}4Kv42N;{!dC^T+u6u<{ljUSp`@STu0y zCfsOtb1h2?D*FY|XR@-dP<2u=-7}I}QgrG#IYn0r3;X)u)#e@9|7@}P9YXpJMtDes z=ZCK_!M{0vT3o=>52(9C0Au5J5yC?OJhYJsHCya;F2ylRuUP4@RX{*?X06h6!v?M0 zVaeL{cTXlP@gx>E{q*d5TSqwMcoERw0WUFbsJpJwq3gCMtuZG2C4EU2!YUC z1O__CjuWW!B;cNqp$k$9tc&Q6k7Hp%X_-&tSUf)UB57%~qCcv(@*^ z{g#sNDFj$D;2@*0kk}y#50p2H9O)xDW+t(FC!#!fgni|BTAqi~{}J{O=K*{j-P7cQ z7RHubhg zXzhav6UB?0TrehzXn|wj;Uk2=HlAYm*&pFbMHzT7X9Z?Y45S~0he>3HDLS2To*LpZ}$ia|btf8=Vu~DJ8MEh0bmWt4& z=|Dqu5Q0nMIPiTpakr6V;mK>UiU}Y}ZlGMSzcPj>FTy6dBrNU%yzPMhvJ_5s1sW1d z-0xaQ_*g@8l}|sNs_?Ib6&@dOw6_=>(iM=mOJR*tdL^$jKj-{&Jk~lF7 zKn*qhIxdXq%n^19!Un78ORR>u$GlVnMnvw;Ed4V%)tWwv^ZiNzt_e%b2U!k&T$5-K z!=1lcNELaoATkEm*IKzH#f8tW)YT&3!;krOuK!H9=#rcJRSg*O1ibt2CWw>|`R7*y z_5S|$t5Xo@&n~{g^PK+ntJxgv{TTTxIx=rqiW628c0;O^q@5KdJg#a$3aE1>9;7WN zHzvpkkj4}}t{doU!It>JFH4VMnGd9H4~Dp=z}F6p&CFqLhTSgJ z%eOpyG`m^Mn^Z`Fi*j~&&c1PXYSnzSdi@xqML?#yN}T#ei+)Va*4@P}LC_iqs7_mg z7;WOv4s75Gp-gS0uI+nR=K(&%T8YMCNEM~zk3j}gN=rC#=xnk|0jubP^tG3KjMuuP z=L=qGNe7L9r2K6{w3J2=V2OtY4!8u+uLTUR=$h2M0}992PoKOzI(_%*=!Zu~$m8B3 z8N4ZnRDF{B3*6M1WK+=Rg?&uY3;UR)*IhXmU_ui(MAT)WfC)rQijG;=k-%*GFwfKu zBIsf{F&Icg0l`%mqYG~nAQ>QJQb_225tH=qP4X^HvMGr5f?g&W^fJkyN8kfyTPFno z0Qm>-eEAU1vLXWr!3fH&P};CVhy32JV(`t`Al-{ zWu&W5ykXusSdHEEKuQ&t+8dQSHmn(-J$@k+%?ym?HGztnq*R#M2M&RF1!N@$4Q1|McP1~%p+CKHk%jY#G+*H+Ek z)^wzLkSVdOjc|EL#Z46{^>LUDfb*EADgBCJ5Lw9l2=v5b3asgg zX%W?9+?q>lh-%UFW~ob`%D_0OLGc-O#b)tEib5gc$T$;;)g?exWyKgu+8Ci3^?2eE zR_o*@Gdxzk!ws6^M$Q*1!$Gz(p)ieGP^I2M-+Byy&}K2yqsDV62Tv#*$#2~Jqb^yg zE%YH$N_ZsLhO;0@D&)5@S|9|D1;wiBPEJWS`p0?pa%?^uwR`qvoqWeB#A)Eq)SwuokG2Ius(Dn4y(G}pQ=4t09Xxs^iXF*(Bt}e|3ef{ zK%s|N97O5~`cokGYM_Eb+$A<@CXje_ptB zi7EM?hAHh3?x%>raufi56hbc!nDG#tooK7s$N#)D%vJTl{49{b-PUOJwnnSBHTIDR z3W&oh_5$gM&J}d^(_)b0gDF{R*X32th&5 z-xdV8H%9Acg8T0_N3*v%n!U}jhfqxl+Ct#^jnGX|KSOaDy;F#JO~*`vG_gsjBCyf; z2fzSe|0n_?c@&}16moW+T_NN)(QoY$r|`W!ehz!=3F?O_I*mltq%zubo#s7ynIiAe zy>H(`T^Ty;Xu=QCwih!gd?3;UGbBLK9;D3;6G};GXj$lxMpK`cTF4HeE!77#=VmV5 z1VJD&Hrs4}`g&$c4TK1sh7nz}1nHWtAaGTJu@2&9{v{3J`N3*v*n!W9@`$BEm z5Q;?#141>>koH1IM1?Mr)aVX@2!a)~FVt@Dh1%`Y7YdqPC?+LNgd!rk>_Myv#9By*LJMrY&{2|(?M4pz zLS4^E5YTuw*m&^)Dk*7TRTpX_w0+;ab5k_$P4U(Xb*nX6y{*yeZH?U*YEal3(gi^~ z+UOD&oyh{MB#AIE;`X>t%(q0TSGZ;!D(3iwyjnZ|5Q(dTS-!Ogu-Wq>V z3w5hKn!WAO>}`)dSdwKDqFC&@0gB#XMD}Ki&~_bEyuqCliU?>7NWUXgP5=oqjWk6) z9Q-6v=zYYuU$eE`SHPr>qO(th+O11waahkKqGVknc+i1JJ zGi)h#qeU10GwX}p+e24y%row+A|&kKVn!%blzvg=0$}%yipQ!dnsS*5B|`NjfoliY zLH%`wyD}bB%j4fu?QNBHZf$g>5ohk4#=;P^GqtJJA5MKihZ}P>mcXhgObob&f&wNN zv{8AaEp$b33DEM2>b0~D`ZdP~KV*{Xunl8Tq823|8KvD5=* z)VVwqYJlSdK&F{_7sYLY%2MecrR0@DH>0=)isLC2IOp?ac3HZRoSnM5XLLTeQ2n%U zsUx~e)F_EcbkTYD`0?P;52NFw!IP&0-I&(`ot;f@>T7fbTDGA^SF=GYZS6)6mo~B| z*l!jAeSBDFb)CA(I*hbkw%aUuSz|&^wj)u!J42$Xqtzo(z2-KPws-&3_nMnccI?$VTsy6r>mp_~e`rO>nllu_YTf<}ZYFwmZ| zLR7b*t^p>-6c5*k0ZGR*245&N#}T6=0)m#KlYZuhY*F)=8U8#Ex!qAq7vtIdOr+ux zj{r0))a#TnZ`6NRam#p?GTYrU`nt|7OR=_RgH|awitiXV^je%j4}e^V=Yr4}+#kN_ zYaKB(Z%Y}0PJIx!4+X#->jE%3iFddH8Tv{&Q{<>cGN3N_${PoE{m8KmQ>T_)ZX8xJDBaac3yF<28FtXgrHAs0j*c1vp1w4grz^mqzJi zXx}3iGaxTzL3^!=vnCMLAXzUI$palm%8g6W(hKBJE(s`iC1#Sx1lDkgZPPViU)e_N zMABiFxzkGYB=GnM&IQ}48F}2_B_ll4;DwxCHFhi2ZPDE+TVC9t(}~g>hjhZD+=M$h zVhwxuZtrgzrwuyw9&ubrIAE(qe}{KxcIwb?nChES2KDp7X;Z$SY4h%l`DoAZB7p@O zjJTkHMq&VDPo6*#Jw=uu+>Rm1?n0~{@`8y)RMCMK2op{m3ih7ZnAM?bgwBtkSE|6? z?VnYU@e-hYkDvmO9CfT1q?L|u2T1dZ(8bxsdRFh#?~CUc%ju8n#pdkp9&p?$$JM4>tF4{+;50)71;(X>ggPo6VI6yH!tiilW$Gt2^0kx*-^ zOo$oi=u))-r;MCkYGs*2YW$>mU^RHKl<29Y&RC%~#egxM<^|95G<%Nt&xZ#GkJqg5 zMALnapvJVUd5d^^#ay&CrM`Z7@)>5*Y}P72m%R7$u=M;7cOp|?^ANwIAr3bTrvp5- zVc+ffR?muR`vLDz#h3C$sOzZ_Sjm-9UOY*@P3-mjQ}XdRxO(*Mx8Htxdhz4@cYpe@ zel&Ud#cG~}-Ing8h8O76bX%H~ns@3vBby-Eh@!+4zMYbwx{p;Z zg5T|ijx;3vSYpOcp9Rt&vq8efg11+iO^h0UR+?tWM7s|#*WZdQv8Mk>v-J5Q02+)8j z(e*6EIs&Ee6l^8}QBLd;mMu&ZP$`-z8pslqBoTL{iDw}YacsoAmQg8%6M-_NDZ2A$ zSgniZ)-rUfN7P}_OeTNA413LYF+t}@X}PR^wM~<;k#^al5i|{^f~;RCpJ2ej2pm^J zx!M658ZL=d3`O z27e8yPils>j`~)Zsf+rOvQCTh`D#8cFH~6+ppMQA8N5HOa_A3Hv>K!Zhi4FT{|nOE zyc4rV;AmvXEN7MrJ>F+ax;c>bV{Pr zd6FVt3Smjbm;ss)-G;@jLb&NOZYTRXJMH|GXMD=&p82TGpBriD5{I~_bRI;M!5R20 z{8Ng40@3LzYi7!ZBZE=hWSHomnvrcALV-33cI}DCeocIjr5Jt7hLHknfoN zZX(!e8$IDEwS&92y<4NF$D0m*M}?XCh}ari)aSj!AM50hS&a?jJV5h;UtL$=Shc!i zb=!Ad=0o30(cgeDIE{@EZ4jm@eJIf3cSBfUIZM3241tD=6G(j_m2IUKnzmys`lcU? z&DsT8&t0RZPJ+EF<7re0lL2>@fs=x}3vy#NKK8;09LgcI93V(;w3N}QwObd?QSj#S zc&0i0`0dgAzL#d$Hl_VIiMaso0ov(ea`p2p_%G`8;adO&4(z5(Jq zOEUYKu}CqvrspzJGc0bnD~p&Wh3O7k7D*W|t?~ zy$}gOM9ToNFV^$KplY6n(jHnZ!6Pk+9RUy02a+tr?K7pBpgY(yQX>dm=zaoC?^mCs z0eAZDp5)e&`Ex$W-Kp6h<1z~VAmTA;5!BaI^RcL&b00*%LD|d)K)0yhxu>3u* z417lF_>_{RG}vrSDKm{9rsFg8SUWz;!FR-C^hhjq4wq8LIlb;J*XhS4&#hreuHB?- zzO1nC!-{=($eK0n+>^TZ2oRl{ShIyEJHoggWMKs`b4vc>Dwh=?e%P9%w^ zA`0L9J_AOTx^yW?pgvcp$h{?lh%bU+408hyX(>*JL{xj=!5XqK%x|>U4y9g;1RfSng)=5^bCp5Qv~e7C$Y9;TI*PjVFc$T<@1Lk`8*;_$9b~J? zQ~B@Rw|?Efxo&p2l=a==CzV(CcMyVRudh2z3E5;1HBW5aR*rM*)~?si_T2 z%OQ*}LNMasZ!d|G&;#|YXA7_-1Oa#vDwXG-V`Q zlG7vaIFE555-+M(pk!k5GbT6@U{D%>eyNL&=xUfy8B*VSMfH=Zqf%=+_da}rUyB$< z=CwQ=h5haWb$no#r~#fJLfRga0>qx_nrK;){DdYNTAzhX)W6A{6Sx{QQoI19AJ`-U z>>zN#$ha>O@zY~!sZ`+pY_Nml|DU~g+l?ei)5NZqPZ2f3sxx;Zf_^^aXw=OlnVIZL zX0nsa%4sbYgdfRh#;N0wnNe4Lu@L4?fV5a)1grq9BtVb=Jybt|9zoy#o0*57!-Hg2 zSJiN)tGnpoZfrRLSK(orT)A}xyrPuD}p?glc&H%I~DO%kg*^sYsrmqOjQ6u z$pkRGD~T1$2?l;GN-`*+iS7{TPnX1yy9`!K@h4Zx5HP^O@oc^sP_0U-mkJ;%7=H;i zvR@JI&D|qvgD9^@5U13ShQJO$M=tnIX>-c%-zEXUI!Pv`5rw${R)u^ai27`?eOB~Z zv>@LuKSXVDqRt4EN{dyrUR86lz;KJ(nhnoaAk85%A`$yV5WgQMH@)vs__P87LfJZ<;m6;V zhOLPKraZbx+1ygVsQO$)QvdwbMG~5K6Kw7_u?4EBb>M4XrayX%&CLp#>2Z8B{c&>B zSknte)uFz_685@;tF1;wuZeT8{SdrvA|KrKSa+Kk(6q$&0^lnEct>7nnV}7MZ{W-) zG3qf9!46#;Mg}Yo+YFKA97d+?QL(8W0%oVE_gNM|m>)S@?Uqygedv~v=h{@osu9#r z0-jC}e6lEQ4G~Olp=lxryfdv40{tsC$P_u zx<(ZqNvykOY`F$dWeuDT#~_*@4>YZ@^( zF0Gb0)*sAP(g0rO(pI9Q?4u=h|J1 zE+M>oF$%GA~#D;Znhlo|nfiX)SU)w86Yc~c*Dz(YdKiKbav z7XzxLR5fd*RzKQx7+$OA8x*C^&v(|CQe+Xi$Up>bI|`@Wkg!Dw6aq|Msc8l#+}9v5 zG(86l46fS^JCzs#;H=XdtQgAA4lPlcG1Dd)D72I*& zqO#%&vn<_v>=kBKd>6mG7521}8defqJIzTiI4K$_fjX=N@%;T)l6by5B0d~@0P#b> z7c^Dy)}*HG__1q&xGG8!UPbAu2J{#(RT+^3U^!6r#sSh3Xn8?wi(Wv>M>f7`KjKQV zIoUvaT1mf#m9)E3lsgM-f$7)b*Eqi07KoO9sMdwrc@)T!gCqO7!NGBXc42sZNG2m@ zAQ5Hvbah&Fd5F6>a=9eiOaP~iCS)Zc_YQfPOR@~PO%89u`U3_;Zvz`uMnDBNIaOPJ zSBI5b`qmoE3E_5;9Td^cSMM(`VIYpkcI?aO;c7jiY1k6od>IkoH;Oqq#wp+ys1bG$ z5WXFu&r$z5GDk>#xJq$eQQY~3zmGr@@DmWx|Do`I2X7FmrN}Y)ObB`7x8)oD`j`L0 zaOONL{2sw|1KOA+#a(HH{=7z7Eb>w5-cJh<1u$$naM4y{;bZ_-9EM;qV(ecLo@B&b zTn=L4en8)m4}0*huJFx&{mVa7hra85oa6Qkl92K3&;vr#3;J?6FgSn!Mtt$h766Zb zx&`b-Z=9qnVM|NT=u9yMC5?(h#WtqartyRV+X6Jr7h_6CH&cNjNK?RKT$JKIF$j?lXGQZw z;cxbGWpK-Y7eQ!wy~M4E<0qnK7^W)(V5R&Y{0;1{5$PLCa!x#hj4w8f8Nv`J03}36 z6z-p>EPmdX{6*OAreb1M(>!D}T0F;xlv}{@^N1HYca5d1bUUPVEVlzfYD^F>CANU? z4zNH*N}l?nIC?Sr6K4f1^YDjQ?2sBpETqhgu}EcL^Phn^7s>~;byq6!h-b7S4xVve ziBll=l0^bX;)sA5U-aq_{7z23Kg>4G=a7XnWN6G;dVYg>;o|R%{Yp4Yzq<#1A=Xb5 zZejkX&Ul4|Zg;~fEIhKOucq{Hn|p?CEl0{H$?sO{c!p%12zf(EOC~pLx}9DaqZ24~ zqjA_)_E|cruG@+|EBAKCpw`kv2dbBC`YsY)Q;WE$V*rJr9twdc%?WKl9BW7fG%O#1 z$pG;m`URQYU<5Xb0LQR2;d(K(y%3IDGh!?12h#$0pfZHB2Uv`N{?K8Ug2W&IJu&!U z{U~*%l6tR#o&7YbNjMuOll73`O!Ep={(2RW%V@#bAo?PXC)MdwXtsw=_b!fmrxOBr znpIS&quw7*&p!@AED1Q*vzzKPp?oKY0A8uBz@b;N67*->Dx0M)a3#wMyBfB?>^-a= zNF{oU8^Nr7{JQNnDV|7JUy~;xUU;{f&H2Kb4&mZ4LG2!Zo^M6PJPO=bIPY=J5V_jP zduVB&ut_52t&iB%coZ&1bWPp;`rSut9MkpJ1B6CaixG0E6m~Ms32~s}+Xjkw1}EbC z%jXQ3OT$31ssQH-)CiGF#yklr(H&5Q99(QJREY9t)WxMxTVIXU=}($6d+2TIXW+kZL`h8gj{$OXDQBq@$& z;cUEu*nut*eO!_KFyye{XMmoO`qtKhviIcw(ib2(k#+I{mBg#T3Ey{c@P1mBZ!r)g zHezvL?t0It{A%1g0^T;X8zDUm_r0F-?FZlgaL(hC6B&V@M_iH#tDrCtQ^0jo$n@ag zdr~XW5&FEooB)w{saB7eG%1n6R0uQmCBIPn2TRT|Wr|ByYQ`IhQbYV4^tEJypqJFS z1qY26A`~W9+5vwp)G@G7^8LAP)~O=KZWuRixCyJR_+u$e@bhS~0-Vi+Vb7sw4iOg& z0d7ND!cMh?2N9zt{-u-JQha0=Yovp7UmtL;cdH7)JMV5aBG+84Dj}O`Q77{I{k7Qd z+cb*m?cE9FJUf8@I7ZHj37;7pT8R!?Y@3)8z#m`^Cc^()hKrg52WfKP z^+`)rHn`VCImBM5qdSy}?2s)e6)F7PK@d82f;<}w9rBQS6+xp2V=cA2dcLY~p(3%f zTzDfc=5A}_Gv22xr^;yD33P0iw$#~$OM@bFs3D5%1O>sNQ#8S_T{G=kNnp44|dx)Uw4E$|83F@~H zR4%Y>5mZo|?IWmsdv`(Q+q;JnA{3ChU;;|LFi4XSk?#QIi->N=DLC8$GqFL%8d$)! zghAv-f$rNn5IImxMiN}wQElYw8M^_o7~8<=`ao}$qR?>Hd`G3*0sznY~8%Ov;GVlE;(BFXW`5DeqOy<|?z3Bj4F zr}XckPfup`n8eV+zI^#ITy4h50-T)40wZAJ;bMIt?m3%oF^ZYxL{h9n1C&2xc*W$t z4435k=B%>bhc#wvA7;2u+fS|4SgXI6uocG1*MK4+1Qxys00T0+S6GlwCdo8<@*S#%h|xEOk(4tF7(OX!YSPUDofCjnuT?`b~RnS+uYHrfs%*XLnRu zVA~r2Edz8x1YjUwnx#<$wn=CMA(9~+P_KD~UmK9)|j4_B-`E_nb z=!kt~?OEbeoolbNRCF)o!6YNYCH0IHwa;b*lz=Eq4EzQr4g=wMq?%b;jJy}b_#-z( zO-5t^9~hi5i5DSzw{-5hz$z{Yh1YiIwi03M4(Fb0_mPQ5pFVTXYMkn%xSgl3d(DlJ zL>^$m6H5n_1K9aMtq5+u5L_6Z9s&hS3tWQ;GQ5~5X2B5XT1XpAj5x7L*SAX3=UA5e zc++Py_l5D(PhSVm_Bsk?q_NI}AA@<$vt77e%@ojER1DTYjD}JO)nHM%I7Vn^O2|nl z9!Bl$5yD;sytJNIiXL8ZwJt*4h$YTsl{euk^|wzF-xvEhMHB41V&1xbByN!YVLM zAKxQ*>E4<6VOh8=!mS)xcGb5cemI&j=K})V_@2#gduO>%l$F(I?6;$Q335qeu0@zy z)C-6R>={QVmkQTPaN*JDsuu+uU<_8jA(}J>v=+g}t$Z+PqC}4l!GkeoXC-bYjyNu3 zj~N;Zg$4-&T|u{lc0+j@@b8MCFwM|Z&_qBB~2AY?kv<`^U5t3E`|Cod&6WRB4n917X7Tt>dFSp&>1O#JSplC%e zqX^1oV1}UwEhF^VT*)D z3dT013r1S#feMFmhmofNV6&1}8nwEpa@i{f{7~|;IfOTCG$nWF9+Lz$uw74&skx#8e7Rmg`CAgDF~ZWZ3^wNr z3oDdrHJuznPRp4c!Z9Y^DxqM=Pe=GK9E#3LnK;R(}5E5YsQL@{T z6k*}#nD&>7ta&aljmFRB#E+lJ#lz`+6dmn|x5G;{i_*oR~iWky=u>Q}KY?(DS?X42$zw>0^AT1~bqpWqe`5rn;W|VTCZ;w- zn7KH)rtLZv=TrOS+PknH{jxdoGn!m|e=3C9`O@~8-0tyMBZNjD#GwrW6ewg!L&dxy zEx-h{EK0sG%LcDqWDtaDfUM6DEO9XaW1Z0S6E`RUx0pzqwI6qKy>f!f&uDU+6Qcb+ z=GNb1YP-jQY(VYU2)C!PP6^InCQN|O>SJ#Ovy=~g7HF=Pjz16q06#Hm@(dV-vE{(y zWyWqX4upWW?mCaTY|9$v-*RT#)o4^bAVeFFfm}kfkl>No@>*!Ui))26C^YMC_m=$4 z8s!``XHxUebRO0!+w_X}mlCRD(c8YFluixDF(V@?ILOMQwIOe+ddVtn(1d`F;mlk9 zyS+)O{1C2Z(pixP91BI6a6U7l!H=@%)Erla5h*e|Q%6v;smzLBB9-h6=25A%q-C;7 zs~|>O-aPY>`2QILExMm9@Dmo)C zJePUX83d9dE7dpBuk-FAV- zLYS_PcTc)Po)jv+lhBQIAosZ-GSH0xKz%M~UvWjl77NHVAd+~J+hBAGfZp%{fAx}hA|W{AXX%ikvJ*9ILV+2^uz_yrsb1T79!0vMn0MeZ!LH! zJhL=yj%Gggw3T2M_nx-)gLm?ExEy_5Qql1+TC8|Xm*SkQ{i$@UI>Zd5`7N1Z;^+Ef z^%8Z><4Tqo#$l9h1hFo8i*Q^mlA94xnv23zF7%o5K`})*^(&5x-tmX49#?*kE8*S~ z1#N~RP32X|HJE=-p>aAZC`6j8?WgJ<)ooPDpkxCh8Bv8O>ZJ+RZQ1j*e@pmlqljXd zxn6DiI_&+CE0rW%8KB@6oB66z>dJ?mh`!=oDIMR2g&I$kyAP@QI4gufoRpSYpW>vb zT;>1e?VPBR_6Ut8(R$HLyIc9W>sbaF8n6&iZHIRVNxGJa2%XNb=Eob)u1h>}Nsly> z<61jop;o6*=sRLf+7SwE!=JSpw#nMHcWpK7iVb(~VW36~gaw*h&yT$ro1X#ic%+ll z0dA)Nc}$3bA|?ggEEqd*Fo>;EGq7zYC1yU@t-8C{>9r$HRo-N1gR?>%%qyvzMlier z^gT>`IJIDB!L-p6s|{JScDR1Fa2$7#@js2|w7a`bHpwt$;TKzU{coi`+F+P5uce68 zVOodk9?f;>Xt%sWGd$>eA?xM^4*{}pQ{GR}|MCv3+L7p@swhsPB2W$ow}vn%fl*TH z!vm3vLiK55+TvEdtPzGONCSAX6=T8hfSv9u*imSYdl&R10D!3aUnZP!!hsQN*35*E za@q?vQq=kP32wj7Pf}#!mc0L*&_b;Y57>gpI>3gLC`Dv82449xA4a4wb8Q}H{xE#Q zoclEo2opoxJv_)*8N|yJh-V3|A*+F<4-mFduIr3DFro|`RQHdW$e{O3yqCt4Oh&q- z!XnRyz3#_>B8BU>+BX&IxKh=f6wo`$kXpCb+QFFP8^j}rN~T*6iH2tm2aRWgxU(lZ zR8FT6KmKmp+lE{keJ8cL(FtjBPm`y9wKtkLO}BS1_XnahVNoR^D)`~Mf+Gv*GPVVj z>A=SUkm?rV55Rl#kkmlfZKfZjrmtHG@a$O=&O=h!p6`_pg|>u9LI!9r%X7+fL?KY9 z4G#e6Rs_69)VF~0ol+#V*h9i?RF~rnSST?1ST*Kus78(>tyeXYU^Ug{<6|)YejFD?toQshi|sEZ$2-gTjs; z^!~6$WScx*qDG%j=G?uEh%k-n5FQF&I5ga94gktxLaK_{_FA0gQP#Ua_Kt2%ChA`C zZmy~k7js<}D8$@sMuPKa`3>S+6xjkXN`Io&c#|1uyIvu`G=XvvsN`LF7EafFH9qtM zP9x=6oG{Uhc)=E^`MZN3BqW5ums$$oE`pGnu&Y);IPC7z7Wq3j0^c3v@x^=izb%F{ z&!$j2XfW`iJZN+A-j@Wc0HT;J_igL#Cp@4ET45xJX&b~PX>R6<9IzQhR`Y$A65gAn zLji06faYdRnICvhG+qmcBqT)>R0V+@%z>jdANPAd-u8QIWO9@+tBWsqw>5cL?YSK- zh%RZd+6>4%ChUdghmR#L0_2O(+kjw2;B)Z&el~kjHEKf3=G&n82B$RBqB|M_*-N zfRbkz+K7^@@c`fP-N89$l8|;v;2BIJMs5on6SKP{^Y<%E5{byda*o`bJP`i@EzjSO zmMbW#z9i#_2-VOmUJG>??vJH}HP7<8g}0vcF!iPk)QUVM7#E?!@r}~tKlqg7UZc>( zsAB|7k2#($89heipVO*lXck=ft|tdaspp8H1X=`(7w8d;b7jSWECK;fKE~LR{S}u56!e1y4z;OQ%0-?^6x|lIB?*LL)_wVhC zISHas?E8n=9zMYM4Du&W2an9IMjCbRlK^oo01qaMFj!MCtlm3gbO1Cr+%8yJ%|OdW zTMByNEW9?_GSJJncFzq?P~aT-@U~cRm;gvMh*7}fz|ZWVn#eJrlL6@ho@oPBVgVso z0l>;ZfEe(vNbD&>X*p2Lpy!o!GxtFigibjpz#;}B!$%l9HUsn+7+OGMkL}p7kjHF3 zphUY3(6y?ZJU$x`xPXR!lcYG^CB^A3DcxydQAP@y;RqQ6h|lYA%K52pLt#fQL5S=S zWR8O*O4E?=6M`TrMPvy8DiI3*QE3R7o z?vm2ojhlJ_P#qlDQM!)=BrODR zyHS!9nDmbSxRL@ecJ0?GDZ5R}V6w=VSoqC2Pq!9k-LF+Yu9FYqRt-(Nv1D?3_lDXl zC5&&;UCDVHF-9?)8^RJIz~L=sG1+DrWHDzc6I`9J&!BmZNVzGxeMwOsDSHU-XT^CW z?6c)Ie|qQpv!Sf*hj@3vc%y_C(J`3k*q08@?4arIh_nucsvZi2@N89%hMCJn2g>w)8cV^8AX2 z09{~YMmca{UKja{S}jBg}3O`S*mXM}hAQd0Tw| z&kwsvV$ld5I)PHmQ<<@A;o?hC#7xhANEla{=a?eU`1O-W&7#IGgp`QJGLH;NJ|T_M zf*lckHqWmz9)+OL2lu%RK$ZyXbG3N*;($4^K=wQf@!t`Hwlm22n6AQ5A4R|;fsNlw zR#EpTMa6P@Y>Vk=vQSiSi^FUm?O-LF^R#@^T67>e$%ktSH;57 zv>a@&;N<$c{nmf@JH!7wJ^=pV?-aU4E?uX-i+^|cZRlu>mUKAF*;{U%z2&yMO&L&T zVv?GGqR~-&sXJg~3S$@lMXBjV95^Y=h`_ZY2U+T{Bs|m==}8nLQPBd1O;K(g&oZl! zo8|*8g%XnCliYqgxz!rIAC|qh+}eA~t-S{Ty=Q;|DM>+ujNBvSKmwS-L|PJx>N%Kc zX(^bIbchJ>Ss=z{in<0Mz@y|j2>qgkYa#-wRm!9uEVtT|+{zX2lt(;)s|2sLU6|T0{8k z=>V`qr0g#@(`Z;5r!9WU(5~X}Uj{_dqTV7*I%+R(y((T@8K~+j_;ztObYcbyVryh0 z0zWD&8%j-@alR1(5@cfG*#((P8)=CJgn%!H4*?q_WeK_> zO7_|@D!^5jpO*h1N$8|_mm&s(%5tUfkTN-8@MO>D9wk&mU?OHcUMfpV{@Is(W@kmP z+_2?~uRWZI2(TK^t?8GM9djIS9H6tPwR!e>LBsuaL@t0!w0Xlxb9B%0x#-H#`TMu0 zClw>^4Nw;a7x$LqYHM<`>)^9MXqr(fGg)0?6r?2ZS|PI-b~&h%<>&KamQBFk91!XA z4G)f>4LD>=Q<|p~Tb^^VK3TS8C709Ho=JgvE?SJY!wFmV0ts+q(KD7N8HT6En6g_q zK>G6g4h569%#)t>et-G?9k=@C>in%Nx>xf~J(svtXpI5z3$7X=|3S2vEtf?s{;HgD zO_9i{?merqiKNsb72}Xv9f(_>lflkucpDW7DvL*15t9C#IQn0|ra2VmVoRf(z zG8s9md&(Q{G8Z#n1}tRMp7^949g}&HgRHgZKOc)a*UfDI>|jqLRKOTQHA|3+9(f3LA)W!_ z*+xkfk;szT7YAMu&Op~eI;&M{ZytuB>XxGwrmgu}ipZ>IfHE3g@xjT7`mO-C2fC3y zF5%_n#p&@G)F`k~?x4KvUY@%Xmbzc3WPBS{(llCB$=Z+lRu&Mi7QGSKeV^5$?Xz0C zLkPgN=gwFVB8>e|GV_Cwo$i z>q@32FR)8oe5kC&GMvXuTH3|1`-bIg9#T@g;XjW~eP+9Q+%2 zC~j_hj6OFKgvQK+M?KGd3GS2pc(W<<>tX=JCvq25YV(}qnIojdujmo*BJ*oxS^$}s zBApw*W&M#nwadLfzxNDj5o8lmiP?HtLi~G4}X|JbYzOPLM|caiDIf#EY&?G_oWZzBo0B3C_n;5>O>?F z1GnYa`PmxfN%m$U@=VM)NWKk6V^u3H7zisobo?vJ=yFR2IZq-QrA!7y@^k!|65eCQ zNKC7n2ogE(n5}WjhzC3#gswV%{ocvd`76Y*QD{dP<3yMUF%+%}+|cJLzU-S6)9)8S zGV-EFyd`QcAnw6~gLku)i1KIdU73rrn57ACX2>FWu^pyt1%(UZ4H5Ysk#hKO@%$vA zgnjk(jB`HZ+ynxQd6}T)|A>^Mo`CMNa@hge*ftmKX=(c zBi5vKV_dF|j;}78v~65||KUWm*2jylUwuRk0Ps{mfIsX!t^Svls4XtfKYu(Rht^4_`bw8hhwwgp%Y9J@i zCq@R~dTBVFwE#6VR*<0D9T1*2Y7cx=UxlV^xR#zcz}y9iQw(yY^7*V$s@~%+XnjEb z*rx^kw4i?u=X3jV?tVUN-OuMe7{gJt1Re`Luv{+%(o_%=y$VRr94u}E*9H6{SXA5) z%i05gb_yz7-2n}Yi$b~-k;U@)Y@u%MX*oYF=U?yntotns8USxy7c_3|x}b4ucj8Rn zPXir1)h@E*QM!moOd3CkEQSJcHE>ZgGQjk7>KBE}g zGFKqb%%+Jv;-{5)TiPC zggGP7swP0Mb!-P|*>+$9H4IDEGIfyQ>Uza~_FT<*TFy_)`PXnhw+Dzvv!RR+Aj0+I zdXE24|5mpuoxSCTT4-kbe$dRKy<(8~J)*t?5!C>#Z6U`U;XB|D1sjA2DGVeoP)HY* z*bG*Tt5H*0d&?&cb!5Qe8!2wD^-M z$5nnX<=y6!&jrjfbMaEMOECr<(3OedBZ?0q+&NhuaAI`+aq%MAh~%(c_I{7p$_6oK ze*7KbY_|f<`nhO4FfrCp;+k#8BG=;)Z9p| zrx`Z24B@RdGADpPOpz<#CPaa$-`uY1X8$1TBUKpmR6mDXAS@z=TaA#$k7RDOh68e^(0*+aX*(RpBr6B1bwm9SoWZ<&F@=ifu zO?uBhjRuqD4Ec@5Ok;6+Nkh2hLc%GM&r1TUKLQp63Lqlumeg`$cYTg z0@w9G#1LG^l+x_Xxr#v*xrovh;w-_35E#XgfEj#+n(Ktg!Jk1+keeUS%q`_j+q2|3 z`bu3KiP@Ft#wd=MQsv3fI9yMpxE(>nma_VRn&QF<8lB5bTv*1l{UFlo(bZDQ<-pK`IEKee zNvv2;BJV=oCECntdALReRXK8=RKf_?cgROn*2ri|Z~enMS<`2IocG=#->A z#-D}`SsK(m!eO`(96i)A2Fa5j%bbjb)E#~&j+nc_5*r0AotNWv0?_Bxty=tFfZ`Yr zZXwZ2k>|8p%*IRlYRYiAU1Nx7G_WTnlAuz4^?0+u*2VKAk4pxdP`HtO$^0YsXkPNi zwQ1D@+Ekp=;UnsBt3LKV?+_! zw3hws@c)#ACr3YEg5DinVoOSHM|tp-T=5z$C!FK!uUU>0?_bExS~2PdjYjqyDV!my zT8d|XjGl-+E-oCvfdJwG&xD{WJ-YZ_wD)?nBA}NKvP6cCaCshgQUmU_;@f5Ez1|NWG=g8a3H{GRH=dlw@?XZY{dbNuHpQ+hk(?NpxH zvlOZNd|M?_cK1VAn4}{8FfOL7ky`gciynWi^!R#jLx(53`x0Yh;D8ua_W-L%HAS|Q zgkZ4nOveGyU5L^l2X2NCEEb3qq!CCYB0r20A4NrRU__0^GD_!3e*bpzyT6Nfw+QR^7GeF~BHZ2o3&10g;X#6dWSauy zHX=5=*wK>|<(xK^bcXoZgK-4p$HXUGq{t##i7;{y$}Neo=IM@KEmnIH;h$85_YONU zIp9FJf2?MDW}tm)_fshN$$LpNKiM5~27yP<3_Kf=M%!{6j0EV_bx=B|b_lY-D0Q9C zNrBI-ft=kMAQMZT7Wpaj_OHZg{8ikR5T02tJ1tZ z3G&Zfv*;+x`n_dYzqc%RS22j`4@BmMYXHaEMCvs#=u!|Cxkidi?Etu1j^T$MD$f!? zfdxck7$cwyrYhW=cxhF|0A%BnEdQ)kjNJfWz+|42OH|eTj>%_6aV}g4QLZ>5q@pMX z4A$|1DiF5^yo1)rs@Vh>1}EOJcmZ5`97!n0Sv}x55SrE+&Mih;>i0~o2bUh4ms>xW z^oWPV$WC>c0w|bB05QM>Xaw-7Wi5nQ$B#ypf0YGFMfp%rit5)DN1T?u4A+^uzJ+uy zeMEMbnZM_L-_^@F@3C|IYZVOWGr2waD5js6XFz&_|hfN-N(JlXfc}0Z@ikUzj8p}W=-(Zr(ab^==LAZ&P8ue zV`3JPXE?znx0V1q$Z7U8}kh`KF9K&b3kWk(ffZ<@K{6H@4 z76hh*^F3Yl>`Q)oqi+T;k|d^*7ZQR+qsU``(EO5z>H=>A zs2aiKXV*{=JDL{WCbt_zFjfJL<1xUuDO`d$;Ej6SheU9!NvWk8Udj;^9AU|A=p5*X2?W39mrH^eFI{GKwh+UGj{<+JD5$5KKdh%y5*zhK1vVaL0W@k6 z5yf~t79pag0`kKIPAf5pqdA2io5K(?)YQL>1V5V)<5T1w@c1;uAIc<6rMlw$zAA&p z?F^_YJOEyX*%V*5M#=r^kk1S`huMkIAxN>al|6$NyW4ApQ=_MQ>hTJk8vXh^8l+@0 zWc-Ql(By@FH36$xz!cwwtEM|0NTp;$^@_98#wykE*uAD>w;2^@k5;<->rCWiItK8_ zg8=+1u=-Gf>_@Q{C13(|!3PRNsRYo^cItqKC^B@giNfU^M9`X3Ei5Y2rU4>x+uI8+ z$K9<=n~3o&j3XU2dZrhma>uknBL&K{4bHPfOClibqcAV@5va2w zD>h0JM}-28{kQ;4pxV*@1xs9`bHK45MdIv7k+|;2;jZNeaLIZ)Sf;cDaQ3M4)STGx z0uu~-a3z3Zn98~x7X(_GXT~lt!z08l>;x=)6ag+s9Dw#T&wFf%)17}A64!3YBX5fO z{AD(ahe%kVEST|TI3?)g@kZG0t+!OIb+LVL$O4O;){6|QWW4z&kLjM*FNiua2nO@0Y6|ixH0Vvz)tsgklJ+ES!Z#&7{TBY`7 zW*g@N5-;kK)2w=&zdh__L_e?`DE%oGa9s-STHr8q3aMl}!b~xqlg!$s+*^8U#)CV{ zA&Y-v>qfwRufNJf>8T>Q9g-oHBO*fw)+3Uc7rj${16hvrNs2+15wJ0&^j69R%>c33 z#92{xSiA_nFvi$1tD)kn*#ad#D(0e+xySTPL$-(nL=vSKzfh0y7mBQ0MUJfCl5S^Y zzzL#gepIkd^%ZYefT4uprjmPUK>23&givcDbC%%xIc=H$Rpd33&w@e75>9~Zlf6XT zXq>f`n?IQ}oGe9h8>cEt#&61SqXahvlKlb(IZt73Hksl|B{*{10`I%uyEu8pH(vhm zdOO0)L?+thnYfAeD;vU3YU1;P@uHVY3*Q2G(d)X(M<$%aQ8rp-G+Yulj^(JQ*M5%n2vmi^7bm z)hmD8zKj-eKiiSEf-BY`2|nBV0M&2s^Qku%CvLggAUSsX(Mz>*#18_ha7GE5vJ9ly zBa!(`oyrwllp5#Nz<<*wy8YZQ*|kiF#RAUBB5yo2Ch3g6dzle9@cuGnzYvBO!oQj= zw@t6M7qbmVF%#Ckh{aUOp?qwcFMbnH=Ie%DMouuXC)RXPbWU!3JTAoT&3JgJ+y#=c zBymhvf=QyshcHP)zGu7y&}ArV^Cb6KL3XX_L*(jJ>%wU$u?p8^+`^edA{ks z`{3)7+5qOb!%QdWim+dKaDR;bY7>k!C&2)Bg3ZYmD*SaZG^^h!jR=ip9-=Y_9qMBT z5Qkv5yT|LqMVD`{-XijvAdg#>xNB14>TRH%&vmwXka(D&q7~^YiEEmv6MAt7kB*0^ zD3T1F*b9IX9wbJDJQd#siBbami~!~i$iPx~go;*=VH+^iE$7iu_KQ5@lpzxvi2)1% z(+q*H1wLS0fWZ|dU<;1!&j~C`VY7H=Bf9anlC%NIxLrU0M$}8*H=|yv*lG$D^}kiG zLLeaK09k|{AmR(%J zAh3;oV9&be01u?A6St1@x;~K;8X|DGJ4#U5oi&OI}5jbAa>~uOgUt z{Wh=SE2>d%a41T$I(WMTayr?96!#WhE&_y?sTyou37GR5%>M0r*e6KVe8CCH4Y6%a zuP8LB@?pxBR-;En*iN@IVYrFHXOvQlj27_#zeN~X;#I|rROKo? z{OQ{`5;ICe;X%Y`A@OMXnap^JCZSBk+@~=iyYvmh2}o%1S!MVn=dA1Qs}oh+W9jeP zP8jUqOPO}U{g&=Oi4d@TyZR{UL920G5O?2woOpq9;(bw*GyZux@%oJ}Jp3F7pP%;_ zWSRsAT&b^k9K# z2v3%vuE@PRb6j!b@XgUKU641mbqn;DlEAm`XZ*@=v{>E71c_7nWPt%vKs8`!k`W`# z@YGPnKiq^NA+=OymD0M3Ln|`9_8Qs3*JPoXY#%7MC^ppqyFn0hCA#_$@fm~}vHgU2 zon%f~=}pY{R>VxQP)+H$5Q7(RRvUa^<-?>hZ$jP{vm45Oq}Nu?))zI$Ej2^P2tsz$ zK}E_%GG>GyisTSO3)8sP3>V`sRQr}nv;r`HK*tfA!5PvxMUJNGPkuB;rC$q!{syeY zxjTra1Z#HrTOK3j%Pz}S-IOS)q85fiBM@x8{It+?*rUGnWXCLf_i*GMs|6twVsGfL z{?@&F1=;FfQHQRC|Fpr;Tieby~Cc`M;sw>ucrr6;<(b0efiMW{y9?I^ZgO29)_iwP)nWV5))&x>Nuj8K{AA#VlP z<$9=7wYW@E?a_zx3;C?#Dn*!W+VxXeR3M^!3rKkys?bJwpD+Z3IsmGBUxkzS;!Q>0@Pt=GP#S)oK zo~u8ySnB0^?csCp0 zt&)g&M!&Lsu&w&5{#J7CF~N@JwK(^zX6N45OVMJ9VUtZ1kzZKy>&w)RWu?aQ6bbNWh`9-g+*u)TW|q$Sh%{UG{dh1y*eRQe_x$aTp)~sZE$z zoO;swpm@FXo{1i04wY@E`5!K zZn7*(45e^2Y}mIYiw+z62j7@L>Lk|D(M^`VlItAVSNz!mhIR=Yw=#tuAbBnrzMOA!cPlKxTaBW3Z(c;mE&JH8UFm@Y3XeC!lQasiTqb1|Nx|d!X zMoVsg?fu;~O#)&`Eo83*q+segV1#sE^CN)FBY{8gfFj_7a?u6Kg#pNlBz6o9_yE9t z^MGJf%HlVeNurX@Vm?UIw8{n$3Z?*p6d)%6)Nqjz#c>Q(!t=pG4!&`_uI#_<^sXPo zpHumWKD3)8N#9eF^gShM4~{=SvRp0nfbDNuiIe)N<;PIHe9uR1gr|XC#6@}s5E^VK zhH<2uCaU3({}hKBU{ZpDB%%07w{?3jL9%x<#B2I5N03_O!D^BRZBKd7_LK*$JEv5X z>exkmfZ_lq5_FNaAHtxu+#u1BF_##qRfFYfxPfDPChP|nD68PA@KGfMpgy-O51QfX zk0=jijr12J54&~sugP>VTH>8hQz*c{oGrJ?dd-hTZC$rYn{G*4x5&(*rBtV_Q=`34 zXtnqLvaGUie`T}+s8JOCs)pIOZE%!55=HxTzsUF&00Fp}Ftlfa!5v!4O@@PTkkhIK zm@sJul9_s)0`Zy2oU43Hoe#Qlm;cN{+_N%m=FQ^{R5JLy;!!JAFnEBW!=g$cX^mQDOZ-+L2KibY}x435cRGyuwKT^#KSLJJ=B82#&kN4!{ExYJ^|pVxzI3fki}z ziySVCxO~zgbgtn6RmrMmIy!Bepw+qwW-B?j3A!OvKo7J+wg;Tl)PbKn3QeG#5$#Zr z>dg+5TuxTDEjA3|Pp(}xr#^9QiU5S~m^=vSYxE=Q`%0g_uk>}-$aF7`LAM%(h8-eY z;n}(yd#J^YEuw*oQ#T^kRnROumKM5Z5~T)-B|va}EsZR{RG|x(o>x}wi7Bp8_=r@i zS@>|u-~=v_Mm?(V?Zn3FH)3QxvbZ#3&+aQO+P>n_U139>HMSzt)*~cmj|c>b5n>UDh=Eej0px&>IkO`G!Ij0uux>H~D(wa(j+%QRu&lz_>~4u841kNV#OEZTX`F^Od+Eqg;`lnaNgVO2s5gwj$M3`O~UI4rf>O8sIs(K$*a# zLH(<_p{wrqnrGl$awujX)7&~~eoYVW=aXw{r48=rJowrZ&y~{~lt}&VhW?}%G=Uvq zvQSWpHF>C>k>Ey6AV)wA*N5INg_T6OqO%5olgrloyV>*=wg5oN^{*sA9=;-fN8PbO z?;gl8Y1B9P-HqgwrR8=lmRmDJW$pc)PY~eA*p3G9@c=~$I3UcxqUtwrMEii|bU`r< z7?8vRRi$M*k>-b#Pys|-9atg=Ar|p)T_BO7GQrr3rqb|OOD{ry%&6#E?u8@fS{wsTH)$0cnD zViuVl%iH(2sfjR+mYRtA(eGLlvA^pTtI+MR3f(<&H#Qs>b3!T_bGmg9K_ z7E%cF(+Vx=Jn?Nai4$WLc)Klvq9o z2S*`{S^g|leo(M?<<~J3_@1Z?L^=7UGlZ?NoQDDMSce+nKzg z?+&n(8f?g7X>xYAEu3qkF$ZDSW8ITl93N@Xi4SlI*V2(n4SzTISOU#*P!eyapt6W` z&rFjT8ULDT1`b@AD3=FhstZiWV%WuomaOT) zJE&rSZV`7)Q+;&0ik(Mc>n`~-N}|b$wBIPv+WjP2yPrgN_sydc#Rq(YnWnntyKbmQ z#8(&qU>drmK}j}NVtPO$N)63Q0XZE**io+%rjZj`npKi$WO-TMqf4~?B+-8>iS`=v z!~A+aqIJK!mm&bu765%v8bB$At_EuZiDOjxq9>u|g>D4X!$<*K0&w2c2gg>5 zLIWd+bs%iH5V5KaPUQNL(0lOpk+XIZzWy&*F-9g?l^f zorqdhsvxEwfzbni2k&_L@neoN9h4)m3ZU)xq_U5s{hR^S!~Zce(9Tbnw@N!m*g%ZE zx#NMCY?z1w2O77Gn4XOA_D7E@HSgj+Wym2qm^CLKVO*Nt1S>$jUhw-!vKt23cqXte zD1nTDF!(E^WFp6ZK>*VLJH=q-<`7i~KS^M*Xg%qQx@#y*Ao`-PPSD2 zqkD!O8ye|Q5}*ye-F)y}Lx4LYfAVehPR^Euk=%B2-Ii8VxUYMDH*`zz3mdTs?!U;1 zVKgB>)&-3=YDH-2mP-VI<>EiR?ZhT; zXUmCQ-uvk+4pR^O^ihPugv54j!wH-SoXENbO#{bx@PHG$Q`X2gI28qHAW%l+G_GbC z5M(Qe9#|RN3(qg&fHQrxSo#Hi1B2$E@^1x&&LQ1wyz5>T)%;0Gf;EU7%C0RTR zg~69EK|(myXKwq#UN*G?-9Zruaj zaX^r44ba)hj1VM;7X=0&B62{?w17rynUHRJvqp{9v#q>e)2W3jJO{wjfXM}k6R;rAPY{jSwi$wZEDfxhv#t9vwvUa5 zT*rlO85WN|MJE0CCd3*!mXwt_I#vN2 zMOzM^G6WOH{F1jL021;8{L-uUC+|^sjALU+tdtpJ$qiA6E`Yzcyq(SG#IZWuo+^cMEEw+w=Foi z@p@NoU9I@dO{a>v-?W(fO-pyI6kz|Ov?sD`e5D1(0A$qTvpqBMC>Ja6fq@*N@HO^f zB3ZyC1Zz2RC3PG%egv+_^xIF1RrYT_#h?sZ)1_N}Bj)0s3fvM{bSgzdJrM{f6_r)EstH0vp9xl^rEHI# zlXTA(l0_hg@)H88x`2EMoXyWt{xB@*R{|(#kmLRM7kB33J&OYi7Yw*<(CaC~WS&axKTftfqmz^;8#V0G2;|y;F z+J^A4XZGf_Gz^&&Ldsk$oxF@cTf=YEsDkYPVyXOG_S7zUsVr}I>SUl8#+`>1sqLhh zH9fqiceb&awyF^EP`M%mSAVCzycX&5a~Jcr1JdQ^Hv6^->9V|oT{i(-R&VT15rKFq zRTU?I3WT2Fr$B5op#(bMga~6KvckxP3J4+=K)E0l6?m6OiP3=Xg7|CV7wLdOuL7i( z@-at|W=YV!I*uv18kbeA9LVJGHzBwIZIb1AE5NTfi6YX}nSrCk&;zu-~qcFc{=2?z-GvN^LaBEuE`!EuEY;C&SzI2CiyS(mQT0!6EQ{vf*8( z>Uz!Yh93>tdXjr>wuspVt!ZLdMPAsy4Zw}j!6rb_fNvFu;FNg2M`Kg}*qLyRmVXcg zs(rP=l3Q*}mM?<}oO1xwW zy#o{w)7}X@CesSs@bd0=1PUF~k3&A6>dWzReOb=2a%YR>;e;A*N^=BI=FrjYT?Kii zTN?K6ZDaM`$^1{%VuCI^!8NtfQee=zjqXNg2vQ)__~@SR#;|k}u)T-2=Ao23wUP8l zX<~YaOd4#pFe7Yi{Q-Ej0EcOLA&@GHNi{eXeBIj1y4i)fBR9L;whurIP;%=r_Igww zfuP8b6Fp8nL>o;DG(k1ne%G=42FOd;&~bPgP4G+pLnX$4sJQHrfMxhGvRzZigKLAM zoqXSx$LX?we{)eU=A-fpg)5p7c+e6^VxTV|OG`_Ar&O%z1DV7vXB4)fGqvK9R1VEO z1f_lQG^*hlEN@5Ys$mKryyzdi;=3<*4L*N(^x!&5Ex_`Hh|5D`GPT6lli0HHw~P2U zxeIhdw~#EVff&rjL_)KI`y(z*Atu=Pc}?N&-~)Dfoc9x$jfDHO917Pzh5ew5fJZkU zjM#l|B{%^H1;cC6-GE48H0W+CL=YG%^E7L`6sM4$0ez_bTLv*uaSIMwiY%+e;kIi< zT@6tVfF1~wIeSRm+%I`K2tfK&T4BNLAt8!RJx(g@e$v3M4bphR_tC z3@$e8e4vsLn7Q=t-~_r8k!K3f3J(c|@dK_>IygW;S6cteZFof)_e-J@ipFb>3Nx*Q zpoA8oV@??8i>C(sk&L3&?WQ-f@{~U+oeWAOzX`o1q-B6XsPwn| z92^|TEIGIVL~@#?^}c_J+bZ>HWC3`N+b&6@dd~>?Onf|Dd>K$`8xOq%IK!Ovo*l1OeV(qqz~Ak| z!$AVxm!P-wD31?aq{rc`z@>M5%lRx8G$Tai(HHk!V&g#{QEoKoy%}M}O~rMDbHP51 zizf?8f*|Fck(VLa@Rcd}4nb)Y2%pjtF?E6nMphjL7LRfqeIVeEmE6hx9i|`x!|Y*! z-17H8>gWkV6C@B|tr(LqgeeLO$05y?xUJFHd^s!Zj0aj&qA zW~@C}6RcjV|COr`qpwQ4I87xOe<2c}FUc57IQcH;+gpbdW@q7t1;06Y4Qa{HBQI#y zDKXChSZu&PRJi54-izn=NSI(j z5b@bX|5CE^HKP;DJDcpT`>w8?W?7ek>|~qmF(Y05Yn_^GMPPYNScA*5xz{KI4Zb&P zsP8i13~jghCYsqF1a-F(&<={)>G|$Sl@YvM2>PX9`LGfwXDIWw6R2}$6lh4MgqD=z zf87K*DJII*Yz>7!sB{RBK53vHqL_E$X;xW*YqQo2f0wl;)SJ8pN4cJG5c*bxE}@DD z0CQ1FE=2?_Z7->@*3OUKfwQ!ZkhPws^$#CUQQ&a)4w;v4-(Fv!_5s%~8z@`5TW*k- zCJ<%aeVDxH?!)9o?~d)kg)(8BYHkEe9+68sv;z2Yb?Eq-76k#&&`cd9*(i2QJ>&%i zt`W!J=ZFCH5je)TloyLM{!&Q!7cDPMR=(~$P+pt|%1igWwiEzAMWnqfR5yDrHSwC1|XHZ=?w@Ly02U-eM#2_TX@TTzIf zY&!b>rN}}I1Ys;`=#+5|TSNt<*|wrSehCEn1AkP;(E{mWCSMT>p@+~Nm43X9!!zmQ z>GV&tjfhyY{Csynw2AmVSP61lH8EePI}$S+tz?$1bSGqWPhvKJv$w0q*x`v3;X^go14!VWnr>4TzQ1i{t zgu<+rMmjoukc~zLy8#Q8jnjcxb@>W4C3H3|3w&8Z!i+_@y@~9!Nxj{BAtrGx45np3wjv@+gwQe-tiflorJo4A5wj-(9xl6hnFw1H|ABtI?7xE%Cxn8H*F| z65tr3V#IpHY8X)=On$p&w)31&enevt#e(Vk8-$&WhzvT&IxLpxh7EVZv1WC)6yD#MO60z|< zz7_yK>_eN)g~N{hVi+JT@a6ShNY1uGa)G*b0W_ZRqKmDbzjK$Z^d%RB$8R~ z;o0elxUeLur)t;5lztt@Qv5}k6Faq*@Mz&k;!>_;!h0hj7r}YZd%FfkDxGagj5A(# zI<~S?T1eNO*!%gg7soh{?NT1~XRB?sGfk1KIf9zJ6oQebY*TmgB0Lwndfo+?F@NDp znO~VT3!)2~Br8P=hu{u-uDR?e#IAcwi)TGm^w7EXep_ngHi&s#c&3QbEAs{}Xo%QF zPB&AH6ZVcVxpRMu`Cnxt^c`&!oPkF6Lcog57ZPBqfS~VZ%Ij?pG8te>>k;j6%OFi; z&4k*Fq%7eji+I2)Kti?9XFS}Bp92#%+XfVbv=h1+sTD${%{4%oi=ZgyiU|CU`BJ;z zSt@j)+`b`pp<)zc8)=Z@FTp)6;_7BMi|A%=~Mj+jD!QO8n&LSzDLlNKYAFy~kBOT$KWlxA%e<$`r^_hnUF!J+r-|$7T zjr&J0YGmbs!HYtRKSAsQ^({N;i|j5%WqKohk-dj+vEnDX*6lr9i*=88&pn9|kw66v zXhOUMHB3Gz(hOipdjYWYOb5kJ1~NMk($JA{2JfAPhqn1gm>JBk&k~u!|=tWeI6!1WrEPbYj45!GWECOAw%8F=h}| zjE>{+@xWT;F@J9=`fpXnQ_W^9N8QJ{1Up8JR07Qme z@fKFvDbj>M<@gL;5U?(}4%`P`DDg`4fDTI3eam`W z>Tj{q^AMayYMVNe)$Ig(BBgWNM(V}^;@r?Xy%s>PA{D{Qw;xZhfR_1Y`)}7rC+BDH z3V)+y;Ogx1GrUYs7EVWa(c*}g3|&+fP47|?ce3>3zX z7zQ5FW;T3}){vOJZ^u7kH7;v!|7Gp%zpT3#1z>#yb`nr=4;1ATY#tso8dUoj$oByF zgztih1c(zMumeEo2%bh{RwN)%bahgF($ca9B!yGS5_<5mE@MSME$gRc{kK@wX5+GU z_g~iT{>yp~R<8uKdU2FmsOiJ9#R2RA>_^8ns`UwNKMH-Y_2?SFE+Y-KK4E}r@5GDz zB-X$w^wTVB>uFg(E$g3nS$9CFy9KHwCT1NbIcU1=#gYZeiO;&(Vu3nLtXpe=a&zU3 zI!fjUrc`7F%Zf^?dS4dkD6A5Sc_CbL#gA30@o`mF)^}+Dksqqu3IFSx)!(bM6}oW9 zSHRtq{dyz_kFsE7{aqCQmdE}n*+e?$ba3@ zz#4T#xLfA=DH`5n(aCz``Qsnoyg5{o+rj8AJySKeMXZAs#AXvO;UwD`2>A?$;LzGt#-Uq}hKa3S*uZkp z3dXlx=wPU5#o;U7**z;iB6hq$!+$ITrO-Hvb)fNCvE?MX?|2E7QiJ3V$8?Z_Vbihz zL6!t5QkyNqjf*a8#|Abw9kzRLSvb47EVN=cBnH)c0s|E**$~rKMgAEJfcrZ%@rKPC(p6fmOQZ%2I;?q)m zbfc57UY3YPkpK_FHDqw{Wxj=xtNc`@!!0OmMFUj#>1Vy34wnp`m)vUa6H-9u!Ms}G z^%}{d2uEhf<}=ENpK&!(_DmkKlCr8f2U2ubCOM9N{4pqlv(|nba#D;6M(YohNIIpM z>GqXE{pW;mN`dA#@Vi7yL-k?V>%pY~TM{{;2LT(ELzK%P9URYM9+1@dHVE&Lx|bWb zTHCGqdsj+^WiY{b1xo}jsTN3lPOAym`eetrX?q*1;HHiF2*=A#piJE`xKwXnSHG?eG-rv1q7BuU?O-lkCNMYb;;DG>fdlI3RI!esMk5Ou!*r@?fEC6T4 zrk#KhFi3#7rnzBahDATChZ<|A%ml%hYK=v+t*6#@?dMJ{6ni7}&h(Hm6=^_Mf-(!1 zib(gp(1a}-)|^`PN5(^IrfH5F(}Wt?f}7?{-*b+;Q(5Ve6@>v2XCP-9#zGue2@u$U zJsNmOvPn%JW%iK)Ex?X^7c31?>^S6M2S{odl+aOuzeOVQ*mJD?Z0A_plbWnyzV@5l z?(5f%Ves}nWbSXge!_iHofmyK$hYS$m@l+g^!k>tc zx|;EbbL-XkVmi+4ULNm+^8Bgp%0wy~G`r7$^ODCFiil>EPM33qceuNnr3ENQtHnny zer`}vwuk-++V8#Q7nEsS(lwPClLMLny$D|l1v9;4!Gp-pE!I<_G^nxE_*hB`xNPCP5t9yJ*uRm`bYNZtK_lbhzxs7 zg2piHsvM8{wG7sa40oJcZ3Iw|DwtgseCl! zN2sH@rK-zi{_PpKTLc-SYM#k#I+G-s%Q=y9Qf*s=600!-%Z+F2SXLCt5Jc9Y;f@ZS zY#^F=gTVSaof45wzAKkSD6+Q#=iq{??+7GJy!B|j;=i~oa>?bT;_nqVn-yFKQosVL zJ|t$AiRON(Q1@r6?9#69xtwt7;Ss-pcKrOuI9l%&DBM`W#Gnz(-P@$mJ;6#ni2>Mg(bW9J2{vDlQ@Y!J=H0 zv*fEQagT0HXPHE2DC(39L|Y+Ctqy#n?+7}x4tQw27_71UfFkIHVzB1eDm33M}B zjF+O|9>t@gsS26EB^|`F@3L-rDQj1mtdpbh;R^l@j^gLaG5wZC8SCm(MdZ;I{%$0 zv?Yk242gmOK^T1j>e|hM3QCX8vN~vJbp+l@vHmW ze-AxCpt5X_epzMBQ`#f2fW`1$C&@QBKYo8n0%?Fa`LG~B9gd6oM|aWp=^Z$?%`SU- zGnsZN6+6UEa8ff9bLxvPRO+yF*q;cTb2E}%w0SeNw z5WO^=2-QJ>?V-BTcAdh04P;2q!hiPKubmx8kj1@*S;;gXhk^?tGCM_($92Gr8lqCb za`3-yR2M2aJ~}`BaD;Hh@%s-avLvm3?C!f`w>kC;4*tn#zU2RMtVU;lW&6io6Z7^T zp^J-P7!Xs%{#!p#4DOagXr(vB<7y7$eYZEPnb9A+2Vol|NZBG{vYtqf5t!mEB^Vv zdij6(zv*A_&;Npd{+TZPC;aojt3Uo({__9G|NY{vwColWyH^R~=y85;^98sscsI3t{b z0fF}rz!_uV{f1B4isT5cyHUXvQj#VisOu(#+CVKiGD_-1=e(0H}98?CwAK-olou1ju7%OO+Ym} zaN+uJ*v5VuTB&b>Sq(+(h!zB>rURs^sZq8=s(WDs#?Zt^37uZ7pF={v190EH$abvV z@Tz*z(~MmFIPzfwg8mfg6b49X4c7oW2)YzIZuEfshAcrr9@*C~k?4&K{PT;qr#Q27 zX$Z}O{|yLxn!^>w{^DfpFHYS9k6>y_LDvk69a@#=xR!>}bFlCrh8SpOh(tA>D=@vF z+#^aHNR_GUmKlQr#ta;#kDnhtGW3dJDF%!L&LxO5NLIG}n7ZZ(u0J=M|1X`h|fW>CU;Jkd1M= zuK1%~;B=e-dJs@`1TjfI)nFmW=+Rj^>IrZUCk#Qd{TY^IIlQv#Q-``%iL1z7mnd&X zjIp*TbqDVyr2y&tOI9=w0_xa6bZo{zUI;~ywf^I5HbI#H;rK~nS1J?0`TzTi)7OZW zkY73j&PDM7X+YvCs_wr)73d!XF~bT9c(Z6lWKG0OD?xZ?Q$&4e+yv%e3jKog4UEMB zJDAD8P0)>``jF}*UpZogyHAWJC-4vj?i(D+jENZqa}K&-$RjmH{3sAm;e3K0fyV!U z052uFfI3Q_Gr;0Ta*j~^APNY_jOK~p@>nW>pF$T8_)e5vB)s?0xbe6!BByXyEVpR2 z81?yjrTVU{72cy@7h%(DNF=NHdg z)V{@ly)gBv-)ofMK!;uKM)p#7?ydh}IUT;0WBL2}%`xv69hASC=k`QdsB57OW8scc zUzGY-B6>^*VnaY|Uotyx86HD~&SoPV)b`p7jxN5p{`7nMo%dz_>G$@{dUdqfByU#V z|D!!3whwi0#Z!d28bZl3b~20TS{IK|zPb8ThbD%RT|3~l-o!1^;mPj)q|lB*XRlj8 zP;xxivK<{Y;D$zZpowMKAp8p{zcq+$xNsc&6{09bL&9s~nx<8(N3l>e?o_dE{D%Pl z%QGyeO1KsPT0o`0SWR@045lH)Ac7c48h)Hc&@F=0aNWnvZ}~RG#0LF8_yu$P5Zw?! zcFw(jU!m!a29hFC4I^zk7C0UQU>;#=V7b?8Q%pOn^&{wTc3rzx(h{@o^ zW|07eO;1wLjo>d0{$M{b0>^af%`>zWfkRRWM%#@2^z+lV+4C~U|s%(UTE^)K0G zRb>rJxY%iqVFNiclGatV&!)vvz9yHPRpB4+rgY}Yjp@4x(D^bFnJJ>A`ZQh(0cp@h zYn0V8x-5PCpdclLHn&4njYr9VvS^lY?ylC;8?4M92h(KMNndSxcvp}Rkoh}Z0=%O?mLyFs%HSO9R*5I{jrFjeArRP-z-Rwg>h( z1NrW7HxZ$*A9`SF0;RAOhq0{#XT&S!zMGbV3=}G`dQARsvD@S}bR>vqE>iOyBk%+Hcet|}inO{-`QM^;>!+=FG+H2gNZz$aS$o=}tUYa? zwTB7>n@GE%A_dS!JRH3t(X)rpO6)ZBd_)^818`mmiV+eZ^5NfI$3v+iQoIsM&nm1v z@M~+P_1J5#RFv?v_Wp&fJ)>pq8IQ8|j7M2}#(rxLWm{(I`;i0QRg;JW7mVD#Wt*x9pl@64QjvxVlPm_y|VH9wDx|@Yp(;n zWCEtUF-lM3G?EPu(y#_|c=`lgPa{{0-2@ESaVkKlADVYid$Rri z?7hj799edzx%a0qN{KK5iVr#u!K!L{UB62CIzV1BcD_IqI&`Cq8iY?gRQhBk zNHqjiU598mtPsbba1C>LdcPLA^y&PiEb>30`Rz|TeiAZcP{r0rx;iKU|CZSIg6e>|HHy6oQAA7+Ce-oBXGj!1DD-b_YQft7B+ z#O$8=alG!~EX+Z^`yzCy>Vu#kY-X7rljDJfx6G{k9Ze}8RentybLEc*t-@c!_hKO~ zf|wEDNvmgQor%Q@i~FYRFQd@`a2nA`%Sg*!&!k7jh#GRy<~~YU6cjg&fTN1qWp?74 zG|vVf5LEqdCSTut@vU!je8~c0z9;5aO|SD8&6j7|>Z#Wsjg0+NWA&2cE)~nG?YtuT z$A2F1m!t2i+vCCOMsV<~x{~(TnMmuc-JtYjo3caGDAcT!%-}$wBvUJ6+<6=B`1tbT z;e7Hde_y-}k)3VKhP%I-6qC`y@bVBXj-cl@e*~;@3Jyr+TSa;E^48GdV8iWd$h`}u zM^!1Sp9Q*8jY>U^yZX)42;3Qb_vAn~-S7aR0A5Z+gz{MYPR5eb#dv7-zNML6l>-|& z#O-S_Ukd%Fm_}nLM)OtGH|Gm;+{zYiA6MR_IE_!1-)4Q}`?}BHSGI4!PgK%!3&<0lTe*c zBM>G&exJu|QlajIszvx)(r|+()p$tzpu)Rx;o)cPZ1`C_+h2YWs;Q!)@CKlZ{3M+J zwG)9DPyH%SDAy%1iv@Tb)HB?ldpMHLod9HN{M)=^O){lNBB>{sN*PD}@=8^jWn zBi-V_Vq52EZmxcPNxHhirS{8J}>;cZh zX0;w8m|txtpUj{hjtTihI=Q*R!~kC!=S{CZzP?ZdOoOJ*gfIt}aQ30%+6MSn( zMf@PH6Mm1LmtBZ+b%?ilD?~;h&e=9|d#XY%%InfBuTXSp*Ejq?y0cvSQ-k#n)`(j2&ywMEzY zkzI^xKJ}#=GWWq0mTrR+&x~FGSoBOeWQakorxH{js`kKqxEJq!5980v=Vnp_$S?tY z#A%i^t|(&v?s_O$@pe-zS=3mRLtqwS3_1r8pf+SQj9wKW-l3Kfc?O}Y;ffcuLrib2 z9y`z@A~|T%J`Dvkbv?GsH83nxni&?H|Iy%;upj7BS*P~U_`fOy&*2!GY=CW-JUG}l zb^NHkqfX9&wFCO5k1y;~aMC_=eVo^ollJvkS5M!nDs5i!gYV5b-P(fAZVpX(!24j+ zoo*IQGsMQP+lF__d-G1&{5|VDzCWHguc8dUqb^W=PRT7N(DIcPGDdNY=wJjQ)D1y= z!oOIQW#mC3nw|_B#A7yqaMcgrzFBAPb(GoR-kc$^`Jt?k*b&I=W>NFY|f|tvV^5Di> z+fX2UXQ*B@CqNcMCexW#Fe4pLXA8Nf_HPTR1d|Mtd26~srq7oKCDkhsrPul`*LJR( zYQ$8l69Sw?#i97M6}?G>2EyI4xowPs%gCBc=%ZV)0phq4^X8U!Y^48;^+ z;;L_nqr>T6t~N9^hP%_R*FwoyL)$pIZQ&y4vy0Kh95*Lp-*Mo|Ycr6p`i0)5mB1lT zgz9F_6&1<)?(F5@g&i7r{OMq%e=eXgtx&GV#dp>w$kpNWt$?BG` zMwf$Trjkn*S*)p^j7wSC@DQPPp;^G5?m>$=dw(4EWNY zsh(sqQQ8XhzvmWkW{>)p!K1U^YZog-W)Ow)s#m)gRx$0t9UmE{m3FR{D$KO|ioZW> zMdI4}&!)8+$I*$9`j1AlBV7UM)?NPhzX)yW@KT~4*&{yZ5@H$a9F9f&+)UmAW+sw1 zzmcd;z77&#OeO?iO*WtU)Kt2-WkOBAuPtEdO6%7@zcu7+8*G_I=JpF~xZiAdI1|r> z##9HBN!Uu#?AW4a8p{+CPbMwcnbsaxEv>mQ)8{BKX7c&9&Do|yIvYr2Mr*Z(nWt~o z=X$(%^z@48?R;Ca#)bp>iVX+@9$#*K?HuuN9erz-qt|D=>*SN8Jw*q$4Ai3lVB;w> z$SO~$`D+hu&~T?xT^#h2cJC6Pi-RrP=R4l2@8e(#75eUzd;1)jZCAu0Aa`-5_VaZ@+}j|_bPDYf4n?h=Bx2MGLPP>N}U_uy~DHpbT0TD zIRaiOE)gs{Fqdk7sv9B45ZBPP(^S~)a|le;0x4P<*^Sz=3-F4KZGJlds{3T7!%pWk z_CLpd{LitU{yr<6k}>kbk-SZNvWIMqy4x-P_v>d{?}$doz+P1bL_|a9Q)~?5ki|9RxCfw5IBI{(kqeb zlfpzm6HDcLm=LNPNgY-d?;vt79O7nmC>_J(fAVtm{X-z)+^hWvD?#+vVn5p>BxsX| z*B5AWAFK0iJtY$qZT@YgfZ|Dn=%icvf{7vB?l|bxi#LPTAz5W9TJ-@Xv{(puBoCu5 z|99477ujV}oOY01ZSb#Xzq8Bj4+ci8h-$8k=#67sIrW!FO!H)C!8#KE>M{^NiDe}4M+|9kNJ>%|icgh)lcY;+(+l$o7aF}=p) z{^7aa2flwjJg4TOQ`HRIg;Zo@+fRg+-~P9({ow+(N(1T8%|N-U7C>^%ZORG?PbbsM(b6u3pSlUf z)ONYQTCGrdus;|YD0w=}U?tMo0#s`<3m4S*Bm;Y}4EmdIdpqz0!JdsqhKO^D00qxC zLS&=r+>mqwR1+$1X7rz&P1!xx zi5kXr{T zBQalE`!1B(!VG;WB<@zXUG8n+wP}so6tV}7(C;KI{FDwOwp5to>;q|v?1+AFawUeFpu95{hOwc!tekF7Y#(yNh-P26a_;<5 zKR$S4fdP#8T+d#pQ~ahg*>&>W#q1F#)7B?N$nWHntN|2LF3=0-H!{BY!@P)bD3o-% zT*@)g-rRhC?V`%sTiY|pf#yq=)mr|L*SB=Z~VeBXF7Hvhmg z&?^hl2{@x7yqh@g`!MJTC?P-u9eAA+6&6}ZSRD@b}v;ZBXi!UZ!Y=>~`RHLj6xq_N(w z-DS-a5L5J+tVS2f-CKBDhj>x)q)uq(jSq zPj&{hI0kYekYj~$>U(hlXG-iQ6{yT0h~tFZn^)DTvut!IT+?TMzeeY-@}+z= zx~)dnm7xpVJ&n%Y)9CsuT+)1*q+SK5BV2uug@ld5OVK$2QuzxE8HNb9LiL5-yiV`R$SX-@)v5EEBoBWCTC(W!lg%%-;hnz`&+_0ePNH9jQX~3JBX&9?)=2Qa_NfnsJ zq`A}|LCLp7R95urx7C$w0{!s(o59PW$WY4q&zJMD?%=>T>sDI%O{0_9u91xM z`=KgYotD{U!(slsU3A*&B)yl(k4-#uS|*|%WmHXh&G z3-a-Jk|0MDjZhEY+s!Lty1c4z!NJVMMU7Zm1{0EB6fgv2RBzl0d5V&nb`nJ^8t!mk zCw>&(qh?zBtzB?~q8hMVRFp{_q{wzu1?^gC5C`xsLe-t7UzD?{i_IgV))oC#@D|7PbGj8M zQMBrF!cR)@VFEytdVJ=To=f*94ATJ=$IHOcRNAv5_kOK@P2T>i)&IJ!-r19_zQc&q z{cOHJGku7(79=k)ydXvF7o<;$7=G~>ivlK{(Y0KJNE-*VIPx9HoGgz7N>CwzRG?4f zteQMln%=L;hpTD^UrqkkV)EMq?w@b}d|X@<(BurIS67x{F5J}d<9f)w#7Vp{lywOMcrHtPUlzy)sZ@ zd9#=q*0RVqlpB9DUrQ={QqFE>thLpuQv9)2roldQfCEEMVykN)~vTE&AEowto3}^|psad;dcCpNY z$cuxMf7Je8tEX*lKc`GAWoWgE|M=JcDe<*sc4?b*7ej4Zk+cLsrt5XyJGx;IaGxEBZ7UF*rTP!gJGVluzb%$ZDAN6BGx#IJJ+094%^7<12mr*c+UwFqkYW zy|1#>{5SFQP|*GJ$IDR9a1~=N?IM|eaRrz(o!W?6-C8_7AE{w|+bnH^9QZWkRBm@U zXZ13>d@;=V`MVc`-x`3Epc#a|@TD|on|=e@y;()>T(Suk#!vaaw4_LlFu}lv`5R#F zy9sbU1L?llT4CN<^Y<8UwaqC3`3|sh=c+lyj#n2a3-8;mjKH?DI$0+laopf#@mOcH z3R2fWm~E!N_5iE9JLxHd?Ho7nDg|wnefTN2@t|};+B-fZozxhe$M*-WBaM#S0MPWv zbE?pVni8rDoI(%gN_1!H64pI1$vJUOAlU%8_bQ5QX@;I==q<-QklO@d=-+#!zg%St zoFt1OE2LA7VzgUj!0Zz|Q@4iE9j%;h2XM8j(c(k9KHy(OGqp1VB5&sY3o78t9>V`n2C=xK~F`W6Kvtkt?kwJX# zk#PFgWfl8s>2~e1x2IL?#T|+eiCcTVXiY(HAD6|Cy{}ci*jEY$nCh_P^e@T@Z|^+`<-Fc z*#=zsBXOaRs4HAaH=vfglp#~u2=V{)Hl%e@Am3{k@^;vT%1h%b3B!QI3d+H00Hj*B z5kXjyJI%oqR~&9Jlg5Q!Bo8&g-VMOU27i341dM_%ydF18+ziv4&qS)mbz@>jSCgYk z=`4%s?WH)dax>4~y?b*ez}3O?^$OL3pZpKUw!DQ0`N$N>O!KMSu92|HKH;$#dD?MP zd8O|8`V4BCtdRfnBSDpP*vj3%7>=)h)*_g?tr~A-66yg8RWaTMt83xYL;yqOhWJ1j zXal8xkVRmn1^z9&q&PvvRU34tZB4zFWj0~&{scA?$k!$Ok_z!tu?Yz5$FsWXB(zyL zVXLdEHm(T09W|@===p#3Dq3wgZ_ldLrV-|FL?LbvGPA4M&JN9Ylm`coCxchn#a<;X zIgh|`vZbgM?{tX6q!*6SjI^2aFWJ_B*YU=B&skeKr?#Bc`)lUWX#^QWfVO(cUtCu{ z4OLi0UY&Yqbp;5L1TkWUUI6_YgxDDko_yo@FSmpzZn?-62Yd&esB|3CVwphTgKE=8Znf$hA%DdAu z<=yF->hB>UUI$&GP{c2=uRK0sQ*W*sG%bblOl1V z+NT?ggrJ|p?dd03$^oSa7FT$KQI!y5!Gj;bn4X}6m81@klX5kjf|E3GzCKf5pQ-!# zNOki|g~6RZQo)@*QhPLA))@*CRn7+rDbl>sDcuqmOx}%Qh^n^wd#PIUxH9kRRX|B(E)a8LkVyTVfZ7Tp1Ftt0jPjIeDxzmTX^b!eWt!XQ~Q6U zcDK}*RV~-IH!C2Gc}xBJZ<|``U2hA%Kh%!fD8$bwiW);K$+7)1jcUpG~eP>t8)oQ6~V<($U;7&oA7^pxo^EdPR^+^AF!2c)z94YbM*}_7~da*i2N_1&u==~MaR2|Zsw=&FW+1#kX z!;f0hdcoVUq799EvqfeDjYwDbumbZrHDJmoQB)ImaZP+rf z5FhYh3I3F{vNu-Z>9 zmSI-W5(YL#By9m=f&cvc%|IF&q`_j+h#!~sNt5}vEDcMSx*Mrk9{|zjWBN9@+ZWc! z;@hPMR%rdSPCUCkv`fuO80FGkeda+Qu@)U?bHi=0rT1|CmHplgwlp7ZJifoDgWw51 z4B#5KERm9rpcV@ybVxv@QFjXxM*Ib(={MSA(q)hSp7!YPX^(rr-YNbi^neSCSV?^%b&43_%>cfG5G~Lm z_t@m7SYxV~7O82wI{;k<* zPM1Yc)e&L?A>*~9%5Ca`Uq7Zaf9dnrn&kY z?XQ8Bh6=P}!dQ;}-W#Smcw)GyMYD867l2&Pb^%o54@%&5$k2DWpeXNFf_hh0uZB2% zZ5qdeZ*NUktkv^ci13uXniqj3w)!n7{l$1ox?+VqIvdZ)(&qbV$)WX*4Eb{_k7XQX zxiRsco4!`LF>UXaYiSgDk^LkzYwayx+6Ie-uttxQaA6)!ZU~s#aPNvsA@I8tT&yyp z9l;T{sK;db#Lx7WzZtx}9X zaA0&Ul}~Otun-Ai?LK*I#(QJQ(7qYiZUR~53l@Jix^3Ey?bKG+ZnAOh9=w4nYy{8Q z~}9j{`4keWnj&l zDL()K>u=9qziO!Xuo`Imna1zzLcW0%oZenG0p7SLnqaW~=#aa~Pg9L-*^7k-QYFr; zJn2HFLEj2WiKmNXV{!AkvJ;gVyi?LWGmqh)Eyv!w$@w`!Y}1fr=?))fA|!3asbTBa z*OQX=%}j5;v=qshZwwFK;77grXXfU=k(d~*YqM6mF3rtYGd0hrOj+Ku%>&WCHmX7z z?d`hG+3$rHf>wFucD%HFewx4db2U}f0nyq+d=a*`iwYHPNfIYD+tpN09TE$^n#)(&~R!VS@{a}s0=ribgH}TYw=|F|aO@hQFjzlT0fKNDbk!A8T zKQsA@6(6~uJAC%$$)o4w`F}WDD35Gk>MzjA?02KoZxTCh`dfQ zw%~3?6x_{-`e!oP3@?B!+|NC)_JNe=MV#dcw3s4_K+G;-JOx}JeG$1#a1t~ZvWbNk zqWG7s;vhl~2+}>jiHHJ|zhWcW9#)st@kc*zRrVTwC5U={Kdf$=z-U;=O#=+&DhI9_T%A99BJxH6Dm%$Z?DS(Ur$GaNfWJ{U;cdZ@xqooMXy*G zuU3i5cHN-p+pJQSy|YC#l?+;7!~X8gAJifJ{^bkfQ;Yn~!HsA=Wrj`b@Bef?x%x+L zVDQ7+=SZH3w`%@oSePc4*CWWmnCC&G=sb9EKAGVD9`Q$^*MbN@obBq0eRS*p?s zUD;eLTZpA#C9?}*LvhaCls(fsSwl?d=*klyi-Vq$5F(tkY+pl(<8%R~m@Nh7#kH~P z#sxP69|jLZ@>>>f@vQb1hU2AjydfHpS#p-%thL>vr&?%_yp35=KN4`TS^7$G|8$t)3&XcvL^5katakw*B*?4NNcxCfN z_hUQgp7@E4K{rtwa8@U!f%)h0(kno@!@BqkoMZnuImXqG9dB0+)#+11T^w#d?&4kV z*=`o&9%uF!Gl90)P0IvkRK%#Wf)+L>#FeeG(kp8!2I7S9u}8ZaCF&G!q!vXJKwCwW zl<{(#TNpaXi>zvyY%3sm(JoEccvVw42qj`m@W4~r!M&1s;`q!nLh@GIt*hw?ch6z2$7+H}e&|?#8|rlgJ~ zATN*>9)v8Z7kGr(fIJevK&(3Er3B<71%3*{MC_M7cvm_#>JaX+G+pk@0D~I)_iR<+ z-@?0U2i`M-_^i6ZldG$CvllU+oxiUHPLcLvcJexV8ds%T1EkQxuKM2v{Mya+1Bu8?ZpmOFJ|>*7RE0p)olEmmL9CB0+>=p!@qmj9;QB* z8GF@Pl@iVQRK#BAhvPv_j?8?9R&A#FX{M}dD$zrs3)gS>o&7KbvLpv-IG%DgG z*W%P=rl*ei=m+f zez=6!J$niBnXgQIzx}=Lj(S7XDh38 zlX0`G{BUGseNENl!^Bion$lLg2gVXk@(@D z-Jb&`alX8@+hWT^m-&Pe+425y$P&p~Yj*F9*FO0B?BentjS=33bZuwUarxGHGdpQ6 z+aEKz;k-Q~j!h%M=w9V zx_q3S{%-o_>gId@Mg8;7;raRL_kZ}WC(W^IylcVnt^g(NL%J4prb#<7Or3A&ZXDB= zQ~IYz(_FkkpZZF{R}#u*w|0EWH?bg_qF5wVT=XVl7zc0>X}XL;%4lmwdpD7tmGy88%DCfVf1(VSWHQi&aDOj`FJ%DRy>>-D&Z=`HC>A5NkH%;3`gO%)mkQ)!SiSDCyINk_ z=&^rX_0#2V28UIU*jw|>*@s&N4&gK5N*32wCR4n0(%`{Q;AU2wyN8-^pCkSTPfyR@ z*<&PAubE|K+1)Up0pkr-Lv(737o(3&!Q%4r;K5L&AH+ij^dlIXmcZ8*Jj6*Gh_r-i zbQoBS9cQ8LVg9oj7&mh$k~1vO!{dDkcDE*;SAOj4(HUbnF-FkClUq2x6Xx=gm7P$w zkR!XGF7~r^4;qWX{u+y2k3qM};+8Y^fF2Y%Tu31uymjW(Q0iwqOK@jKVGNR`h>=-L zARvSLg@i{`xnWK73kv8yJ`)L%<*#2HueHv9&bzODrWfCRF#ng3zte+L7g>csSj7w5 z3lb0JU4-)NyKn7f*0c~Fn+WTDR)U_php(CI?!BMx-ut-+2Uh7OUV=-5`;t{CXl79^ zAf$>8=v-t1b0_7$Aal&!(DQsz(f}C%>V$fGAx+wg`{@A!*k?cGJGEs$9R=*Wzk>br zJ9%R|3o6~aRjZDmx+cFExu z7;T{DQeMYdxtM&C)Y7Vo!Kyp)Vs|#_8QznMTd5G&;hD+hnJkj|t;5?`KCzp;Ulv{5 zh91&V`Q+!SyqaJGCwAL=X-lo1*6Ee}D80t{kZy=GYyYbWp_}H2<^kC$GUXgcJCi$o zycj#q{z}n$%iG1Fn?w5dMS3ZTnbJw==F0utrK*=^MIP0GN6eE~i67&T6%F~`Itr68pwLa)h#W%} z1gYN=l6;xw!l>I}advN=vwQ3Mf5KccDEJ&% zmvth}rGp+A(#Ija)=8Nz$Jf|Fl&%NZ;b(rtoI7pZV)(qP3U>Ov2HxNItB392T=Bv! zoGb06dTFd)3_-blZp)z91^plV|J5fk5Pq6}eOG?i6xKV**5*JdCvOVt&2TFpVp}>h zzYZEK-dkFZHhqFuCU@*6Yae5x`8jls!)EQEQwFt68&X&@AVt|aifr1@#3$kU{M>4d z7N*E?z<(O`M}tT^ef%vSQI4m!Lmi|Ziod9RJu5?K9mkw$xG)Z=DWG*ZK5w`-Y-vyQ zBx&los)`RHE;N$zS~a~IqU|W;Edo8X05=&+V2N_}fl=A42=|*R)U?XELtKm}+B=9; z=C=#;sfW|qRoi=IPC;eJV9hkWan^K13FXG%G?z7dBR6oM&^y3+B4$}y8BDStC}pBr zc_FHm-%Yg6c-SzRNOFVrIw~mKODisPQH`&4^5iH;r7Ta-9K`frQTYQ>SgFhr^D?cF zYb$KjjK-DG2G!bHv+ZY(zi;$MtMZ_^XAB4pU7WDC&rkns_DE`nGM_@PrQ352YIf4l z<78!iAOcYQg6GD+23G}`V(wX|uF6PvTc%OiXvwM53DmO8DPA1sNATpfcEP_5K6xrT1gfX+AOG@NO+ zYGEv9C8JBVYqo5ZA8}u;r5;G*90<=wAWP>jyh0VzZE|GI{7pO8{4tSa72UWxI3B4C za9|{?@Ueas`K#uoaw0DXX_Y)`UVS~1SzRZ0`jDsNE2*~H(^mb*Onz-&b32_!?>N}y zIwreaN3T1)PPTX-D=*{sZnDJ>SwFZx(uaN;P+xA~Ch*)<5t5@QSd=dO#b_uwSrR}3 zi8xb4xhG1a0GL@+;*@waP81M`FL@*=%LI>0&u_<9+r#-e=GIS9$Uh`<`|Kg>+SgQlN`q%Z;+YZjfKn0h(T>gQ0H9Z3@on zb!rYw{PlI<-NGn@Rv!gwbtQ~`x9xe_F5jT5tUD}Y9UJ1ne4SS?H?)HPfG;>wc$GAu zXt*Ga>dIKizobVUFGDkHXIkxRn|I{bR#VhC$mf!6d);EYaC^idG@KEwTBn?sU)RtB z%>M0v%dpL~_2St=5>XIvrUT|dWh0)kD8 zn=^x&lhcWEpgx!hw2_mVfMU(dd-?p`FX!IDUcwt@e3?zNpRPy46Jt+}QTFN1-!j0S z5VWmBAsvfR%V6v1v&ZJd!5e$#s^3^PLrUve54SPeSvd`QIgZ`3Gr>03aq)HYKG^0r zt{mAvjg=Iratay0v}L2zR0gphIb|4=sv^mV=!Fv^h>=iuasAqLb6(_Nhq5q2Fs)wh z^ar^e27!N%4n+IbTx_q6T%b#k##M+P0&0(f$Vng`h$?|K`RTp1*KQ!Z+K&+#gk&e3 z@#=>Cni$-?ElJiO?rp1aZ(Hr}?8b0T2mu9h;{bvXLx&vSr{-H{fE>#N$2J1HhX0GT zlIlcgtip~%>C?)^V!yntu7MrD+Ul3J)v)`$8uhl-sJE^5chZr$OWiz(QNqIw_o9Tv zKjArbY#MANFb@Ep#6=xIs!|6veL1uVdU+a!k&vjYIO%~Hb9&#l>V{u!^)Ew4C=;!n zpIt#toY#||=K;pzUsnhRed;-j!Ss57;dj>*p2~2mV8Qc8nRL#GS2fclsW#%6L~$;O zS)8X~eLYgYovbYX`FE>f#}Pg;$zmEvG|$Z6hUa7Gvi`@}hvA>EKVJXKgT}v`nLiv) zhaW#?$D-2?ADvV%Y!zH`p|;6I$z`AUh(WM;yRt;y4tn6I92!s}caFZ5u{ zUi@r2@hx~3Y@(#;g+uS;x38WLiYZT*4M2178Z{zJB|IrhY)+qDRV4nDS$baTp?9~X(I9?ppu2YwVQz2E4wiMiiagfxI zB7@pZS=U$*K_&WJA4TIA{*WQxibCa2G=RR^K|eC z|9jRcnI!+AGB0LuP0#DU4Wt(Td@5*K`qIfVZ|b!-Jl^U$KZRTON-4I804k_}Z^YxZ z4ft)T!1~+<6(Xr_trg7Wh?b_7Puu^J|5#$ao|h7qPc}+Lb8(#ZY?1AmRscAZms-|E z(Y(TbL8g3RJnNcUOoq;UKHY|)xFry_!bc4_V?jD+;~d5yca&z>uq4}>#p3NWzbr42 zB=sMi{aygXh8bDuz_57HldyPsBJ!AFJw8fI8c!V+fnuwXkwn-x-x8Vm7&; z@_a2$&z>}&nVuwv8u_8~_H-&8jfxW{utWj9w3Y@>mB|qVSwftxRTSkx0Eu(cK&H0e zjIdEt_kiDlfNF8V#nZI4DReumIE}HL}rKPF!^{|5{v7pX?@)B1T+NNQjmK+ zZiwL9$MMwK^5VSS7|wM=V?Vcc!%ZRgLfsI~MV%06De!yL+Gci!=0P!`jto3m_u0r8&W#3v)`DfCF(tew>fp0h^|&e+Nit2Iz+@y$3JDxfQ5f%)|< z4p;#TXjPf6)b`{qx@v8Bqj$0A7Y0|vp{|t<-m}Alq3<) z2Im*E>#+Kl^>R(qi_#izK*fn; zYLY0sj@m@;zMylyl2!>7^AmTZRkZczv5Y%gqbYncmgZnuX+)X^NisVNC{ISCtQb~n zxfNQ6FQx7&_0H_-N>G;PnRLV$`jW||xTZ51N;Q=xQ(%RfKT(2`h59JlM;pmiRgKia z2GaPcvaw5Eoeve~sEbh=2mC?^!&|YY5kcCuWPz>)QrS#FvN1L)-#8Pl_^#=0A-F^u z>Za0v)YZ-5Kqvt1RE1l8Y29SLdh~d5W14D26;5>9m?v^nLlZ;O?<*HH!Gmf!9S;tE zm@Uw=a91kKEM$O_$;Ttwr!7QtdEMn&5?q^}9DwJUHpwh5_Hf!+vvc4N8aKU5=~NUlv+qH89RQ`Ty1bA2X$aPUl8h^XEUP1lv_^ZGQLUR{e` z9IQ3f)o)}^5!j-ld@Wbet|O^AOVmGGoD4D3#pPAgtkmwG!csN-W!)syX9ih*EiG!V zs!uBMr5usj$`Y}644GNl9P^*5IxcwKOR3PC6Kt9*n;GQnoN97JpSsZNlU=OFio2a= z7x_rrzYI5|D*x%8Ny^U>w;R1V>36F;HcpTJ0M8F;!cRgU)LH}P_#3rj+dws_=N!D_1C^p>kD4+XXXWeV&5o_n-_eK@U{E&&N#L` z<=I6SyAg}A6Qe71;JXn9PA8Vk>ylX7_2GN(b*U`%IJ3WB2AN|-J9=FOnGfwepaOb6 zVv1l0FyZ4<$fX@9t&1q889j6=n!cS3>SrW!P|sN|gF!zI#d8wgV@F8)L0?p~g_S9z zS1ICWffPqLWg#*?F||AV$!vpPwrk0LE#AAk1(oP|ceW(&&X&|ax8b80AH#J+CDeme z6z2InWjh={Dm~O1=rY5_her##aDcT{8Y0AmRiSa@m4pJzJWH~dxcGaqg>QB6O;`O2 zZDdbF+Oc0<1#Zv1^M1K^-mm_=Sg^Ev!W2@A6dz1jB&328j@JYpEDwPSd@h!yVi~0O zvkJTz>Y~Ug{5XXSGAP&gD~j)Xzg7aWz4vQ(P4`k{Hv`!pSxOPxnb&sLe_OBddOJ~- z9hB)h-T(yTgu>&gl#f?g8`?Uo5gIbigdRT7M2&kX`_m}KmsKm zoPMdd37if)kj)BFcZ1H#0ccFW-`YE3171ElPRz@NY6&WsrorH zWnutrK$5?iBktI5H0GFmwA7V{ZG~cL)RZg&a&Sw_n<=$O@S%>aX@YyYxM1HNY$mVKhVn9!eyj#N^r6yuWR>G?m*Y@O>^*D1pqJw^LKHiZS}Z0 zLKsf8h$jm%ywE6P0Utac*=iWSkT3_qYhtb|11+}7#imR((&$OU^7*L-5^!Qm?^5ah z#+15cNph>Ryu>o5PhKsu=T~cW^hSA@hxDn0tlwDGa6MP*W-=Rnx>#h1Z04fU^~%jJ z7{VRs@EhH?!kB!_7;6*U)DdAy=}6kZa?#g`Dei2Hkwu zy(qu#0Bw~=q^rX0@AruK`#mE4i!O92Qme?ag!Wt@q8TKCDMjX@Okqr|>A_8MXi4*e zz$_PW2gq+BeW-l=8{&d`MMW8Q+#vG3y&jQWMHlHLi@xk5a=%vprA(gUW%upp9!YgOC(w`LbyMlp5Hq^Og>mXLC4s^Y|&30hMb1ZAeaL9~W;z z1Mo$W=L>P7g}p?HODsV6Z~-O>v;Rj8U}QMLv!e^_#ti-m8850SZNWtcv;lZSnh0d| zy0|hSw?+ZVa;*Aka{E-ee*8d;q}o``KvDuTAn?gjI5=Mgjhaea;6|v7>J)cPYyo%U zr)*~L^Fwwi)lVt^2(O8h8uOV%WEIHNvcYs3cD)H8UF{qd_Qy}d*|pa9M2PKD598_l zxNrv+mo5PYSJfy4BA07oZLTgEkx3KV3E|AcL&J(_RL^8=sa=jg+Mr*U4ktp@LKb59 z^I@}#)zSpja%2yia_w+&MT7;%%;^G}WD_gf5L30nC6c?zr2hDPtkHDCdL!BtCaGd~ z@vyPX4_ZZJ1(Z;c_k|U9784y^pG&sHR;MnS0-dFP%vk@jnp?E6)YK}ir}xvKd5wPB zr7b?aoa948FVyq^Q`l;Gcn)o*Nq5_FR!#_Z=QYFD3hlpnsy~v2S?oMG#@H%V{TS$nvs__1$qe>fpS9N5jg49= z#f7Cci8T@G6NXW?_QRyX!P<*9=^ojk3%T{mZaM#5UN`v)`-Dm2O{Kl{h-S5q*NS$h zJw&gW=ixiv8i$g*=&s$#aa%aR<(tqarZ$J#|I;g+UOJ;sBwbF5+3|czC;x>{-gz|- zoHxbLcAH3j!0MD;tg-h%^|*MG7P2Z|BD211xC2;_uTPV8Quu{i_2-o+1o~egcG#G!>Tx+|wyZc7$CE2;ry{lt@&=Edc?L z)C0XZCr+L=JiM`t4(T z{O_NC`*sfY>Oyzfefu@VD#l>DxJ;9@pTQ<+KZ8xuJ#^KRpprgK$9E9?s_TN@dLK%4 zpMFVr@sq3+WA!{}Q=E`WcR)oegugU(DOtzWmp51#I93>bz8dT=Ww2ez%p~b)ut`sY z?cac$MFG5NQ0%%6e3mXr$|MMBmZy#xgZ$4N-*K~!Q8U<^y}+h_z(_YB=AD_6Sb z1EJh?g8Mbtf&V1GmBw^=FGeYUi+cdE;_bYGCaacu3cT2HM$J@4G2wuU}&xxouE8TFdn_~{!9#+9jN`Q4a6gnei2yM`|ttRxL z(f6jBii9zE7OlsKItjnPiXs3qpp z>YW{;I+Ea%p>iW;B%v#e`>!TQ1dx)Vn)+h!y=ab)+rPZw3KSBj$~J0)8>tCaCI%I3 zq!9owP4#~HdbT5W@Wa~|vJu+8y13tZZ5s0jAFEpv2#Qdq;d|9qW#zlKpkrNPQ9{>d zT-fV}vW-iNt6}qSd`+T)y7u5v`*+=p;Y@eP2s_&LzHuvm z$uB82BB$--ae?*^iF6Q_6khmfu$Fp}&Ucx!@tBp7^wr*n-ezAlunm^v^*6fy^&09O)HpC8p*;;MClBM=L!H?vB3hXW{I;S>(-i4a zh=zPS*lKVBfXt9mwtI^$!UTJR))(uAG_P4+O=u*M_CP`8w z5D<|nsy%v`33DhK)`=s9L1?3Egu!dy_dLW_U6JN=J?(z3C`B(zoFx(#-Lw5IImln_ z@3TXd;pEu*5u4c|JDt@=UvkqCt2uj}wY`Pm zWDo6=x^E9(cO=&lYCAnSS=>Hq;oaC8JH*D9+olbDZ#H!EE3W&5-Y+`3 zVE~gQDER_Sk75t0UxCzQ@F2#Ckj05UC++nif{uKI1usE&Gu-%yb*4_ToW^mJFbRUa zWH`5LInJM)#Vk%*RlIR4uW|uy0HH_Lk+*e;e}Do^>Gx@S=tiu_^11tNkh|{&?Vh9p!ug498J6Q3umS2u{}1MmJe zNZx!x754=;$l0<%&b}Mu?7KmGl$x^8t*a;xBJ%ffT{?6G--SRAIog4D9%s0P{xz#b^b*mp{8}2WX<{GNbN)X>_)Qo{ImmX}nc9 zk0SZe7-~k5Gb@V=krc$=N&bj#SA`KgA}t}@Y9I9?T>2GklnOvGP3bM(K>I%6DsgP` zZ+-zFEe&{N)(8&?9{!4Cm15*IIYuYLD{{t=+pQ;(DY+Rb*Gx*OHkk{6Hj7P1o_eiE@#1#84B3irN=#rhx$V zjrx%#8@RIICVN4ZE^4xwtwK4*W;MoU*nbB5u+tyCs1szS&)niR( zsV!*LPC;PvTy-4#i40Zk<5Inyd{SB?t^Uf3jw!085s!M@s*X? z>R6=Lb**5S+H|Qj&xhGcrKl(cl%xj%2&yV!jgTqnM9d4zyyUSrGrJw!&0jw& zOf#Ix>OKL@V*!bq+4y%H-8Th1y*e_l^^@i;+{@P+}lWr0tpz?#0*6IL_9 z_N#=j?d?v*tiX^(Z*OsNdw^UgyBYl3=TzrJXy(+L@p{Z{=Jj`FC!L23l^F% zuTCJa3HTq{w?hb#5;SA$JP*4e)sJ4EzCBYFcFlGi+CM*i{%ZY?%kb-OU;Oao9qiK2 z=KuBn^zqB*uNM9d$(|X+QIky zy^NpZ_cDHt?;kg(C3Na3qO%FQMrG>6LNkY406pD2L@*zgU~~wU(Lk!mQfH8X6@^P$ zI}0)=jFyQfXg>&&^uEdHRuY827UOS>{a0??JB+lmdCK$lG*WL*BkiBuDbl#0=P5-W z0E*5uBqbanb4Z5Afr8)l%A$$`Kglxozo@9!WrgLI zh)EbNn?$*Mh2z0lK||NbP%3C9#Rx<xnlLys-C3kl z43D`moi99*_CIxAN-=0I^YeTNSOje!lCSqLvpaJ`sQZrtX)e(_Gk=_DJBP&*L!Eb z&C)yh@XYMIiS?gV+34y+M_ORzUB0=f*L|Be!;iz=sIjGU`a#B)FS93JtZOSCJv)8% z-4hsW*^P}!174g$MfnGkiLx4941NA?hCUGgZXbl0-`!){BV zdDKz|c~`pd%iKW{(DTR-xJ4c2Io;I=4;Lv&G6%R=Qc?q{W8VwXm<$2c5ty+2WuFsC zH3}&4T3R5vds?9NSQd%7V#iOAdWCI(*p)egE6q#lZhlap1LtmVIyc}SI;5n|9zA>V z_=gt);)X{Vc*N=3mxo{}mj2zqz1Nrjt}fPJO`o?BE`nH&ECrWCD;oc+z2lDEZ_ zy!}kc+s~9dXP;bUK#nF5;>Nt@l_a(kXXuqWA)F9w7l75$2_Z@k(+Kqn%+w8IF|CL* zsui{!hn6dUtxCJyl(zGP1}jv2fBmMkyI!2(Vt(YiE77(A(9a9T>%XnUt%2kgym$*D zD^lu~&2j6uX2j)i)yOg*3J1fjjZ}$zW>Sortzxq_&+*uE)}UFBZ@CJwoQ^Xvhcyb& z=5NwhU`v8>WhJCj$6rO7C(1j1f$RCGDfhLlAnlS$jd;2+rUZmj<;vE;a7Z*^1hgJV zwQ*J**rU|_)hJU5#SP`!-#y$knPx!k`xiG`9ryA8GWIKJgMT7!y7`Aji*jMph;Qfw zkL>)~g_ZGTe*D<O!&}q!-clN&FzAFxrSY^t8~BJcGLZK+wI>g?V_?m zyQmlWu8+0}1(+DeG9t^34S<)+3M88mzJ<%VtRf0Cf?cfW4sd}d*8cLxJcvS9imJzRB+ zF0Schyq$Yk8-L!|r|WL*)0I}DO->f$t~yvRuSFhHzWb`mC`szdiq5=ja@QcL%W!MV^#MN#kd1pB8~qL@-T>-45LqlN;)x%wK`T`{hc|-OdqD93R}Tv^ zA~e_Idf#f!*ZtSdno zP?wE3%;r(itCsAu9na#&Og9D1)Verim*K+(*JwJc<#~JX!@H+PiLTQNS(YQWV?DkV z_Hqk8)f!cPeIftXETXX}L%{uy%M~tVWtM3xBlEU>ebXGID~xw+;DM)R@fqwb|BGdM zsgz&_4QarK^6+jR*~&n-*O#MT`af9x(%J+Rs;i_ae)9kc<<{omz9R)Iv4%mkW<(}} zgY8-qvmS?SK(b{8UTB%cjHI_Oo^Q)WZUU^_Zh=HKGUKtXYteD%!jNt9|7Ta3T~nn8 z(^nXgqW)u~N69~@&3ZS#D_5Agl@60&vX%_4k)@umvx$@zZc6#HjR0@=lQ9yOaWQtw zJ#eYYvT&%o;fe2@1mQ{KoTQEv8qLEEJv>G4BU|<}PweKgeWwG{R?hl_?zZDm&MzOkG{60TkEDr7j>+^9<`};#*{?$A9jCWAZAVk5`pSDyF+b zRZ;4#YDSF}StB}BD*HnVD{X|N&9mMGURC`xv-S;S*%H<5R^#fjz35hCMm< zjuzaxUF~j3&xW^^VY?c4lJ4fWPA~3#|8PL~^b0++&;$Yr z@f>aT?Y|K6%KHMe?^_4`rD@-jW4Gs%>fZ55b?^A3_OCIEk`t`(C_dBdUZ9?Uj<}N) ziBloS8%CjY;rmkMp@$zH$RekWzbpa}j1_JCak2cQBG%+M>3u(`E9?+opVY5U>fTSP z)ALDn?)aoScYIR!NX#R-7Nw&bCtPEEU~$ul%^cAu5+*b(N>8g%bI@&0{;&%5Q)?je%a=W35Qdg1sz zv7(D-ZtEx2kxO zkz=MwG^r~o+EvKyqRZq*DI{(Iy`&+%bV=nUV64K@uPMd5LKnDHo{tkJ#eW5A&YD{>U)TZ;q7@YSs&Of>qM}oZjqrQRZzx_B3DHE0x%{aV#6z9!YL+t zuMy=}1KqWy1WFmu{V-eh20W z62jU60)xPG;(K+NBvH7+(S;gzhs>--B?XB%_eqm8qkA(6 zyHJ*FA=Cq`zK0_!NB6f-KJ{M>^u8U@ zUDoOCWu4w$*4f_?B_c;14d%)^qq&nJc<%aWO#!r~Snfj7UU;PhP6d*>1=fdTa}*$! z8`fow+)lXah{8e=$6u}UKZzr{%SPS3Y}DP$M*BOWaZH$$Lt}$#GLp%V*3dN?gBOQc zwTjRZ2!aIXvjECL-Pjdriz*a`cFcr6yOsW9B*TNq{c5AXQb%-qEhhWZvEal;n(=2^ z*SLSi#f2m0|Ym2dLLvo~+0zYjd$ zYCYzua7hjp=p=sAq@=6yv2ZC|O4j_C)=XtqIFX^=KgfjS<$C&lnw7)J``M(vx&ayY z@Hdjumm}u95%#iH0M_bK35$&M3JL*=>JurC=fzMkdl~roffO3x#IcO4+A|1oY(`ZF zrrPVP%WL42H2E1NUBSCHw49 zm1#w>xO!v4(MsLGK%Q@Amp~e?WJ9=zmsTZs!L}8mC=1)@NezZVP9m(dt&Xr90t8)b zL!j=$kZX{U4{kmTr8>0u`y5*N@Ob}n%_Af6tfIah)tQZl^sUQ4*88%3z)s|OFpooz z63ymQ_pH`*46?0ovvkU8*X8_e@lV*C;8K@bFKLP{CPUu-FP>dq{PE?)`{#-OHv8v4 zz5DcR_Rb&9p8htRe)=i+`Ntpr>q&F$nk;YV@2CugejiQVu&akJ>`9BK#qYG3T@f;m8NWLZo}pny&QElYHW3a@sn2;#iYq^6@Be)jCe^Y5O$ z>-imjeERP6{n;O1J=*l+eAt~1K=%=EU;h>kyslB6KYIK6&9m39p6o!aEH<&V*p$r{ z^y^Yy4ZGSSLvPRL$lLQd>K~HjUIMX601gspm0*q&YOR46)_LtBZW>WrrI-cjMIcwRV4vf>8?CSVGOJ3eLqJlDy(0hqrbK1s4H#^-94WpchBdjf7pm-VFe_i zLJ7F?ArXMY+9SVK!yJ&L6{R_7tyZk?b^JnosHq_+ed>ZcF5&;_R+i% zRjz5^aJKieRX$ncaBYWMAZ;avDz*&vU?BHp|M5_U9ZH*5%cuIy!Qe<_KAP*o4+~G~ z$S8zJTbSa|8eAk7lPl|mTR7jWLH-#?pAMscfavdBhF|8%tVQxcF@_WA`K6~#(Skxb zDHNDjFe;k<*ZBon5>KOQEeKfK+s$L2yc|rfDR$ZK3oU&Lk9;@m>tiKV02Z^1tzs>VUBp3{e&LOOY#!flN|GA!b_DaT#oej zf9BrDLr8i5nf%koi8*3=b$xyhh0$lOJc7!;cv`s-I7I)g3i&&K{|cO(%9Xdp`OO>fuk4 znOe(M@D(WSPd#{|yyJLR;vw$C6s6;A_VnT{#ev07*84EtQW#h{vVX%EF^?cq$ScHU zipzvY7eb65g+W$1evOU;VAK-0N**C&?LsvZWYY6ddQR=Q(OOZ!3EjQ;q0(J#DCWz% z$o`i=WSNC^i0U?7rd*V6lqzz;faL+uh2AVOpUV;?+ZuIkYt(yNqu$#Z^>^Ask*EYM z9K{LQq!3|V^box&g8?icn?%Vc$?Kee7$4bZq06*L+z=L^sEW~}S>9HV9#MLq+p6V* zRZ_?Q6x$m1-qx`9wub!`6-$p)Mv*5J-`GPqo!B=hIcrd50UKA9F}ktJu~as*BZ3m^+Fq*^BoJfx^lU>(owA)+O7iox+W= z!n8PgyUv}NmFnM2Kir}UaJ-4bX=OsBvDb*3e*91=iHvpB#EolRYUx6{);1(@*F~64 zm`$di#LfLegy$&uWUdQavjbg6mX`4{-8bBP zAmZ*@P%d6o(sjf~OoVh2H!>>(TVH0#WG8i9R$;d3y+6-6ZF=eb;>bv(%8!#SwZVPN zBG|_)f;}pQxsy`h1C;Am@N@H34QPl^LioZ7qSyx|Q-PC#XECqxyoAI%PicgJ)FBLl zu>1wgVy%b#uiPxWeayn!$1M66zI>I_s|%ReQPr+N%|Zg=i%>dFqgPAoe(I$u46)Gt z@+Y9(C5iD8;dJ&U&R3w^qU9Gje|u)JKM-)MWw`rThP#ht^e^QUPDT42dh1X;!~4v8 zpo)Q`x-76y@g}FoFHeC@)3Q=SB16DWZ(o)aUL2>8#H>Vx^rxmlnEXvz#@>A(&X%Ck z-{)iE@AEP7_mJ%@Y4dP%C(KZ%O=txPl02dPC;}!AvcxOM2o>N#Gu|tbFhaq$K(`jF zq0U~2ddTv}#392F-?weJ$zS2eq|+?+`Iz|od`$fQACnY9X&+;N_=S3I5W*HtTTxtr z!6lL8#wC3~G_z4|MU66q-Z}74rQ|K+W|a_8t$j@BX%GJP%;IhzlTORn=VRjU^D*)F z!397!?v!o<5fFJ_Qb|r+)o~em=nN326c8Ik1+3$7;#V0j6ep@AHAQ3+1fT|1KPK=R zIMLsvWqcl%drDFM>gw1cl}#3F`ETpRZvV*hx3KC23QXFNz+%kl8m-QmRU2K<6jccK zsl`rLM^anq0CT^YPN-Y*|43yO)#X)l^n>5NJbm-9fnq-(F*8PZMe;=dr;+`8Arvjs znf=$pV+vg&aQUIiKHX~Bl`)$c{I~N z>;IaeZ&@h>y-=!KJ!eSym2}ea;8~;5DVYvap+$5?5{ubP^Id~-A{gqyRK#l_Djq6x zwHA`e1DdLT!@l!DVbzUlrJh}I+SRcXH(}v1hIciqM)lxkGG=mD@CpG*5MVdc7z=t^ za(j>)36ZV2OA)U;Ep#BkZd>YRDUlw}S#F2pPm-my<$TE#R^~Vn*{mhySi-M9-#S@< zTb&n23#~4zIlCnNhaTlXap^T0ug8P$k5bHoEhMA)G#gHz=zem7nt~Lh)eJNVDm$-o zpf8P{EZYkvpd{O=OZ{4~|2HBJdpr0@0UgOpDTYf!0NF-K91inoro_7nB7pM!+KkT1 zl+>JO!EZ)a&XfQLzFkIR z&=yssztEo<)8#GHOhWDec`8Ho{heM^b`wtg%i+|%D@E){&Sib&O)`~LyxPEryh}w% zt>^H$YClz19f@5%s7nfTWZ$Ekvb^&0hhY0$`&Wjq-u&7&Hv_nNM)t##ij)Pt7PHCT9t&X#w5YO`Guv>s< z?YRDKa*;X_u>8t{Vb#Y$u8EF`reJmA|5A*FbH|ltP`c=2=?BG$M)ouHV;q%gCFc@L zhAvr4x%i*eS{JN>jZwHyB0sMw&4n&Zjv;I*70rP{I#nSa^BJvmJ5Yw3RQKj?*77EN z0cA!pA%9)Lb65(I7Ok+XF{qZ4M8b(EB06!_B3UO1%_5t3!G zLjotsgy$E@SV+NEO$h9Jn(k#nJ9QY{BiH?ELX8P^Dn!LSO(^bZLjA!!Q4JM4={%2( zK{q%EGq0dA9S)BaCfO2YsLUxmkFJO`FVJC1(*$KtBs)`ByefCOAb=z>Xzk=4O~}{Q zq400ggto_U(jJMdNF_dG`yVEh+xXLpq-VH2qSHv&*&NYbQ`;Y|Ms&ZQ{9KKWa`gx= zgsIYEgU`j|;=ixNw_k%U>rm>q4L&Uluym3s(l3s=6v+TjmFO$~Nt-L%a4h?TGo?vb;}IrIBJjzFsw=lIGN5Y!WBS432&(ceD*hyrKzUE zT54ST#l;$lvo6lFI@JhyT$&zQS%r$|h%opYg zVFeRpi&_YHK@g}{I(_rpT#Z|!jDM@K;H-(~B@_iNtUt&NF>I+2<%tG;04%0g5{j%k z(JurT;lZdsnI4UVcXhKb{dO{{j)dP-X7U}A?{KHPk6UrA1xYYcOH>zlCz8S(dnCt> z?6c4VN3nKR9~4D7DQ1gvYmYct0#0mzMl76Uew};S0$6`E8I8;X^WU3$So!mcGaeqh zkx*7_M^3n8aUFNJOWusu(a2;++4xD^_;P!1&*}I)Zat^>r&5@NoFahDoD_B%rkRh7 z3CKvQU9lHK{ZhghUIQGEcXtE<=(-t_IP83sY?kZ^pd>9PYyDKm5^^K3Mfn66y0Xw z2Y|pQbD)|`;6$Zd8wUZY20rV-zV zFL^P{jAP&V^E%ZI{(acx+^d4NnCm9zp3w%UmzS&lz4-y36vcqH&1yUu-(F0vXHu{D zP*E)rFWF@DuNqpK-@(bFWHsunimxMWTdR=e&O1By^eI`zE{o4^rv$^yaMqFl;appv zdEw%=Jq{OjI%|9Eq(yRJ(hY5m!~@cZCPf!q@lV6jB&KhrKrz#nF-<|=Fyd??xob_& zt41HmVafZK%58(z$CMwx< zl-g+N6qQC<3W*%%EI!4=+W-D)a;9@UL#sqI92S4uj9#!8-uRLAwtQkcUmx$iEg#t(XZ82r zNSgvvARLJB1yzO=U3Ih>(!i-BH>#twaF_izFMu^9 zTBAEiw|!-uT`e}|b-(bHEvqWYLO=BCG{UR&vk0~WBACR@B5+vVFV;Ebz=XJkl6&v{ zb~|;rww}{_uYoT)f>i=L@8)Gx`yyyidi2eeVNho&B(Xl2YBchS9CeN&glirLrcMPW zm$>Q5UWX9Sy7#-+OJDxK}m%)Kob zmH5c(aC*wg;K3XFfqrT)@o?1zdT{pS(GO>@9}d2SqM+0qrKGn4E9xD!UG)#l$4iBX zq|51=ToyC|e7D6fdbo~fS-$W<=c8G}{PV74@E7M3KBG1*s$(&9KEcXHB zZ&RyRZHFAAO$BxFCp*s49N41M#eZ9W(I{eN>hlUIWMSMx@w^JbR2ZbhjAFecdf~>n z(%d#5=z$fPznJ9W2aEs3-EY3Xypk@v)^OGi!ru@W(geGu`HO=utJWzLA5>-O&F|*n z68BRW3TD%54tZ`pTXpN-RmF!fVq53HcVyjF)Jw$B;>p4P&)&Oi z$8lt7x^p~5RP{QYR5t+n{lcp1N)Uu3rU;Tnkg3X?u>A^z7>Gb30Fuxs-=TYQrakDj zdT^#^bY}G?`6TDt+sxzQ5gzb>K&3K;>aG$I?rv^ww#{r`|NnQBT;WG#RqeIEIUCmb z{)ge!KEVWFPEQ3l;xSLWeXx_@zY(OH?e(E+d9-zLxdnj2dB^rkL~V42R{h6_;@ z?@lIG4M3{HVq|8_3r9jFu@<%<68L}|A>?rlR`s%x9i@eMlL?GN_H z_M>jeiz^pul_U;)P`)aP6kt)w3s36avXsnYs+%++If)}M%%Y?&3XsaGAX!Pb@e{{w zr`ssB^&JD6x^0y~< z6Nlt(!q7i$Cd{R*4L4^VQ|VUafTxo206T=A0v=uhKj2ck02CZ(<^g07b?gDmAJu5| zRKq33mluVgo%8UwA_`r`dwe%hi0>u}{nKBt*Qkni1n>m#3rNoMl03W$tU~gtsx$<2 zg5eI5B!zZ?T_Q@!ts)Z-WE7V?y=8L_>z?E){~kplP|Wn`E~3!eDT%jvDfRnx@n zDcb7?O-7$SQMy)5FkQ&*_+)LC)5)$VpyFpF+W|iuxzj^&Sl{SFs>`B68ebdlCwDm? z;U2`%EqbWv?>Wy66+q3Ve{&07)M8Yr#gtwBMp7XD!PqaKDup9k>Z*K_yiPMp&Lwf) zyECy@WT`DT+_l&G{1&umx0Z&xmI*CKr1m6Y85K>xhJ zqDu1$rmlp7-z+KXgk2K;*r2RQ|Eq9ddUXtcr!Uy80W3B^d=?SOrY?IC2(L>UBlUkV zh{;<;4sXp1bQhgH8aYh)!;RU&9m)(-+_0SU;1)o55*1~T{6q-XS(LGBbnkkk&V1rc z=HMCTlLQR*n;^{o=>;-7_nmKKmWj92#yW*1*;cyxYhbm9(-dvHtYC?S7#7+>bnP&% zNFenhz=FdJY=O)vE2w62KhMG3PT-eIb4jde6#{+{>2mhG_(8f>r7P@^u4r57inf)m z{z!HOgl;Y&oD6<9C|-lK^qC_kOF=AjYcjXk$O;U;kSz-R{?E=FV{Ne)fBEzO7&MisbKJE%G{5tgKmRY|?aeM5@Z-|d&oyeL z-g2^7I610nla_4i4W%BC#iIAUoLosoruB_yT5_q_##O1;zaN&T!WFc7Wd{8GR^G#Z zAAQT~e-uye$mC9oue^{y$c8ql(H9fsnY+Kz7XD4Zp9j`CT{BYB3BB^1$rSOOfk5LY zH*Y$hb0hE#B|$3~E>2fA}MgYll+^HdEhM`IOq$ zCkqoZs|I8eXVO+ycY&C*Pw$>+N=6sf+)Ug%suGv`(Pkfg>m&(;w$Jbg|{&%x{0-#>r$ z4QHPIxtJCB{>Af?w?{8I2XAg8^4q}M*PMhIVaW6SdB65g zj^4gMcyaLNc#g^`Ad58f@Z(v_&t30_SOg9y=*_SjoZ%Ybm@z9`Mo99?=7+Y zF@^wu1w^JQAax8u5(0?KtCA9i$07qyC@(#@d2reW4p>uYPE@lMp#!2!R)+ZT!te`A ztgH2D$%DlH+!E_;kXWy`#Cp9Yb_*=M2nJM8^kUGD@W;kNe1q%6sZvOOsSK%uGITQX zh@id!87U_%C{IhY{V>K>3DYkwu}-o`!}%bw_b;)x14*Axh8SOxk3Pqcp8vKAA1&d* z&G6A3wvB17ZPknxNJtd6c<}Apx33?cP{w)^O4{@1n+E2EKd$hDqffs$k%Wn{af;)p zmaA_MU$2Fqo`1_e+)XQ{kf^j5qS4AfWRc3h&HZ~O??jc}=8CbF4u!AOR#-MTJBlMD zI=mTPt*cZQ`s6}<^$U!z2J~lLVcw4yqbb=3LrP4|P4Y_n_nhJNk%H4)lGM`K=u+nG zYjWcczxSfY=CkH4+rMWmf3>gw*5TNh4GeiYnxjR5rRfQ%)c^FK{Pdr&JtX;sC`B^{ zwmfrjD1ZO@<>R;SUM>&f1uVCthGr+T`8~5#1#nb$1$n2sG-yDIb7O{F<0=aX-ugmK z0Y?Hz-nlXv^4M|LGv*s(s5YE7nr!q@cuGiX%BJ(T8(bG_Vc_L{U~iu?cGdcbc*bD`?dE#=jm_pT!3N z+7r_L{B>()+Ydcn5DX(O?=HYesSm#=ka!z_9Ien&F zCPQ%=Bfv+L~DlR{ii?cGN=b^!n|e`=Dj z8Qf+um?0_dU-ooR?h~u%^uv;)TW^I?7?ht6e7;4AZSx3jO1<+<@m9_Nv zzXH(lCZY2C3YFhisQNplsHgGqI!Q?~^~q2sP*UUP6xcwP5DQQnP_a(zkPZQO8}&e3 zsPhCnze;eldTkRQehyN{9)#+yLUm6P<@J>)udhV)cN&p$7z5De)%Z69a+JwJP61Lc zNkOG@AG(Ery7(|Gd>=+~b}|R}Ht5&{Fdc;cBx!9^;$n6D2Z{RGfVVfvmD^XY+`e+P zMQLgTLj!Ctu1_j^jO^rookIs#<#;KB+y|N_15Jbth?IkX2#+u~lHi&HZ4EYi(b}mv zJ`*o|kgK1O1EM>j%Z>XgR&ig&D(#e625R!zJN}L{k^qgp`WG6y;H`tDq$ZKs~$qtSFwuwDpv6piWSVFWgvJ* zfI7lQlorAl6A_AVvI5DegD9(9kZPht7(|?+q#E0+ef|kApr5q2D`>4^|3RpJ7R9Pt zqWUUUabLwM_O<|R6N)=>8l1H9IQc>khGK{(;K0iPKLSUKDvAJTA*aeq2vR+ebSp0` zU`>r-c_>>FMb$spqYo1GGb&cya@AL{iu)>7vAcx{h|+}8o)$!P>#PJSm&x&xAQ7Sx zV6r?mEb0sMEXth>EUg%)f=iu|sP19mwB*W#T`i0s*;z+1KVTPLtRtjoZ)(O|=MT@!f%P3Y$ z(dY-os;5x(Rji`EidEEKu`03jiVR4yFob#uE3O7qFT>vn!DlL%@Q79cRw)Va8DxK; z24+=JlCX8x%w*+ z%@G>5G00hF-SxQz_ga6gmHHNHooBQm_08Y&J3zgpv3BB{q6L5_bnu*fhAF8cQWH)p z&1>yRyT1Hj`a#OQm77YU@XD&Dc?ytwT@kgX6z8}C(_UVtMdgDT0*tb3ExkrlTukPU zd7XLUN`n4G*@sHmrEFZ>r19F_0kQDiffRyMHeTASolBcBGn!nNGbXI7OU+D>#x*ey zXcP*IMkASti)uoe0DYBiLeMq3FtzS<>qasI%4>yurvM4e8LHfOZGMNPWBbdW|EK0# z2n449b!r|4P*y|5cU=TG#!>%d*jLJ`SdmQ&tQvs7_b-uNY@j<(H-Kzah6jcUk6Yh;j%0$2a(MakN8Vi z){c}qQ(EBr1+khcYOCEz1haA+`1dHEp%lclfEga-^QX#Zx>-E8+&+UXx6h#e_L%`r zl=yj^`x#j<9=vl&R7lhh(&q}7djgFGX;)BP!0!zIH&t6%Nhu6&&>-^f{lfj88 zn>QJvUrQdmebqW5>RruFzHDw6=BmA`$)2E~*0VAm+RDa}*y~K>!Svsik@bomMIZOj7bxl9~&~ek4R`I#kl4;M-zf zOeRy?li|Ya7RFa90@T`?TxzL(X_yPxZR|+B)r52N8Fr+WiQ$wp;;dTg_kvk8o*}Vb znXxlglvci&ym7r?K65Mt$%Y?8e*8FLEmVUK)y>q<*^%9Sg@N^>Sy>ZPgJIdKEs`_Y z;u9$c$z;^^Hv=$6g0}tCg^updzQPRElfll;p^oH}!S_m$tm6s{@)A;-8M^C>oo6;J z|A{uvv+~0XR`@SbCL?eOyRm!{hhc~YfT6EjjxoAJx=4G#s2aldo zW%EIni~wnX75tS!e^Z18y`&cE;!Ii31oQdMMW_WVxfwlM-gP>elxFeF3%l-MoX>aG z%|H^1JMfl?`cH>f$L7R%{9=A$^f1HMJ$M`Y1z#)0#kXPQF5S?P6F+ZieWCKEaL~F_ z^1ZhyF4XbF7IpUUQPd!E1&)Ln!Q~3FlKdKP36piX1h=-K>@*0te= zb#=z>W=>d{#C3|-I6E{gF6&o^QnEGH+9Ye#5PSOuGJrlv+MB(VZ0R#d;<$ApioqQWpWbeLr2g!o!$s+zj#2WqO8vQ zh=;eb4qG;oY7gPtqpy6Fg$>tj%AzX0{WOuHFws~{Ii9$7bsQ)SJH4cwRXUIj{-g*< z;zyQZO{j1yc$r4ZHEWA2>ub)ELSWv-O{|T-n0(qZH)`XC%hvK#d^%IA2;piFcvT%K z#?$c70$+MF6-1`d&CbqKW!p7Gvf}JEr*~m_Nl^;)U0>_#!5X9E)*($1^YGe|1&gjI zgVK-$oAId)3BtU`?=DU+A?-EaF&gHV$eV4zfIpd2cXXvS(Pji?o+`r$%!~ErhT)8@ zR3}^pRVlMF^E+QWTs-!*h<0G=u2CN@DPOBDwe>^Hk4%(v>5)_nGGKBrL%+a4nmIEA zaDuuvA7(k9&8QlI6UA~QF#+kUZFwrlRJJ^GflFC~1>X<%z8(%9jVH=rCQf!G6wv@f zR;b`L#2DpQ${BVov|u;m!oFi_B|uugV!~t-kid!_vR4b$#;Be48|{afRfB#vk**ib z0H7P4A|TA144f3Pa?W3DSi*$Y1pWsR)K5kmGDeI%XJL1*g;bfEOR0n3 zAH2G-W#=*{uq&erp3~8(HF0RtY;!ZbuM&2W(IgX!Hzo5hR3_;DdhL0X0sD(v#Y490T#cXCqqA$(X zR-)psQHj1Wo9E-%=DG7tGkX2*-;-NcYCI3vK+Y<-o1OJqqQ2x?;!X=lV$1sBx;y$R zh%24!r5&z3wPko__4!SK8GCAV1CaUgzH_V689V*LG6&OXB_*-9L}r?|cIc;ZC!fQH z)A+qPjn^xxw?5ZfT}2(mpNO-ubZSt_KqvNKRRh4i0(&^~BdYSEkfN!WI1L%&rC+9a zzd`jUBu`R#3l=JkoQO2@`%b^~mQi++I1D{N)*_tqiPYaDbyNa;n~*x1J4N2t&uD-A z9;_Yv%i+QC(VK%^!!ELTVdj#2d35mR=^xCcXGfAoaI(8er}_o?zmd|QlUD#@hE7tz z&C(Wh(gNQ4Tz|4SpETf%AYcS+HmLn+OzjqRqba%OWJ%?z6Gc(Zzhanr5fAuyQ-NYi z;d&%uexx~^`LJ#wn=1W1W!Il+1WYOg7$>a*1+lF9Z_z*5+v63(+@hIfj(j$@G z1rVIM*H+a0Pnr618x$97f-E>9v|U4eJhhs?m|s>671zQH61YLBTEqn2`7eW6l*h() ze13bD-8>!}DnJR-sIe$NPp$<&=PJ8+BKF#c$J0?&U2xYyYakUSY2eZG)RjTbNWC;h6_(I@oPKC}-@uDV| z+~<0ay?!)xmt&wx!DpH;&v?syB;UXv_0@3O(I zL$qo$& z+RqKne5=7U%+7U%tL-9Ge?2gmZ+W&8f%&)`ypj6_rDuwSuK?B9_SRt4n4z8B(QL58 z$zyZnpY4DD+dt#5E$|=wH$ORl^KUoNE0zy*M6!!Zv#L z&ySmXS1hnZFhw^SV1CjE|2gTzCiS~0IM(gA@BJ?RjgGtf=V*|Y;RT|LrVdnSZc+je zK&`g#R}oOp&>DM!0$q~9k58R2Kv={i4^fgNbiwIzW8|;HK+D z;I4XxM9cT)zp!t9p3lp_{G;#v<3L2|U;eS_*Fulhj?_OszC}|`C_d;i==^xAH4<;N zM*8!@5kPWc2(qbu3PDGA5phkJD!PZ342wK_*r$*rpX5f)4aM(-T z-A~sAt1Anqyp-ShoK-QOP&A^|FOE944QhU!9h{X(&@zRE+>`2k3E}=3|Q@dZ?>KPK3G_P zGOuG>u%)o=Qw`k`cBEB_hwfU8_g!%}*l^-LuO}4w);b@an7&|2k8V_yPM&>d8U}@Z33k2Xtn8_2C@yoDiVKVzww#GY!By|1p{4$v(dFV z2fO;VYS+4C#?y0wFdwVyVyeMo35o6xj^>FUQdc*fk+Yh<%R67m9%4eYW;UffP6({# zQCt0bJT$`sCsFnhJ9x&HC8Kjio}c9WY4u*EcYkXBX7$KRj;)96^?VzDGI*s40%z5N zcxc|@u#lVtWCH=q)Y)~pvb$+&rY>=vo{z(+E*^_GtkB7%V)9;7mv+0-^1Cl^u(6&;pJyH#fF4WFPmyU)|`9)2#2kL zs5Gh_pks+xgPTwa%!>;>j00U|=>fm7P8EIn;@#2PSFhfF3*qC-mq&+hdlriuW%Kom zgFie)wcLIE3SD!Nr{&~!P1O{rELE#C$&;(P%`ZvM{4&*8VP}{x>Aen;-s_;hTMs}N zfj|JnrA#>nV-k6NVs-(9Sg_iIIt4sJNMJCf>?%?`VxOw3F!O>e5A&$SsT;aU7=`y8 z#dDK~b@2DH4uZ~g5cggOaqo4|-*s1$n=T}}6l(_{`W0SY3yUsU>J{E>pFp?klVA}$ zF>;s{9*IF=Mx{0x7c7yg#r_+Fq2t~AI&jj5b?`U24th8&rs!tZW4`{gIwu!=7Og9* zP$%AImM_B^z0Pg%8T(0FT=rdjFH|-Z)2Y#^Weoo?P%aXEzb3V6>M)^A$r+d8!Mc{R zH*30FPB6eEtM<|s-K;t`THiz^{aUgw4OO2=HvKEv8YJc{mXdaC*8Rnx_FJ*uB$$3? zNP7m;@nrI&36_7#KG-V35u@<0^9C@ers~De)Z60fT5`(8Fd9h8yA+`rgcM^PN*ksk zOrp2sw+pwG<_znXEQH;^ohWgY<`6G+Qq0fE#rz>tcXcjhV)9m7$Edy|9Z;znu+e?V z(YK|Vrod!o-)s4zfmIQUSt`uo5d0MuDV%A6m4?Rg=h)4jf~_o?VnoH?>2mfnWu&B zsu)7cA%%UKa4p3Q=ILtuaY!1CCX~-)ao-S5Yek+~$R|P@#>04>p3Iyy<_(Q6z)rTQ zNW7Z+3dHC=C86Y;hSEd?6pwbpI*yvYK=dtnkob|o|9dZ+|p zHWg!5lRL}NcxH1n^u1DQ%ue=kcxuSfO|t&*vY`Bf)oGne6lu;xe!iA1lFwyzdM)d~ zjMiA@NL(bc#0JF;{jzfm%8yX`OfI!Zm4cH~*pQH*ZmV6w`&VLUuZ88&c{{{g@L~5b zWAWhYv-Q&lr}EeCiKz&-Bt2UB(uLhKZ)NiG(Sn^K6N>o7_KH!Um3N1LNOw~0}N#8gq4S-;4P<2d#EaY8npGIWRqSL->w(=E$-(U3^>{dKr%&z_$gJbiKW{=1_;eDm($&9ggVs;$+FRD*f{6s-%T>-e<VviD`Y5)E8JE0Ced~mTeX=0Hz#!BJKKpB%+uw|gXIQZ*d7n0%d7eHi?^D>aa$WZG=$AIWY~U3Ga!mb zBZxNGZ3g|QlFag{B`4M9G|6C1Bl|ngrD&S4aJzMdR^4rwTFJ_&Bu;DduBXP*Gezzi zKyE*NQmI2>x@YZL{T0Kg8ei{kUD-No)mS;tkB>S`oDDa&kV@M^HA{QG)f8%d>Ejj2 z=n?X~rH$@9?`lj;{77(+o{=rElINXGKm1lul1jO_uQ0C5Y2NEhyNii945p9IrSYx+ zXDCqC0EpjW#kFqfu;b>9tvyngF0G!b{r7|S($a<_cgvRg!@ShFgK`?yX_`5?m!qPF zFuu7ZWn1Lwgw%P34jJ8*?*}!dj)g;YEgtd!5Xn4nmkE|QwxeVfJ<{ED9*4KVyu>Sc zt!V{lV46cNR;I$_6OnA2)=;-rnM;Ig&3U}t2;TLm>wCH&?EAQZCLi}lpQih@9((2t zS$AdmqyRYAb-KauT&cm@^VR}jkG?-?p9ShR`c$d&==&;XE0pw~!6R<{s_orf*SCF2 zwAVnb%)ErA!fAhhf4Mw~AINiBc%jm>H+XJ%(WFYo+_uoe{lUTO?*<}Txa_7=v)2o( z^>=x5H~CXj1@M!9k)<{rDx0`wyYPrbpPuEm&DgR9E5WoNuG#%wyzA#5bu=C`R&(0* z1-ykh#rnV|dw{Ps7LX9@Pn&gmo;frH_wA}l)v~1E4Uc*G_)3#%bg<4f;Rv`BQj$hZ zI+rH#nCdI3KU2nZ%}~N`*}TkVdg*A3Csemo!A={zF<#NB1^=*Zimwt6Z6#h%MIsxE zceUKtnUg{(Ern92w_t#r%N(kceOS-lC6I)}>8!y;H}X^K4PA1gTg&fe)WRXNi^dW_XDh0R>WZa!__H$N?=QqCuEvB#QjHJr}b=^2h?>3&9X)moud2@qsxq;VBSO2bdW(pO0s%H3Qtz3yHn}`)c9`j7{@6G8 zg0fmzk4&~La6@=%iB4Gg85*srDrb;y6Ln-8L9$mxJYM6S-N!AhJ4cOp|{ zc9T4gv%1bei;>0w*>XT!K@dMF@}NTIX_cP{CaHi5^fS~vzD3Cp1Udo@nnq?yFNcy8 zr|m8kH!cn2;utSoka$Nq@0BzyzWfFGX)yjv#JdnQmR;0NliEr2rTV<1D`mNp7Ms6W zh(PGTl{24ovq&2>K(gYR;KYT@&dzXbkoRV9+#fte{md0wsG{QYA7qXs4kI9vO{OS` zBq9-64hD~oIT^HOdUh;w&FqtwTemou(`NIW)hCkROIu4T?xYe>e^PNjW$O4}9M?b0 zFd-!+jm*czVYaEF5*yM0%Z11_UD3faIlQL&RX%G>HRTRZ=!U0=Jti+Pv-p|iL@Qw5 zzkfO#k|zeDaB@u!>!ay4ryINWzkX$BLd0ZeY53VJ1@CYyy;2xV6FZvJtHN0VNfo2O^3Eq9ecl4ZGhOI%h^{&Z<|6q&`1&xvvf`XvBa z`#%bx=6(T$8X8Db1}<^XGnrW#b2b~>HQi>%lLmBmX`9s@b~*$4qUUG{^5DhjnjqWk zE9X-*e~T?e*YKVlG9`<=^5V&+=+Eo)xRD?#NO-zBa2&}wKBj^6!RcFea{ zG3#&-EIzPx2y{U;?PcRH+`nZ~^Wu{mQ<{e#&O{!c%H3^O0L%5aX5lWn0hWz-2Vg4T zQ*VRBQhVfftA5_v`g}Fh%38C2of-7rm_h5U_wEP!N1L$A;dgMN+Rwc-D5|m+sw=;Y zys)aE{i`5*s7P{)>mq<(8>e(a3R^0$S6&`21F}Fwm9&XPC92kGqKiAc+eDvRoC_v; zMy@p!aFs{xD@d$0-h?Pl5*+8m!2-AJF0EU@S6SmTzonXPJ!bxP4rw>p;E-Pbf?H}g zooJ)5McWEnw5_m3TL~MvkPzMY9^~3?4zqGXbS6yj`ot-j@G;Jp3h*>9DQXClQ>Hm6 zQ#T}b6IYZ)C(99<5aC=AwoP@M-p2Th^?oGxFKmCCk`->0tZ-Y&3b&Q4a4X4j35Uat zoa1gxyvh&ByDifgG8%E{<*sn7P(?)aCm{v~VUU!UJ-yKUFz z(OwQD)Zo%yTn=Q3PJ5x6?m`uBwfMxMBgl9!_5PjNlXGN$`<%fg66*{l@${@AqiC$O zLO21)uiMikQ-v0OGN^NJm;d7r9#QQ%5sw6P1$ZZ@_!IQq@!qpH$NY`CssHCQMTaGk zuFEU3I4{avke~D6;+n;2;3!4tBjz$hFYPZ-t!>N>C4m{&zYDQEY9+K?4zJX`vDMJ)d4qn>SQvQ4@ zQhR7TMz_CRMnvRXRO^nr-&kKquG}%kJB1;p)>Ah4>C4Kj zrQNwtPKYok+Bl6)PR9z|w!WFKN7ZLB@KhFOIpv1}@g)wN^$$Ir8Fyf@7gm(SMQi>g z;R#^>%LOB_W55*8Y-lKe8uQUi*PeAN2@Lo}CiEn_ZAjB5WHCJ()>rGBofVnOcUjyr zgHS)Y23^fsu|G;V_4)>W4{ZAYry%VZ_lt#_=SixX8{zno#Af;Tp13)bV?~`jx{8b~ zz1YP1H=WqvySTTNv!^l~JOHtsS+sZA4M)QZ6aF^&-!n|U`O(8Rcvb9gmb6R@xo6nk zCgNj6mhm_1P5gfBAoC1`4hfKI{5@tzXQc!D{jnh3H~8-z-#@gN0k%1Hk6O>XPMIL+ zH#|QI>mUGu>RPm8Z2U3p)*suw_-@JBNBULfq};Wm*Wr0sytTLI!JPN!kLPDT=iKAw z%-`sQ*!<|@z0M4SgHiQiN*Tf-f-_$ADiDsSJ|o({J(WM@6wcp;p! zg4xd@0hZiyw~pchg1@=~%sU{%4=ZvxgAb6PBu&zLO&PGd;({VirY_mKaA8JXg)fB+ zKB~W6#S2&1yLHSkcd4E`K6r_4y{hE*uBz)`{p&fCUnobngm5 zbrs&jx(c^gS4mR)Vjb6#3!B$joK#@ULFHOkPzmQ%QU@hq200lxz$uGkrHJbaID5Yc zquOm_gTvXBtmc&ev{W4GwcUqx_4m52g4@?sa1ZM$xQBHWY{Lvu7EuV9LtRH{8l+^h z1*IDyso33VPI*R+J3UBi2+wM&tK%Rq$a?e1QIE+gD}J7J<-`x`>hEA(-Qq{P+W&EQ z_2adqA?}Y9(Cocuy=Ln!wR~j0VtjVO*!hZbm#+1ZX@diA1zVl;mtA#u8B1oaHFk_cYgLmCs^# zS#H)zD-Qk-JFje|G1i*+15A>Fc5lw2IkmCr^JcsVpU(zC>$Sh3eG5f?NL>HQUbQ`H zxSCc%$%lt~PyetNbfuS@R=OaumM`{=7T0^DYC^TF`9`l-#JKg$mQmxCH?9VZ|0t_L z63yFY-E5iMYS+1>Sl-Ip8TaB(dRblVzO}qtx3VbN_pT@Y=z>C7y8*oSy6{lCZto9@ z0`Lz93h|60<%sdq+T)-E0VOP@3>Y9+(2Rp9EXq7~*l<#ktUZr-h)2a$9o4M@#w2x9 z(G8cwF&lZ={o4Rr!!}@R23HORUrCYj4@WAKiAI97jP;p<;^nP56&Hdpb99nB@M>&_ zgyVFUuSyVVoB5BnoBybPERJ&TBu>hrbc+n^_mUVFLQsP14tOsLP~`fKm*<%CVeHhU z3&B?4JE4?+yQPz~=byIR@V@82r4{`R&3`97Rk+>!huh8n7I@9z3OTOO_IkiEmwu9f zXzr(F9#K;f=YQw@ z_I{FyzYG;8$+Ez&^8ypviyUCyGCz${e(>FzlJg2bG;nZ{CubtLC{ehK(Lm{&6QwJR zkp54iY%r^IVTa4;?8#e$?K!T=a{>cfX@?B)>!#q z&%gP@dj+&_fahEyTzDq`(SNL@nmSGmUSpri+=mFIkN95+U#1aRRlRSv!7ZS@x2e==>^zJN}-kk;0 z>rZi0y8zk%1eF(2lo6TpGf*)}AEvw(8=wFXiWr5L)G;JcRNV%CLauQV)TmxT=(%md zjKbtWFu#akqLAK?K)QDpNcYYHxdlL3kTb7dL~e2D)NthbNq|V^S&mp%RD2_PB^WV9 zR0;-D9^_tD;E6~$;mLl_)*Rr@gFt>Ef!xkQcDu2H@2phDcUCIn{z_#jwlT3Il8@sE z>gL*uy%_kSH1fgO_T3nY=QJTqgHTog$0H0S*k0TZc2H7Q%V5GO+vk0s-f_Da<3Xvs z53#(nQW@V_sf_z8m3f%r&q+z8$6J%t?0zl~jj%G~u>q=%+;yrr*#ANYn0FU!$A}W4 z#4D-rK&33|wphaF>pY0%FG;EF6wEs-mGPaG%6JQcg>qLi{_5hWdQ z6ybL!$eo0^4>PPHV(zsQ#X(!Xyb{6R49rc`zc425bZ-mBAe8YJ5XwgC?o*B1Co_;)@T{Qf6HT;TK1oQhKE zs>+}fPjavxDv5uWVc?}MwfUs!FuyLY2m<1sTa=k2z~ltvi{)Z`;QW2?@{;?O0@wLv zo(#7~=3yxvo&p7VK0UW=9L(v(JSM;XdMg_5h#zo4Wn1Ox3A?a+k&4N<9fr3RWW=kb zR19!6UTJH`8qSat648{>cRYA;d}3jO+9CMYO#ok^7JuJv%iv*Yh1|e&WN@q;pB9r* zk&7f1(v;CHzIp*HP%;9RdSwufB(E~B7C@1jBunXT)AB01?(y@t>yrTH(f^L9|C$^6 zM*a__ha#hc=GnfSp~dNNFCUI&Y{=61 z*W$eVm-o}X&(viW9~6=JY$9Nj?+uREdlroM@803tXYaoC;@_t)^cFKX|8jjf>Vo1W z5z5)W4YSAX2iaqr>R^R%Ht|BNU&imm->CI{122rbK`+>?zkl`SJBxddUbHZ?RDW{yg8usY+gC4- zY=7nu_#K4}-E#4zY8jHd5ZnEfM(>711BZQA&THy;+pkR?+J0&BQ16u)QBN0E2~!_P2a7d4iJS*O~+L6}mm-i#?w(&17)C|y zdU-%rN}iVRw_+@Zd3$AY1mEYG846I;LK)V>%6wRvKOJLB25AZ%#t&IB$)`FvQX@4t zMb}(x*`aQ$kaX1!h1w`uYlr^F|DhXy`Sbr!L{&kXJrwrJRZy)riYG0guBH~sl9w3l zmKn0^BL266|6pWLayR~9lE?+-cDR}`ZaT5o?t;azUU;W6s2Wzz98?|wqH4b|Gg!TX zBUCl-6DV&1b`o-%VZ#%k1fA9Cr9njE-;)OPOXx`jw^}f2Yzt7ri1{^~3Ky*D?&bAZ zaa$H4ybS_Bo!OQ5OcoHvlqqLY8C-V3>K5!RDT17Ro7m+g+tSr=YJg|?EXA}1u2x^T z@c2l9cI0uzvj#TYfN$>`oZh?|SG9nGQyi&dsO65$ixx(Vf=GpN#$O;@t(K$Jvgruk z((pp?kQopKQZH_}_xZV$2UJrF2R?(`(OKsDNhTK=E#9Xu0pM)#7DV|gBLfzH{j0m7 z*_X=xlvGHfyVg>EC*0OMAcl^d=+!M0j+5r#G*l=klyL34XhjfILY-)^$pXBpXr;Njl8x5Ew|ml-LZ1KGC{5pqMVm!3T(zgT#*G2kv&pK_8a7XjujEsn{$X zuL>ZegwPd7vBx(gs7=u1)JX>`&YF?B&)ln@ic1m3of4C5EiuW~64ReYi8QOsi2}i9 zf&DX%3XjCTB*~E;KLoNFcYIKV83A3NG{DHIaO4yqJ5-71)PAy@R^So9Ro(oaB_{Ya zNKDizG4a+C6K^dsTU20!j#WFrCc~yhQBneNY^nvh1!jbsu;Uap^_h~bQr2NoGhJB_ z)-loHI4|q0B{A$U-aYRzemlGQ??PfW>*2zyb1H=MXG2cMYX3YN*?7>pD=ls=@R7X5 zeR;JmGe$>Wr-2{uAMC%_f4$#UxSQY6i;rJFLnjtZtgSh(5GE-Khvf6ahW|5-hj5gmNBzlH(I&^jm>qad>049+h~%adbQ^nmz4 zp!74S@TZjpn}uja$SD*~bZiyy8T;2KRcU1bLCpw`PQpE7Ke5&RRt@|or&yX+66j0PC#>bP3VF^JIh?zo4p;TWts_@I5 z`vdA~7w4q8U5Ga3q?#fsmc(Rd#}W;p*Zt;731sxL5`zd53Sf_FX^7>qEFAiiv1@jh z2TR&hc8*M+H+Ivj@&<-A`ts>)C@NsOC+pSX z8l0GSJ|28!Y##H`b6z)CJSPTW)6o8iv@_0PnQh~d13rbQ!&vo#}wWS~2h>}R;Z zDYwH5Cl(777}EUu+|GdQf?~iZ`-og)sINqHuCh}sp?Y2p{gp_IZAIXRhss@Nnw!Cz zT7+ML!+BKgW5|A^oGP^UE%qIs{{509%g3k*XJ(T=@!HeM;-u=2fSV4-Kh982+e-#g zR(v#-u9yYJB$`ocQ9e#rObtTonS7fP6sf%EcN*9m^BMbtot>{6cSLio0ai!gbjtY(YxHFzfVE;9UsZ;~PDA2Pb+~wqi0f?Xr}&$Vy88qk|y#On0rjywtqq3e+Z#n_01xMz{|Y z^j5B&%=o2bY6NTGk?h5~oD>fFPuC)qnM$FIuGwqN47|F&y12e-I%oyVP#oHQQaiRV zkGn6};P-1V(Z6OY;Jmx~C}Q4-vBf=r1yki^w^_RYJF^ApJI!;7J7ASF)^7Hme%Og3_?MgGBDEyK3O)ShMNfe#YliK5`*!sOZ3|CCdw37 z_C|wWr38F%o0e0M(kp$M&Fav+SU-WS8Cdu^aT0%CDeA+O(6BL++P03`f;&`uaF|`@ z{M)cLz5oPC4#KJ3wb`@_y{-#j)La!(AG1G;*A2W9m7SqTbydxN*d16$PeYO^BZN2T zqc5YPzM$p~!}~OyGi@#{OZhIVeDu{gcJ2tOyhQ4>fKLJ4C_w*%V?9e=>tT8amZv^_ zfBb3qVK{w!HaV|ug=SuPxECt(>MQ#>xw@avN0a>Vw|LIe;CGY znp3GAQWFbsWrcsLa;RG{ZYl!UVoFwkUz8MtEoX${lK}N1-U`REN9BUE?3AkMZUSCV zF2FrljG$WjWtHaMh}OC(`|0e^YlY9< zHcffGIJsL1pSwjJ7v6~)%2z*;>H;rt1Jclw8ai-*qC6t8@IQqhMLD(O*AA{O+@dgZ zCjsC9Wh*HAMTF09v-dm*UvJ^-%Bu^Ut%T3nO8B%6s8h8aVaWKAa=0TWTlf#q#pG{4I4GgI)g zI^~}u?UZr0ygE82Vm$N2v>3C+z8hZcF2;g)mF3qG-Iz}06<(&{#JHkt@Oigkj6u4v zgBqhp&6Uycd??+*^3e~IUh@6T*T_?I66ojRixS&J80Lw%ODukgH+mJE_uzK4x-Su$ zHqzeE*OM7u>@{NF*5Lcbo;#b|7=<=Zb*bW{XYa?i@w2XI;+Lk9K)rSUP3W}$`SH?E z>oVH(J?o2)H^S4e+dAII8`0?3Khj@k52GM10Soca#G@F_*4zzZ5GyItg3JNzIT|Ie zjhG>cV?kc#g%*2(`0j*7w5&F{Y+cX~t|YUiKQ%76OqP;Uc1lc=*iIsa(P8R?3Y9xm zB6;lebj{J#Jyu`(%#ip}c9xa$#_VdIE;avc^=yb2X)IRH26K7!Y&g#jrM2Z_fuA6u zI=!xp%azTL6_uN9)^M7OD_?h~OuagL)>||EG{ikqS5>)iTnyRtTir)HyO3lly=2Z4 z?T88TSn4o_ZmRCRV%8mHULQ%GlsE{_ByC9g^aD`|5{E7UEPsvPXTouFK~N>bkIUA1 zzS!^O4_fFuTkyEDx8s)E5x>W7^NpIkBu>%!2O4)6eYm{Z6W5w@rcd`5{lbKs4$4y7 zoNDXRu(I0C-QYt_bKw)u;pCH3KwVOD%nD7rxh~i;##462d)6vk8vF91CB+2p8)9_5l-b_y?qf*avX=47V zxKnRhoMKOuuf8aFpSX?r!ya|^T-2YM6tSJ{p<17hiS$aaujBk%z25f-Bz5nACBczp zS7DFrtkyH#JYqR|*g$UWb~4@OV)FZVn7aH+n@UjU8^QZc^`?ur^@qan3Exv#oN*9Q zWDJZlIZqMAh{2$A2Mm*w)|N{C8l~RTdQo=3oN>W z{~AaY_OfZD4-sM+5rvi5^jIP>-&dC;y^iW#bH=^7W}Jm{V^WYe&Q?}lYxvf@rO;TY znkdgi{U>brjBhR{gkjL=1q)+eW5as4ghNKsD5q)75MNWsfw6f?ku>Ft8~VpG<#0YILZ1^m;shIQwDY;g8gY3mK#YZVxPl z>y?C)tl=d6+%@Nt4a7^_q~bdTb6d=w(=m;b;4zUPXU#!1)Dh5wQqE4(kVMd`jQGp^ z>m}hpzB7}fZEQJk&qUjAzWL?w^z3R+e|s`0uQj4-;`!{0Q+pVX^`;L)B@W%Yn0%51 zUrAHY@RsyauGt)I^9@tm{Wh{9t*}>CHCD| zf8~Mc1(B1T`NxU9JRGybGxS%(k*HgSDV4+RY*>kXE!og;Z|-qxsSL8sXMb&H($3AU zi9KFVuaydibxf4Ad3(d%$%#6e$%Sp_`!iwtoTb{>i))*^lo^(`#>7`IRbyi9h?L9} z9@`sZvM;<{Qoh9)9+^HEsomL`Gg806aJ!aJpJ)W*QzFA>lHV;GY&Mqc>!l#xgVvruJ3RDOJ~OaI=Xg@82=D_FDImZ!!sNt<@#g z+|XN@$j;xeJ06?e@v+^{`e+nu9^5kNdud!*4?gPe^Y_= zhGXZwzp2K0^Mn1tQi=e8_Xxx7OCm5%feQ{n06d!E z%{&AnOTpF2Yw&Yi0i^_|G6ZEm@j%-r!?{Wv0yd-!W>ST{C=EUd*lBu?($H4Lf2q=N zd*<}{=zL#ujOHoRv!7b_M<}_{ZnD`A7p}KIw6$gX<498$HPSE=K$^?p=Oz{NoNNgT zVskh-mw-l-jh0=BhE5tD3Kmwz)mQs%U)<6c?7N=tu6W~?+gf@x$+tEWRIQkW#71F< zSKrL!Le%wheQFN@n$zq^>7r?EXE|Y2b1OSO<=yiolgl`hrXP5XB!L;`9tn?_lD(lQ z{(Be__|Y^sQB}{}k9Y^Mt7o)C;2dNzN0gU{}>=>C=hL$kuda+-p*Bd2xZaa&kQ?B_kTwCT5B}F;N|NT&Z#O1JgCnWRx*I zS2D`;o59H|Ot(9PhL+RCaJOZr-7H3ZGQ%zr%AD~>>)aobiMv0XJhn^i77ZAY-V>u~ z+T7nm`E5PZ=}tR-aja_jN}-~xBW>X7x*Sf{&^Pv|RGf99t57ij?%+1kM_I z^+%ny(3u%A+cDnjj?tYIv3z5HR5*};!pu)lXiG1E`@2Sgp&Bsx(E{Q)6zkLu2O;p^%Z@uzCZYuV`mH>7aSzYkGOiHlM+NB3kcMP_NkLp1fcy-@ zLzE}z=E(-noun<;oCb0eG*zKNN;rIV@aE)b<>#-Tzg+$CHmHTCFWw!!ef8??x3v25 z<OH0WXe{wW=& z-^Z_C9C4a2(cSF=@B9AiccAy49KJa^dih@7^78HO@1H+?vkc&{eA<062>uy&YJ;TE zq!H|YBz%*N_wB;^=f`uF)eSo7EeqA@yHK6J3$?!)5)}YSqw54%B1K(8OX2|V1)EbS zwZa7u{2+QkAOW~<4f_rVCOH6(1z4|ea#9euv{0oa5VWv|b^5SQ?|+@Hht~aBSE`rx zT&Z5#bESIyEB?XME!`~h{0N3Cs`%47O#m(R9Usggf?;@%Y8(hf4SRv(WMNvkaYj%q zOIfQ`8)%jD*~4~o9+v9EQoY}$x-N?N^RCjQ?$r7o&$Pa-^23{e|BM*jS4{opsuMZ?75i7R^itV%7LCbd^R_Vhkz4ul6#h0kt zxkTN*wyE3KHg)^krkNk2;pSMC0?b&iyY@_hE@X*rq>^ZMtEh_O(sjzP72mMNv&mh8OfALWvDa7eFo$ z6|e@w7?Kfyj3C;CU!*F)st&8ri3(`sAR`FyIYD`p6l-kL$az?&59{=A*Ea25sZL+p z)ah%RI{j@^7oJ-PM1g-w$#WnWorO7!%Yclz;6r%?vK>crIjJhhVzk~2yR`t1DIgKT zi=(z}8bp4uT7CJjR3Db=--KhW znv$P_UCe>68bHa4Gc?P~!VN-rFoRHz?H1L8wN0JqVU<3t(!Vv^G}({3muO$x)ah%R zZh9@Gw5N+AJD7zB+(Xc#j;qy8;|p|Ne7 zx?vio_r641tPgizqQA&>_+Uof|C;J+Mml}X$SoL-JSsy$KZYBngg`g*vJ%otLHsD7 zZa`7ZNm7U;*)qYDgwYa<5(Tw{-~pcJEDT#_q#s9)?>;Q4U-y#wnJm5yi^@%VT72Bt zZ&7h$f3%%C0_y|_5npmrQ@8dV=wE@%p+XP4kLo)o_sMLo@uwC!q@N{K<0KNKB()~g zZ?9Nalfud1KLINP|OlD-Qn>ARr%8)szQP>c;Y6&$G{6|d0n!Jte|b(;H8 zTvx&!#5u;I65mQGOscZ_YB-cVq1bI3XPy9eKWKix@&$FDtLgRvV3N21c|eB0f0&$& zKV8cw45uIFRln^ktHeP523T@MdKU-h+EjaOZaLTft#ZDelAV2=$<=mAuRLSnDXzgV zkOcQd7MvLds6td&z#mD|K4`$EEntuQ*5Lb`QFE#|NZAtE&i>BHL<(EEI+Ci?8M^lq z${bBkPToBkSSnn}Tfb^BfE2EHKFrhE1611yFw^vj{9{<(D94TFb!&CA7LHD(-q^C+ zDl?FIWY)%0Ui3t9N)>>bNxD!-2eQl!6#Mn~Vu8*Gm~{JB^KH5G)Zn$!=pS#S(I*rU zj4k5n*aD%?GW{36HgW( z`Dk6O7Ijo#sdq>TsKi4xAOS&m%M!G_IEGeb85cf%Ja9+ z59GZ2-K(c3yYF6eRv*poqc?BAef93Gx%c2ub%ymsciYkP`Thnbz11P=!_Aj|3Bteh zPK1A7g42fO9d$16Xxrr-ZM(dqEtYqfBvl4POOO%wsG+VYGLU4H3Mut4Qu90jQB3fO zv1fBanh~i4amLy&$qp)7=|3RR1Yr>0O zTOEDVVjIh=d6_An*MEzxxQbKR-A#(+S4-uai^e-JYgsXfp4+N;0z{d>15YvRrLG*v zDk&)!E|}pMEG3vF6v9hLM9v#NCc_hx;X5#O;uFCLlhStq%eqvOsZ46$1;2Ux!{o-| z0h|wu%gM9=xv{`1$F{QExe5)beWXfWNP?tXUVx59gC8azz=z5wmt*m3fa^HIK%NN1 zV{v5}a8Iw5#s;Sc{TH_2!SmmoH=s+VhyVFc*ORM%nvI9~Qye711D5f+D^tgAE|`J3 zv3FtnTTrL{K?5LW7^2R##61_(E@jREF{;#2{*V91jQV)q3xmC7u&5T_qLaRFUv*-r z%uXkK_$R=_CizGC)+ahu<;oNal=W|>LKZd}?eFg|iV*|*{kI2i2hZOQp1nFc+1dHa zpa0jue!o!C2yKobc9B_@O8>C}BxH&sPzaw&KKZ8VVWl1_6PN)w0AcG2S5$tZ@+D|#pU!}t27l=i**Fu#bA`G3%b)+Jfp^QU6l;tw z$Rt~FC@0!EMM(XiT+yTIbjG|Tk_e90uKllp6u2?~cSfqf11~;|45)#?6bRI50lV94 z;0k~z{SkNtykM0u_sBS%;Gxmal?w*q`>MK7d@hT=CXjCcyFGFlB9H$K_}}F7U;g~R zzcMfl6Ydu1P&)WYK2k8nwf7cHRZzVUU;|KAPMC@8!XlTB75(nofP>b%jYSYW1kWg#4Wn>36dzx|k8OZ?;R?#i=UctJP4$D2eLh{C|@eGa)_&1X-(qmv+ zbJlE04}`?(Y-mB}1PX0xrZlq)MB8eX#&hY5yhi5jxuNHq+Hd$;JEBm!^WIMlY_1{A z(%~8nFXr%rXVtlV`XyNE3iqnqO#=ZcE$zu!VFa=>rp>QM`m}n=MT-G+9_0^4$DJ6Rv2Xg?U(_4fZQ>v zYQ}k_VKJ>=b2yN*?h z6gL=6-AW6oFB@#9jnDPZ6Rj$UL5Rl#xa*NKlTv4q7pY%4K$gXEO*2qU5F`$&TcWdmBw5q z$!Meo+d5MW>gghKx3bt)`*(}G3vAlp+iG;tNf^5Bj=qZIhaciUjXpkJp41*XkFkiZajZC35jhc@n8Ix$ta5<)QGwM!ElQpwagHS*6o@4(iQxVDP#yAP9dORY1!0=FN`T3B$`+n& z3YTY;{K|x+qj2NSa^l`aPTaf5N&k8$2r?-n1SF>fWSF$qkT{&6sAs;)>#XF&U1aZT$A9}CnxKx5pJPW4Z9>IzRR%_-{she`yV@A z8G4cH)Kv&~YE@9c!(3N_A4-xm;^Ddc?y|Q~f&hXG2rq)DT^AuNz-anK>)2uE@lxl$ zCB^mszK)$uLUNa5C%((E6Zb!M>e#DD*TIa-Dl+s6vh_gk!I>ap5La;^*gmi;h>L;5 z`HIsBR7-F#3ozFGENUM+pr{AIuS`hpa_nr9le-){@m-Fcc#AUI+5@N_ACr^kv6q)s z1swxL7XjseNRJN^F*+@dgAB5PtS#bj#KYzR4^5@w@ zoDjQj2#I!s!rQyH&b>dLkiSsv9n3~{@A<;WJ~p$~BOSj-JKeDG`s`r->|pWPwzoh8 z^u3YL-$LYh7pVBno$X56hwnyM>NdI_JQSSO_!L3S;jFOdo!DW@B2|*CUDwKyU!5fn-0tCt&q@1UMi9 z0u4VYHn@H7R4g~k{d;+I@aE|s%%x{Xk_mURyJiONdQ#k$q~~qOep~l_UAb@lb69VY zVs}d^cDIz`{@O{JhHz~o4+X4Y&?pcD^K;?=aRx7xXtltvC#jF^?9@4+AU;eA$Ph5I z)S4t!(lT`j(R!6L`4iw%Um0{%u59?(Yv)UZAJq#{+4Doz~5O zouX*jmz}sOsGuUwM%=fi#tP~Wc3J$xwMzy;#h#q4(A}|1vpxIwE5a0<`VS)fSK5d- zNwK@76uVnWasQ1NjF~1N=TR^s#8HO8xLvC2RA)qKBup@Pyg$bEr0Oh6p0AeiAc>IrF`#C(ic!043(=bfL zLAA6IJ7I*8_#nl9a~ttyha$P?=acb-Qi-X2;>dUt+rO?rkGRgB=XJRfePKS8a=o># z#8KOoXfQ>p;`(ycvsdlO6P9?h?IdivIM91c2?qK4P2$dXd#PU8SHezHo?v{DnjKzg zkYwWsvc>l~&>($=nc^e)HM zRfoUy$OzGT_RN{smUu2Dr)j+#RTc@gMW_4V2R%OEWwQt0o7TJhrnfxU=_r+3_nuz< z$Nx7T{C)yH$utvx@UnX} zhI6)D%S=>@ES#`?>baXvJ$K`&H`^N*PCXn^L0$l220$6<+{9u%iU!;q?<-`VLHZL) znP(@QR@?&h^_HSxTb(TMU2~wW4pgp_!*7qCy?Y^bwLD3U^TC^AMdy0@{MGT%+c(b- z4OSOz9KCt{=J{HXuJ(L>0Sa@GNUsy6uD3+Gy(QA!qBO*jS#{hPoPQLX0{U;joKCFu7?dA#Zg=aE=d1Tjp|0ie;&6aGKQ5SxmStAGug`8e~`%gk;tu!OjhO0 z=`E4{3rQT<52FB^6>}iVLAwU&fh`iJH-t@5L_EL1b|`AD<-kB>0F?sUY`}Z!9PV^v=KTRTg)C^=7lj}-eH@?aip$A(R)`1g9s>UX80&jhlz;OIFTg4uH zQ%O;~lsrrL-qr$R5NL|O^nl{@iP!-O?zSr!eb<*60pDF}mq%!eQ;MS3AsMtjWZz#0 zUtszKVf^UAl!=eElA5lF@_$=zs2%uNQ6M^!!gMViV2BsG$g|_>Q#NY4I)6-b^6Lxf z`p(V?Z>lmW$(63CnIcuMn#u4HHT;nQ2K@IyncZkDe)RzpK&b1f3qBj&n2PY(w_NIg zny-N&d~#{4aLemz$wEPof7AZ7diWWmqF54KgOw=KcSeQ{b?33jd3mU1#<4eZK;E}K2EiZ88U-p}JrSkpi%z`Ayw-#rU z$+QyF#}srwqlnm+cFzGCPR5hTkIm1fW}55{1)5~m#K>uzLMLRQkRKu)I}=;bmY9DU zmV6@D|LvMWe@f)9t_(;6DGK}=n$m8q0>^OtuLCXDmTVF(KGn$|RG-B@96VO|1OD-- z869fzN7pPb`Idu^YzX@HIZA|n57`LVayvV(TbAP!DU!!;q1FA?6fu8T^K*Z2B=V#+ z(o)a7RX@4 z!sv_KxGWWW5-(zrIhsw_)(X}r0R1tF=g0NB!{rxLed=#uRPy32q|J-BkTx&wUu+6N zQi#vlkMr2~YeIrX}(iZkFt*XjW2x*Hd^hgKJlROfJJ&#aFN--WGzA`AQ7!cba z_Y$WJDiSb|J}?|&3h<(zMcP{Ul@HR^$BYsD_aADCwJ$FA1^h)^_2bEKYAc^RFSkq@ z&_mrl;nG-d(s&xH3f7x6AkHXU`AgI8vSssHJnNa&GIxy*{joBd*2Njx>Fyl1MjEl5 zTMl%b3`KdCNQD99nrQv9L`~G8|4D#!MEkv(To?Yy- zX7FV1tHJ)@8U2?b0D4PdlAaDvQNOvHKSb58E{0Q4%5|*h!HpR#wM|S(xP(Z|h%Yt& zui9LL?F;DaYSE!nY{ zNp=+Ya4ljl_r>~PGNwjItldK#v$K+{*&b`p`cO_`?mcbX}| z`kTzU{h_+aCvYDO9(_l*U77gMbn1l)D>wX_W9A9 zl_1!~#czS*2PLwaYy=bVq<-p_NLJV{l+{^?)v3I6o-2f^!VN?>ozHB6)hh`{Zc=+d ztOTeAj`(Il+^%y_wMoe*Ih1!qOpFXTmp0>#yHS(z zHfl1nlX6Luf$2Cv$;wdWL*RO2(xym5f&V6k^``ON-r`FSIwvW(!z2ZFn53Y8HoBWp zDFF-^HIkUrxY+$F4`BSE0K0-DS_`(p0)tyN_gr#Yyoe-7Ky?7EfxIaG@{{DXE41%t zlDdP!{v9UCzr!T;ua1rZTnK`KsBl?(W#Kt8_yy(CWd7mxh#lsPdIEmTD<7~nA`p=M z@=#t?zU!r}Ndl=MaqfGPR&0VTC+XJYpJ@TsCTE0>V+LmK%`5|G^-@b|CW9;4bK}i% z0JlqNJ~-4CTk3M-$P~~~PW6)i3Ox!Q*NkkJOIxdV_(spu*U!GT4%D{qe{J{o19n6= zSQ7!Ty;&$|+_MQtD;;Pm92>>hV2O@^=$u*|d{7QK9}xqzM2w+X6Ai>?4MZrKt9(S0 zdz5MFkZO}wAz#S_(u5>IBc0I>iA&zLC2*l*0lb}DmrY)fMj;jwh?e~Rf=FtRE6s;- z{Rux@sKPYwu)$Maa0~zC&;R$-0(oj1M>xQ_bYAE|FJJyU+FV^-&dWvh&97N78f^I}2&$KtO%INAkk%>9j8dw>qsowK(m^Bbs7Z>K?XHCR{ zx2l4EQyM*}^e< zi_vBtU3{R2q1Jw=59`{d$1kn$ucKTuH+M3wefsoi-?FZ$390{tf310JeezPwNiK2bUs#Er;{ z(+F!YrxdXEixmDfywfD^P{Zm06ctO7f7Zf&4FJCoe9pEDJN>EPukLa>zfXcrlt|x` zs*Zv>NvWd&+m3w8tnkV;E^G_L+2qHp*W-R}-rT}seD~zby+ySA?M2JqUbOrzM5|0f zH^BUfg3u!rhanTCL`OuX3OC|PO$CNW5Q0oF7=q#)$3k4@h!+`ISt~e_CQyQT&aYXt z{yK8yZ7)~e_Hxxfp&-H@7ECG1nN!ct!XP807Nl;R##&sLBornkB*j8GMQ}G|M!}!= z;X(&q*q0o^r7eqBdub5;YUQe) zvTw@QGAARepejL3uPFkcRzBgdEz`nvOYlx2$~)WK-w_BBiSuiguJy=N_b6Y^2KjQf zSGb(*6)q>(B1alh3^4Afq6FeBsdAD6%7koaa-E^R1f)0i$mmZqFBY&IQmHAcbZSWH zqM)SUeMR94D7cL8SH8%?4IdP)TSaSoh0EDq;d1&bT)@!~y~)DLD@d$Il>E|hsuG;v zBIO2V4D>v3TY~~D5Ws8PGND#Cj>ymhTQBxk6fS`D{OH##T7Mn6+Fs#uwpX~EEdXS) zGIBz|838HqsMjuC6!+B2IP}0lf^o*HVy~{rc%qboDsc8R=xF>fBIcCDX|bYkIixvx zzgoHa@(R~R>Dpf5a<*5voGpM|NYEq~EtT4!)FBhia}X$KpedXzE6`zSFDYoTB9GXw zD|n*I8Z{pHX|M;%dPU*Ff00JNX6gFE3Rk#6p%1tBaE04@xWa8bTvdSU3iP%#_Smej z8hs+>ZUlokKtgrqgzz4?==Fu~0U{Jrnp>6;{0LDM6;2RDD+-rS3UPYx^7Y{1x?QyV z?M2JqUbMFGa5<@;kyxHbn$nB&6C#BY<^+#;e_EAwnUyKIG$qHNM@4wX;a3Xu4-tQy zVyhmm5L+?%HH+3?N3Oi><;vS$uC`FPz?ICYesMy9i=<{dNTMvtVd`+IA}Gkxz!&Nl zq-&Qkkl2zkQIRL&gz%QvaUHi6E(g1EmDlB0BUfKq;kre--0h{y-Cnx-D_r36Q(@(1 zMJW_(lw?70Ps&)3)@4C$4B!C>8Q~w#2R^{TOHt)+hVMSl0Xr>(YoMiYVLJxNuUWe8 zQ{lS3y82Qu$N7TEysmoo(`rSvyNC|EHj~Ql$fTNdCQ%J9$#-cIl|~uKMv~lN`O{0y zTF_(+n3ONbRxsJznnw<__SMb)x{B)AC+r8&N+#7)O(`HGKfILmr2n72x9gEB%dW)U z`71&iXo{2~Bk%W(pseXmW+us0F-a;jSyah3i2Iot#mI>4_#i6_eh@tH_`&iRW61;C zfH7k~7zXCW@W_Ax1AdtK6V{v1f57t-yw=|5e#DKqkr8B8tGbI)cax0!b?!O)p0m&1 zYp(?qfx#<$J(&aK0qyJs@D#b?J|JH#&<;HI0~$ChkxJSQRc=d#j@=M6%?S`kVe{~M zaZR)k$`xSZ!yiNlZUh*^TyavYT^<|gPRT?MF0D^1X?y2GcK?PJ~mA6L6v(A}gjb{O8HSkMOY!f+E^ z$I?^>Ik!!09V_Qm@p`@OeGzLpgP=%FOETq4a-+KY1jGUJQQ_VP{*(n$y7yXTkIh63)Q07_N_I` z*D?>>@t^Zw2QR=I_v-B6+3^lO$R^^yFxX)Bw0?c4*3Xr1B!ps|By!r$LppFrIPKo6@5jL6w4-RC6 zITU_ob2OnWr6s*kzWiPZZw z)fDCD&NKYFDut-Wgx?%1Tskz7EoEc4Bx!*-5ySC^$%tiBjF@2+hPt6}y_FF)As87g zlJ~5B!tEpk!)4p>fajgsvrK)B2@{$iVpl62>4FG(!&G8&+4#GOQg=vWl*?jBrV|kd z)F>Z3)|Zoy>JgT#=!VO#*LkWmd4_ za`M%0zMYfdiHU}=NQsTf4*F|cJ&3kFF+gta3;QITsSegTI8DV~;C zz;Yk`C=|=~u9V;F#?IbhoOejy*@*EZ0aCh9VX>hoc zh`gl(CyvuJiJ-9zKRa@p`YS-=d8;~D8^_tbOMb=KvxDD~akh3P(q1=q)(+#mJ(;#0 zakoAIDjbTD5!e}Az;&UY4yV~gH(P=0f%84}QIC*7Wyb*}%V1zZ72Abbxc-kl&P`wC zCp6BRy|dW@rO7kk3@tuU`~-E+)*NUA`37`4ZEr)6FKCgvJ}qy!?4U`s4mI=Fc=22O z_gahkg}SY{VmVj3rj@;LC1HAj^7t8ZkHSB7=RkJ*&Xo-d1O#by zvC_3IgjSn%?(p~K2#16r)@KLeQBtUV?@X1%-a`Fn<*i|$4qGnzzzT@@geH?wXvsU z#g1ZOtHyn+Dsmk<^^l65{{*@&qx#)FPlm!>p%e_q=|YXX`xg1?c2)4EW_Tm3ysnLR zI-QkG)~?yY-CT3sj*jNKJ8W_>&v`y**y7t}4w?V+%i7zwiR9EfNDs9|`$DcR8-333 zZkID`U835;cWWkycWvRjwdd|o$^?y({s56XxRiYqoPbXh9;Tlqp^L(}0F2Klbjne- zlmev#d0ERzeIMn(Hj4n#229OIO2}zupF|_4b%nC-4iPk^kt1X^GZcKrgwIj@4Ye|P z=A&#BNT+z65Zw8<=NHGPP+vGcyMU70%cECsxA!pGvG~Cb!x!u@e8CQNv>wXN0J+bj zZ~!W*euSc+2$FuNH_IZ|1!)*rk`rW=F{XLnwg=!y21|Dm0uT^4a9}=_{06%WA8AoH z44>7&F6}UU8wSL09T0zq0r7VjknKZms|X z99;#ydG)OGI?$Wd!ns@AaM??HI?P7Q8eU!&i9Yjacn3Gb8~!0QoN^k_fZ)WDc+#x5 zCg>(l(5RaMvDLV6mGNh;?o4%0X5StQ)zdVsJ$pC?Ul-za-xn=VcL#C0MIzo4IBH8) zDzp=*H^XN&Gs#8q3qhcTl@2EtL|umRwf-h@KIAJ91|;_w2!OuBo#3o7Ya26Z`yAeo z_(d~GJebeP$O|&q`D9jzJeGZ^=NM|M zqUFN1WXNfiH|Vi2X|ac`&h=iLzG{!Dc}>0S!(s#=YLe_Hw~(~R2)k2*e9#`p z+z4WP8GRUDs^7UWnOmLL=6P;zU#kzg@tO@j=!Was#TKi{r%l+SB3*|VI7m*yB#7?f zlMTJ?fQD=CyWTV01V>sofxZhjA(L9J*r5Om#W0bB2nLF*1|WC=A_?d+@=OMP3TzUR zh7Kx9A(aRjkkCdt*oF#rgzBr@2jA(^vK9`kF!0@-IP$jvrY&cZP>z@*bYbNK;HRKR z;vqqe5;EvSK-0_(`t889!&e7q=c|gr%QC_SnO-KJ+Isn?q{UFdx%S*ahVcsmyDdr@ z+B+Mdy|V#sA2G$2!$meQcY^`afFLzR#yLb8vm2qB0YNJGSfS;Bx7(rMoE3)wFlo@N zcAeZGSoC=62FMN1jv@`)7@+gT0JkwfZx8=VLKhTrBGBCi$n5WGfL?!B1N8da@3c~f zQThl3+la!W=oIoFHr!<|3^N!c$crFXgPK!FaDs^l#Jkv0XI7NiVeEtB$Y~fLOwjS~ z*8sgHO!JEY-n~;HS0na&45w*>C+Q@=THvqn-m5Lk#?w|10Hiq&+V(Ex;g-G2B&GlQ zyu5c44WQ(zqRDot&LxzcuZbc{;Qk+sAsIfOP7q0y_k`mNu-V?hl+tnF1qNrRVQbax zKy!0bI-+aZ(uIm0yzUfnO696O=%ILL`onEJdq1pstEwKNJB1AU2BWNAV>HHg1$3Un z0$NZ$^ea5*tLjc&al#jLU4?cO(xf(}5y>(;p=<*F6OC?XLvi^R;2+*9E8kSgtx+KUGytDb7P)ZPzc6cL3 zzxkdBP_l+7V-Lf_=XKTl&~EmO;nZFXH-L?Df7rwSb>33j1$$EjI;Wj0B62Zo*e`H5 zxR@dwPUMEfxseMPk=OSUr8Xu$z5Kx8_4Q(I^yb+q+81NlHq{#tXGui$j*po>5VceZ zgf*sFTA)s=nybPo> zb233+4X@=SY&{{OV}2;{*aqH>|I9ve1F3xjQ=S!fgYt{^>P!bKrog0<4l}>;L8L@M zpnWx-uxd)Fo6+leH0^e7vB_qhWHBzTxKXPaa_c)%9ZLwS$fkNi1sjZjye{@rB{Et} zy=;?&PbTt}u;F<71g=-NbCuP<*r=GGI^_h_rG^V$SwF5BEClC?E{+!d4MhSJ;gAU= zGfi%UJ$Eh>Q0F1ao?Pyi|J}b?v54{+HDkc75bqG|{CoMD^3GQv-W^S5lbfsCue$ix z>=wG9=r#TeZ}pXQ;8*y_=O(^`=mWwG*fg7&3m?kxM!CBUdVLM{=LDN$aR zQ*#A-m9rl05>KwZSGRntMhjHy0(DO=q3;}lad-hk=_F_Nd$FCO6olk$YA z%4W#|Jyuj5zHzqbqfFTmryOv#v*rO%JQ9lIJlHlX5ia$UH1^WL3@^hG&)s?sx7+g~j>#Z>dIs(e!zoRs)DOiP;4>>^pphI948^v-dnng~EW zaA4(#$mOUsnMm=?C2x>&xkA$}Y2@rB@zl`y7_Eo=0s*$wmAYF>UzEPc?IT@aM0kxJ zkBSh&Uoa?MO?S+G0TFQeksmYV`G?kUj=5i9`6 zjEt(jl{c*n;!2b;vhgqQHJJvix>&kPllA*Lf}`{h@_ARqtPP0-fKpV4VahVUnMC+a zd+-fjKk9a>Z$oZ?gQa6HB9Fpy0P}!23woJ~DA)%tS&P08h8DG42`kJpfLG8)V_Y&W zeAnAwFwv1s)psHqW+(zG;&Src%>~ABH?FdoH2D?jx*$s>%k&=V63y?o>NR|V;$xt! zjs2ftp^8LwMieLXUKzdwb(#2`JnC^aG zAEg=_?$EIj$+nyC4^5KQ)6wThaPJf~6LS+2MNj=Le8 zyD_}>Zc|?2jw`n>V+0Qo$dMexNG+7Mqd+PFV{;rplP>~W5#=+o&`SCsan3+~3YsMc zdgLMX6;ZN3SZ3P*jNb|E6}Am;Q+U_A1F5?lQxv4`zy)X+HCrt53I>rMyFm7!VPF9X zQM%WH)ZN%)KQ?fe>U>xM3z@P?h`ILTs*+r|p(=kP|Iy81dC%oA>V=uTu@{7UCF{H_ z%;jXu#6DmQSvJRAn)uyDgL@FWSf?CUK3%+wDqa#=^02PPAgSvsGk<|?;ATu#y#&Ax zh7;zKn_;4LP}%npZ;AT#Jt$1UH&|dspU>(J1_aRrxxg-0_xJ;YuwDKER|vsl{QYvY zKuBY+6kHl{-k)Xq!$hRbW_ZLo0&@KGIp+8Im3Z7nMGI?$`f@Bjmo)mMD6-USD4uRGBYoDFr6!HfGl=c;ui7* zDEESJfPZ_-?G()e^JM9Yv2Cp5m!`-cz*k?&mYV-MM1Kg{Xr^nX^ymWnZ?;gpGvN@E z=ch#RK&4V;@o<7v@X1|9!|RxLxxBrWJ0M;)8S!tYaIqv8z!8#VWQNaBntKjMfL%@S z^;>EvE2dg^9oPzF*HW%$c0wcUBF6J2%YkP=!yK#sMFOZwZcu!#H)DE9qo#vkzcN%T)vRC24CZ|F5zO?s5?yPdbg_kcoy zfVz%(Nmehgd)^)16X*VI$sW2F87ILk zmA@NCyE|Q~q8V$wDt&ybdTy;J-pMM}E}Gjsb$e(AN`uJk;DXXI2J2)Wqz^15w_Gp? zBgxByVv1}Xv9^J3(m6HtIBc|UBZVSso%Andi_JP{e&?mYQBQ!zUa zb&?dE6ks8T(-foNGDs*q)c@Svc$pM$x(fGS04L_r2BL7Bez2{1;M8r+1E+4UXo0#_ z7er)`bHZPscAff>2UJAnpg7KfNL>U)3l}63eh!Xh$A)8#+XLL=g$XKHo95vTfLyok z-8?M&ch)@oL96t(;{?vu<`Fnsn@6yH$uqW5jtk;23#{5nhPfliAj%X45M_!mPYhfT z%Xn5lMKOWz#un5l{LphS3NdODp;6H^4@m5|-r#=C!*6Oe{nnbt-GG>z(PEY?K-ATX zuAR$l(nAVnRsmtw9$p3SFQGAa0q@^%cZ02sdN5FCWvmiE&zd_pL0(sf;g*fxOL*D{ z1LqNQgtDo_P1mv2u6-2{K{q#Y;y+V?R{Xta<*Cean&u8AN3ga92$&gP&xjNA`bTw2 zvb~DWi>`U|-RgThxaQOOp8U*R2LpXmCfP=jc$LWnN!j68f%O^#EUF7Cggq|&8Z^3f z9dE?g0a|C~rEE1(K3dv<^NVUq<8jL^fxpqjK3O`9OVc?&`g%rfUwH);PA3T(`j7j< zqBx>RvP+6S&&gy*PnPZ~Rf$vV;lv%6drJm)GBubEx$(&uyJ8$N9s1(MVXxk6B`Hcq z?~JZ~?Xub+tU0lE$K8XFclyTdIQlz7k)_SZij6N%Y(vH)y2rI-qeJZ9 z8}*ra+D?^TtsnNgO%zKlS8UJK3->k#ARgj3v6I@qlljm=jUafF##TbuGV1i;*t$5u zLakK*p57ew^`P>^dS);CI4%~k{wi;4|9;`|0q6(fB#VL!XizL!0O|*jssnJ<_RxZ4 z6VX!p?DF@@#*L(uPzo>{qZaisql>>X#<`Ol=fLB*J zx$VFI*W>8I+;@3pdz(c?#d{eXM5c&kRFdJnz7Yf{@s1>~q@+EjMEMiTU%yPP%vQse z8{cmoX?vroorkAD=3GXzTM49S_N*k#WUH?cTfxrXNEpR2fyJ+s?wF9C7GawkS z?t?{A=sM;43k0!NGtpWbLyDXuFo0S?;=(N)pY!MD3R5yY=xjMakmb~m>G2bfNVC9Po1eyAxP^$ zz;so%KrVJ13AD zeEx!Msu9N`W;nYlc+jvgYyBXwUwiO=$g$ zVxmnWyvS%RQH6U2`OjzuSMI$@EDy(6KEKxo5^;eOyF@i83`8zT8EOPU)?ChdOT2_= zG%P!K#fyt4IO<53&Gc>Ge50JS#Z!<_9u{fcDPr3DGlX`vE58{QusN;lT#SKi$}q9O z>w&T0Yns(Mz^F?!U%K!c@p%<4qWu1%7^8(|UG!ToKTi%paebMMmT^e{-OHHc*5cG! z5$40wlZ&Fta)sCy$JV=SX5d=af7t?Gp-EZNtt-rL@aeJjRd*`K@ z+1vd$=T84^jby@-pH_=uZsEZ>tsb1$9+=v=Otnib+9j0*{=07pJ3~*YtWX0 zQuYSc=2mryJMGv1RKwqXCj(_pzn>t6h5EJtgma+v4O3L1r+M(qR**Ef{Pv?(*57 z5~@DG9T|xVL-Ti?nraTjZnIUQoT3o5Kz=lzhbMIvE($7VVf?F!ov`GRWqI6(OYtKH zgCK0Sgstinou&`h-44N!K)my>yl+%-3QFvLZL=F-38|ex9T#E_ES^KyKaqkLg|vwF zh6c;^VuKfA*(mTs(mi8^(Cc&wh))DMY1U&oRAV5GZr) zg@0JDT2V-=4z4Xq_M`&vl0WJ4%wsZ#@e7L#0GD42)xv+I{Af; z3r;y=wEsdxP);!)m=0uBEE~X}PlpOVa!>|mDYB!)L{bS*Yj0_vY_nj7SkFo4fjz0K#mTzRW$rKi9d63HiVGGHJPTwryNtaFAwewhhgTA-o8 zk<xVjG#$1L^R^$P=5b9Y2wNc2m=wy+1q;Gf5AQ}CdajV|8-|bMkw1Qk=%&%i7`0EQ;P&TJ4yNgiMy7e{d#k--x>ym1gE zo=aFxYAth32O$&%2dhdv-e)qD*vlSl5(fdKiJ`9!i4tha0wD(}(Yd|@{az8GBlUfpZm ztB0RraN(yI-1c0>126Uy%SlKYg)KR#R4IpR!w%g+0^Lh+96>uH@dwzX#}*)_b_kv? zkk*H`3w?~B$)*nqronEKxc*(T2}>KbpS!``u}8i_Dbnz|YO7}V%=UQw!N$(mp}1R3 zWWKR8*2C_`o$*_9-{qb0k*0Z@drW%CCIx9qDr?!v=I$3;utJd>8#Unu(u=F@m(kSL z>er=bl`yi}1B@~goqd*b^U~D#&Xq9@;0JX;8ugbZV}VFjH50XsgOpX2R4KFGXRzXLVj=z1D$Wbo)4sv7?G@v{SB(D_ zzWAmaY$Y?f;_~v2ZOrpl@9x@zEx6~+ZFZaE*MEHN(|gORI84Co2nA$t20D%vfz#ZA9w8EM@G1rk5HNt|uJ7y?eRVd@?Bz^S zSE@_>09a)x>17FF$+qi8pmhxsH;X{--M^d1oMY3LByx8g8+XUCaktm7aWM#9m<14` z3h{>EIt0WGdgB&3Ls3-?Us!TeN2+aQo)$h3dhWiM^6Y8O~QZ z%*_o8y_9u#=o%ucxkNn6dzEWb4%Q0jlw^ieoRK}m55M|A%Ap|w4C5tX7EzjxNbtm^ z^hAm~(~@z>PzFVEF9e8`g`z--VSSq|zu2X=V|F`E%;VW`kOw8pGS4UqHQLA;7Q-UZ zQN?Jb1gsIh;pK;8a-%6DrgHU;C1_k)m05Wh-W;cJ@jXYfMg`9uLljcZafW zqFcb(de_vk{b*>^EAxZCI$2JH%W?uOml+L`K|?QYn+wOz73eK_RpJO>Jf zITXy0GVMe43{(n{+fPIM5jlMqg*5O05ORWd6jEj;%1CB_T0zHNPOgICx$i+s<9>H; zI`!6Td;-UT;G$=RNChMB;{XMexQUZS38c}|;4Xml+D`i^kulSPtWVLxY%vFCcMICS zOw3DIJ?c0P65D{2k2i zu|dB)2%7xM3`u>elpfeQC^i1uZ9%(PkN;^3n=Phb?`}QYv%BtM;o*^2E^Wg2qUf!@Cs}BhS_kOL%`dzl3yWMME zG2=R`>)ty=8s1ml^}3_0C<$i+>-I*P7~b<#ZrNaoq00a#Zjxwep=9TOZ(S|nx(4=8 ziMmoyajY3{9G9G__|cT`nax$AN8HA$MLl}3&p?}UrOkkfw$h=1nSx%d25k_c0h#G= zLbebP#0@VO(^>=x$=^9Mv$LU5&Y|;n4z^>#@#C=z@|li|$Et_`l3E*{6Qu=R3ecL75 zX9+Us;dxNoMSYzchnbT&S?&k$0{np_L|te&K(Zl1Szh8;9*XslBE`aB1qriR4PVo7 zpwxT+5Z2{blIwb5j?`>FALPIjLRcqPK>XLnn1 zcDI%7y~;dDIp|)Wb^D%;!c46E$smD>U+Tb2!oc>TB#mv`2?6&HoI&OzspO`hpoqOB zS+*4ms+faSU8A3ktrX6{eOZgWyS3Q6Tg&!dq&u*J6u@90Co&M3K=&o_G8Bm9X`Whk zocow+Kz%cCp|FC`wH>$sTVTh6oF7DB8rXQpgA9w~^?#n$vh!vGhSC-)58U0CAb0mA z$lZPkN~|mhbJSC#*F)%_xmMo|L*LIZ=(bBuAS^x}HYBk>2p!LLEnv;F2!G1}nQ$A^ ziUUC_=pNj!p`dix`5j(@+HGa`CCJ@<337J`Yc*2A%F-6DVNA|eZU{mWMDUzAn5+~S3_ez8(aCbC8*t6c3*c@4f{2yDvfh4ogsqK$efF5#*&owKu@RlVXp9SQ0q;LKO4a zaAa^Cge0y7loS%`DSR46N-^2waoSjdK&<5r{QEW(>vwnw+F&cr?zZCWZY$d_L5a;B zF-o36at-!IghIjdj)f*hJsBeHV2Xi|A;_jd&FI09EYHQB*d0WmjX_!`SdOh@MuD7o zKm6I+$|o&B8?43N-CFG3tz`!#)&VH{@_az}BoMzog5+RFCsAJ##|X1PmDMNUGK7np zxN&9!I|6l9E5;P!Ah_O$ufPKy*gw;m<#U7PYz=CA@}3|ds&M)8@2k8;{sz!wP|&Tf z50vw^%`iD>JVtx@5LzWwjh})t6zUjVmOONdDG|(JCVK4akSTnKQU{INZe5ls8bBTb zdZtiiON%>a6F@jfRYF2}r@tEUZg0xG=*P4viu<`*FMyW zD2tLXd9M)<0Sq$-`7_8-SZI5RE1o?)?FhR)Ndd2*;uKH5=igE4le}jQq#OWB1XN0( z)e#Ys|M>S{$H)I^=huJvzdph(4q25Z=NI?CLIK@~MRbK;iEUMEMY!VWWHOf_0#Q!q z*Z=HqNQKz>4%F)){06tR*7*quJ1ku}4F9lmejO?Q4{$3580h1ayh#OhIpwvctg;lu z4r?IywFGh>Tt1-BF=~AK{EVa(tfIS~P?k_G;uEI|TTvD$va;j#Rc}ZR3V&k7zt5L= z@ZgmiP%$8;_z?e;G5J$aAPL+l>--~HRgqtbC#jOzlvo_4+z~dUF^l|0B++LN|Jh>5 z&K1%nQ!01$F*BbPfmBo($f0WoTAp)) zqYOh8*_9r?oa`N9sMAN%^wS}g#8iYpKPi53Ma`e0v9y^2|GHr-KY*6}{NO3BKGAy; z?>i+g53k$r^yc3eMVPVHbxN_|RKP~24)_{9!4a3z$31;AhXC)X-%)AeIzvE0U{oq5 zDr)J$gCnG~E)@!EJ}vXX{HqKKpa3Uqx?O)*p8 zsVq;0awZF{htR?8ygJ{zCg;7#h9=A9&V?1o$agD?2)5@C0MiO`+a26iBS&h)a^^bRL)aHCgG z-d;%_yKdwTLX>JIPHZ9C*9VXqHFQWZWk^xlfF}WdVtW~AuK`0D_CGJ-Co^o1)s~7-@wAz9)r_vo}ra#K8Ui=4w9%JCwROYIBWdpH{cg}a`Ni*_~Pv3 zu*-jeUF6yE+3PdRDBW*Po}7_RWVI`QN(ea^lidz-vfN#a$=$`6wvY38ffoi*-}aHs zb`TjHSOY6{@*s9G&u1W`h5Q2IfY_@C7&74JY+&^vYL$9f4nmtoP6{yo!HV?4=Npsr z#h8pSwa-k>F2>~SVoW=5@k2>0$-rmlx(P~hYz`6P$`J}Gk?4$*1o>}ZE3!0lqkd@R z4(RYu?u*?&(w=ti{P@O%;BxRYG^Wj=0Lbk0E&=z0WbahkT#yW{Ij|fIutS!t+M9v_ zu5cW$4+gAK3JG0?7M*s;7f*Lyaa|Xaqe)L4>yH`#w^#Z3qS_1@I@vv#n!^<~q5tr& z{`!CF92#;GEjSUW$O;DsZdPcYv)0nEj~Fl;-+XaXP|`g35C7_~{ts#=zwZ>f3Z$UP zbBPS>3HvJ*I4Mt+QFL~SEB?h_b*LJIKBrWik;}d{^&S#(&@I{|pOZg|^^`o0BXd5s zv$v=GDRL!Se*>Xd^wEdmHFuySh|yzE*}dtOB^wlVI(o_z3-+VV4-ir}d%p*0*uVU* zJLkGBe_r2~OOQq0S0&hMZZ(?nE7@@(?!jkZU#GfrItu7QzVBalOleWKV&bU^iXi#6 zP`o1i^XC_*FXR}S$|n8w|0n}brMAbNbJ>uxe6+e|BI0&ZO{#lE-Psve9XI%E-4pXJ zk2_B(@muWiM#3y4F&e(6*ujHe|M&mm|NFoG8(7%h(d2PQ!$4wJl~rg@oh})|<64xc zv_8JaUktA$Qo*MDS6b<_2QDkzTdRIJvNGJJB#6ofroO2xVd^UJ&hSQb+`1|qt(~W* ztC&QoBA4oRhLH`e>%qhS+yA-jNJhg%s|~T0VpEQC-`Y5^TFIz_i4QJ{U~FkgguK+- zy{e1lT7I6QSH>5U(>Rp>{C`ukx-F@mGw$)ax-p&$by7*Y!xsNP%A|bGzF6X}k}73j z8zEV$2KHBm6}Te@D&6DS)X6XPpLbpqO0iWO2qsMOB>nnIC0Wr{Gy>Te|;st3Mq2LA0Md=aJG_KF>S?Li8ONhf?%|sY@bGADD&DJ zYvvR4ZUr?IP`bqpSnV|v@#C8bMED8?W$dX%v;ITL0Va1<3+f3Ul}clhe``FvS&Rzq zwPI>2C%xZeFaOOyEgq;cii#zc_|pxQE=@K1t%_??1X*kM*jhB?Mwxs@kOYyS6+RrX zYLysdD3H@NJ`T(pF@;TJ=@U7W4C;>ai;0rQ^WrX4Bq#XnAf@+IJ{0|Nw=w4Gk4Gnr zmi3BFZ#Al~K8_`jRt@Q=JYbC8(byAg=}+Pp7lGIg!CQIK#v|mn>Jblg5xWr0Z28EpNqk@mEen~T zqWZKm%q7Mt-=P}vjq;4Wu5C6X)e-#&7nNnEvinJR+iFK zI!~}r(AU95$E3;=Ui24ORvy$S+Kt9gA=6F0$~s+p>%i2aLWN7YuP&vDDeZ$XZa)#O zy=r96B%lZ{HH&731diuc3Vbap=VDY6I{?Y~0H~yKjQE#hZA$Z>9=rsN(kidg zdJ?9y<3D?Ie17rnWXoylaXN6~_+($iio>08|A{5gUNhi@*r zYliPf5Qh{?Y>i2^YD2EKEoUc7+&(G=v{3-@tO3CGKpfeF0b-tD_W-g4BryXNYlbms zJ`e$d?j&?W5r_hY)(0_t?tvq0kTxw55|Msz-k0FnM1dRo&1FbfhbjQ83fOkbAnEE;1TkVBwT#M=O-O@BkI}M> z!Avq!kGn=F455t*&{S6hs|e2P%;yahlR+sK`vymg3D=ieJ$kbCCo!YE2>Ni?8fZc`;H_;#|Ia0&W_I?JgALcC5fiu zUjX+ou#8A3PbuHJQhL=ElW?e|ZMC-m+GIW=Ht6*&9M~~2CLf~n1QH8#{X6vF@Si{W zqprkk@z?Wn95{#8L+9HA{!kFeKf(i_ED)C78;+MkP1jBI*{}cdZwkG*f?%DS3XKPw zbMOv)gQs-4W3L2_KIzj)a)+$vBhUVp%sKde~Q^+t96 zb+M2NVkEWtmqx%Xy~URf)RLh3OyE^$xm2x>Az0r37$_%-(`ERx;CWQVUWsvFriASw z?oTu2m@y&`W0t5o+4a$1j57*)k?D|9N133Vn)L5@30+hdbbEmO**6D=-%|ItoI^^~ zqfu-VEYf&(&A_5Tg=svUXd%%9Hm&4U%F%^Bsm;{Vc-WV~h&bWQicgq4VT|Dvn4&^g zpiG{K>_DO0ASoV&C)M8+-)*%-;L}y*x$@yf7xn<85<*ah^YIWyWeuds%zR(XD$6Qp zKHr?3n7}l^OcQxQd{_)!SI-bVWRad+rR3V0K=YoNGZ(W63+ZNtFT&ev_K9?gEy(PqG%7~LqV<*?B+@}9MB z>2i^nj>Ro9!B{R{f?tP+lY2GWmnN-C{t`bvFT`9$i~pH!bCx3-?C>x#L^GP^jdMqz1Zm zXBpq`s56NPUDmHgFvFz;(C{TWX@oqx3hT4ev~EMmNijALUa<2PHwCYCz1?^Uzl5%F z)hO%4-%x^qX&@gKjh*A$)lSq^JcrLU8X8co1+6%Q4H z(O7ko=ab7!Q6hkvPq+_(DPzq-Lpjt$^^4UoVjIG*Q>Sp&;-qD~HnfO_d39lHlHO zioCrhaA^)}jh$q$rM+iqS6sGRMMHdmw6LoBupC3i)%ar7tRqkn^wa~*keuG)1HLip zT^LNHh#RkEo(YfOl8+Owd5_b!7@UkG=u~A!q4Q}fQ82mS^{?ThE@s?Ml#@)iu%~Nm z`j9=WZIQ%>E3}+u^4*A7(b&v5rX@;qi+@L*OY9iM#XODP3z05gc2?+9nZ35U`05=i zE=GKOgTBe;(jQ}LQn1g+?KTeBZ5NcVNQNm`uTA#D`YR#}_z(6wGp>ZZ zOoveVGLt^X^eZldvfP*%_9J&~*q#nA|&}bUl*oREI`FV!*MIh zd9E`yY6o)Ob9{KPF|(!~Lwrw6q-8^J*V)@{5OT5(XZwXGv&GI}>53h<1k2ZM+6z3z z%WyKq=}n@x(qqBIzlPy=RW|#s4MX3#U)k(dC7?Ti!gW9FUH=ZCaO33dIo6>43uV+K zL>9(Lz}6c85gc1qW`T6w@3YCkrAS6}2JVzM=m~gDV<=IUd3a1}<{*SDd z3P>zK4Zk!xy&8LJ{t=P=xj_ETIvc8c=S9A)=`X_)XG2 zxVCJp8Xz#q!1~z_WAKT1(3&FyB;;|zJV_xH58A)Dxlq_Z23Yo&FYL<~_Q_w^U8~sU zxHRF^2xCn~i!rjx3wGk{Bhr31(M=s#t-VzW_5+DKsv=*NkDug~sL?`7h^i-+k%=WWKI8`Dh&sx* zs=5PQ`4z`TB28L=Ci+Lh%~Cd~^YFWciJ^6#zCK6b=HsJJkjkrd*xIfA>hT6e0XEc2 zy(WNr#8Duwo#%ML6gzE>Pwhr6QGnfUgH3*x2`+bIpa4*K1!8Z!R~tq9DaU2fIm$YR zm>_EUKCQ||wT($KY7zU70jdKe^ebDCXW6KnW zq|63`0r<2mNU%ap!|P+yWM};la%*6X^J6gH4swJB1`QQB3v9Zsy#jsnH>`o=U~gXo zt68S50vGz}AWEDZs6Bs>25Afy1yCp;Oc4Wo7q?^ltsLp(7=#oyP`4IB zIeDDrLF5I?gvG$wJCigH zm#q~k+t0n@1O1yxOO+`?loc`~hCn`ZG7^7&a=Ld& zT#c4tGhelr8j(NT=hgWs)LCyHfP~28W|0pwEW^D z6tLu1&oAm~;n)fF*co!nnN~4gGm-NFV=yuK79x|TRaQvgP0Lz|$UrgS@t{x(oEcZ) zL!{XTQ>ChCM&|d>UO?L(XfZUomYBpEr?ugF$ka!{cZG3yRKc#KZj$*SQ7W9UWVP3M zK>5Lv9!ou!_t}lOIV8hFimoS7b0m2rSCs+!Hq@p2SGL z+}1C{9I7Qn?Y_&M_xNr&jcOjtDm2Ny6Psk;tG0`6?JDE7erjvi@M=AO`vnTJvS|=m z2*J7@cD#T!h2+(@U9jCG5hOeY(1*izoSLs#fE*tcApOjO&X@1mgNCS!?OV_l-cA0^ z-na#C&f~HL2UUs(fIsl|5oAQb>mCHY55CiW4AlugY2SiB0Z{(g*{e4%k5+cvTkX!D zym)hb@#@vZ^XhN!4vtPwYDeBN7ku*K_}TGM_vx#%WAnK4Y&t=m`E+v2w$6b;eMdsa3ca=V@H(PZE8K|uw+1b1J8z1DUfy&)(jO)VwDtSW z>FEJiz?*2QFMx?<$a07}Vs|N5uNCL(P+V(Ysu#NJuv}&Pv`m28CRxFWhTYS-v1AD> zWqdCe81k8te+cmkRvw@h(A*Mu6Orm*A&ewx`Hu2VTzr%WMZtJR$r9_z2ZY2Iy*38Kso9j|rEVWz_>{ zd!di(`_>=yNTx$>j)`2L#S3ct^>(=0Po^dWv8A%3T-?X8(2mzx^uG2CoYLnr26#2~ zhC!lAA9)61&+)u$tkT>9&&0GXc}k z3BA0%?vV+Wj=!9Z5*V)wI%51c=yuRy4JB-RFdRbDFhuD*+J(=~yVBZ_p|J53%`=u# zeJh2qc>M!m7d_*|rW8wLRLN?!3b5Lny_jZ;fFCELVx}wMa)qUmka|vp+)c&i%Z?aM zQh7*OkW-l;aC1cw&8mF~U`*`*A1Kr#C!?9%=%Az|POHAd-{)q2;f1D;qLk4}ep_7C?L^EjY!3c|+>ljP1!}oLIv%rMK_UJ40G~Pfky|ohPSf zUE!%fyQSQ%Lsfco6L(+bXHb17*E~}uM9@BDr0~u3QHST>$b`ku&=d!;6iD}iv#TuA zt~{z3l#E4?M7TaJ3kh)3(*7cwqH+Y4R>EHft5z#xAz4LYwzMe7Wfg1ita1W`fu}rN z_91tR#Tdaz3DCsf)AB^)R1C*^T-a~0)XS8wrbVJJf_R09IxXvohU40jr|VqpUC(qi z6DF(X3l~m=?Q|X_)wWBvPXGfC6d?=T4{b;-rBRwkc4A?dncMMz{Fn%RLv9r6p*cW6 zP_?&^{{<=oXOh&iGzVpZAid#kiK^i)9f$L?MfVc+?xGB2EH7HYX}S_L2c8 zMo<(JHic_JK;nfzI}7WrAuSWXkGJ?)ywuTe0dWPh?B}Y=_wkMKq zHnd#DbC6F#DKRs}ASRa#lxfNha!r%j1b^^1ZBAF)1A2`))fO(6+9Pp(+{m=H*)}bzV`+&P2DlN9XA5R8#uL6rT_p zhC?Obm@(z1ShON<9#LfzIT7;r=_kOnfe?^Phe&baC5tB-Rxoy2DI3A(zXn^GWDI)D zE*h-D0W%V_VS)Sts%cN!RAfI4IIiU4NV6NF`Nt(+2{H;G>qv#DLGTC47c?sXeY1tg zutxLZ{Yx5z55tKF9w=ns#^^DY7=`_jSsxpLEd>`u>{2BadL)^_nj2KmBUdAG1#AG0? zJ@BBp?Pad(pr8wtpPt)q@EltqvG^-xX7;CJ+?tcLtY%@FB7*1xiRU9Lf@**iMW=wf zC4gb~wT^T>#%;ND%>4Kk@xJYIRwRTT4`!L%Rs6M8H^(oMu7+jc%Ns} z$*(}E!YM2Q^9%2AM)0(#XBbi?-cpIC%o7BpZe+T!8r)1222;?fD$t6m&`6v=drxvaPvs(>36CthL$d$4`S-Vl=rHebLQ_Le zqe8$SI|#)Pq948l$)2!)Cr~}+&=e!^?DfSSAUmZkh!JR`zL_XIuDl_t1d=OqoQhNt zyoQ4*JlCWqt}H@USJAL`{WzK$U?lHxMW!Bf4JC)jo;5R$8#_fVQuu#k_<=!ERe5Vb!{X^iZSa_FWItJr#eF%W7Y6 z0b0{nFZ!Z#Qu3Bx}m)Dp-|TqYDg96S_h3Q>$_cMk);c^$k%)5Z5sPnKfsrkeIO!zOjVsrv}j2B=VyyiPVS^jD5%6{^v#PwKAvy1 zbf@}b4~55#-?Vw;i1Iqr5hKH&G0mq&FHMEq^(Kl{5>Ydu$U?shSD4;bFnH#UQ1zCR z&n{jRQr`v%V`eToudo8G)Tq4B+ZAL@g~gRQdR&8P>Qi5xpUbEkw{iEX&~n9C>nt{p zZVN)WS}6M&+>3o(`nE!!tX{Sw(bJk6Y+`$Q&eLH=Jikfzbk)=Q+)4H1^t_&Xrx3NZ z4ZuZi)S~ukwRoNl?*i(rR*T2E=FII`Vq@^SGI|z*8XyM`h=rI0hzV@Zr%-|G_XqId zGDO>=Fn8S)tT%QNfKWDvyn$sklE?_lA<|;qyU^=$m>xhI4){5=$AAU)cAJh# zb$IR07WX{Wdp6{vv8ySRWo2J*T0Wl;<)u)p)E~9Bw#PPDNF%Q)Rw^IQeHF`0#0{<< z#qwoRcJAph6__B#oV%h+G`QTUX}PR%+cRPBMcv2Th;W$7Uy1#)(4fb?q+U|cCTFY< z;2sC=2pn$UoqHWrSC6PF!G97~J~kx}6D_h34(yvq38r(AwCMGEqNu7&%{b*3xC#D& zCh;Z@^d>AsDp*ZaJD0&|+(`D92_;jLx!k8NN!@vH*)xs<5aeD zEDVRwd|J2Tbp<|bHtxCbNALkv1(qTtXqcD+Y#42u#wV-LRgUdaU3>J|Utuz!qZ24?W zG5Ks{bh)*hMm~ls%33RhuF7%a`MAfM@$gp*3a@iQD9lnTS?U@|ZR-3Vnci7CM!>g+ORIJ_g zPi*g-zet8Zq>GCWZ!WUgNAF^Ad+_`(!e`$d9bNzVUtE8K+ZE?F_gH4CzGIuU=CroG zR(a-nm{jxdvZvI#ro1cHl=Ys|-4|{j`ya4m9ezlV!{<(bqyw-awcuw1;m}8L-vOiw zye2MCb$yn>gN@E|V><;9B10kka_rv?sqnT-=$}r~1_GAm;!hXJM#4Hs!$BWkrQ#!7cXTz6jD)+tIsERlIVrqx(?fYH zmA}iuy(_dm8?U1wTi3)M@*``V5}w4Bn5#p~TECBxtO`O6wz$zXm$c)*{>%TNc&N~+ zBc6rDhCy$x&JlErXL^@B6LRxeX2{}&WS&Y-)I~1^%8H+-o<-~loP$J5i{6ODkBTMG-|G*0hswg zyAC~LRIYWGs=1MvWhJ(-q?MZW^io8;JEO2Q&RVcO`UIKgg`|_a1?LJ1kKuE_0DS?< z$NuHN#%gU88>qbXQN*{&?~lvI!U4y(CQtpmkU>C6>dlp$G-D=K4wa<;a!b|7Xbwy1 zL`|ln^uYsWt+WMS*N4-rXxi9HP=qz$%L9~WMH*pjiomA}4Zq7xjS29hpGTrHv1~SF znc|$e7I8vEY(u<)2Zw0gh3TNzlUXG*lFMzGGBA`eAFn1O^G=3)A1f%!iK#;e0DLRVRG#;Nay^=M;wY zd)kbH%3#a7P0Bi?(an5T_Fy3bgPAuqz6_`?M8H86+bAc9T+j8RJGvS?k@V{SA?ip%(8p1oMK;?8ILGYm|a6=e`qRMRa zHKb2#tUqx!l236y8OtqRQlf2w#!P3w*D3t|o5Iu6i{tD{c5_Apt*i-taL{@E>h1B_ z(>E_Vuird*adQ3~JY1dEFAgq1K5{B~INClvPmJbOq>M|&#)F?-BoiGdC!$}`@hv7Y z9hVeu!oD09ypCq`!XCezs0g8v?J%~$_EOsIM8GSz=3`J{2mSyH!Rza+iuv#)*Q*qZ zW5jx7a-`}U96E{@${e9dJ=tfNO-pe~uk1C}Qe(KaN?244dAn+v(CglhCm%;5@u#+@ zD?q{FB(8V?MiN4df-#&7?myXJewB)KT*7MerZt(u8-fs+Sp zZOlzx&LvG=1Q}>4$riS z#K+ZhWNs9-r*g&URjtM>D!q$p@GGvvx>+_jCU?r+$&_RZH#ajw_E0hx%<3XAW}nDg z;eE5I0(s;GbMt<((1U>st7&A$n5Qq#{lYTJ1)#K`V%3}T1C(zS_rj46#D*<6tzhEk z)&+;vubCM>H40aKP`j&(k0AM)36QJoB21T4Ga3=6RPh1Sw5E%p$$eAl2TUP`$3r%3 zoPxm?R`oe|UbE_}s!ge^?uW>U{$5PQ@-4kSdv)>Z@YM^*ks@kVj`unBX(~sFZovpd zbPU%_Bjr0F<;O%^k!#UQLiA#eRzX2U0vj`hL2;2x<5bJ%ikbOel-(YUWR@_CmI$dbQk03FSflwSvM|@!T*GH6 z9zhRzN(G`wRDj_s(w!^b1qM5Aj#z>1AZ%qg&fzZqQA>_3#BYjz05=b3&T6!lO?3`!*+nN41CW39Zy*j5K?EWPMyMIZ+4h3A$79Qk%58A%W zwc=AkS-IT@6M-K&u-Gh*lfW5-QJf?m3SjX41Qv9F!w>`)zV{pXlI$;E($Dita@yfp zeP{PC$=Ur&+Cgv64m{T#q(~`%3I<3U5C~;Z-a~6_Kx*YV)D>z$W}*ClS|d$0CR z(v>QSh#3Z$vxKG&XzwDrY?eN7Bg#FAQJO`qUBn=U#vBot=l59b;A9U~-FGo@05_Fi2)>q--+>E6^7*9k0x;pxeH?4KTpI`#}P&xycZ^hVea z4lgeS6y3=f^Xr60Rr$fgbBP3{!<3DjAn5>bj%1Sf>a%OsB2G=l9~(T0Mx78lGMAGW zuQh0c%gH0AW7(iFhPh2+r|qn*Na^i;E7*5JY_jHilRfP7cB7={H}pAl^m6^3 zRP=W-{W;r6-B>$|p{!09tqLgXPThe6DRC1>w&hWr1&~6t6EF>=*xC)k6dCluj&g{+ z#aRgAztB(keR)2}{%qfi-JDhROZDRp%JDpJCsuj)ZdUomUN1+>kShRI5>)tLO+h&z zkdzjn+K^)b1EE=0e{_O$GNaZK^h-aj7RA}^&_Sbf>b6Yo*^%CHd3OJ1h;%MsC!+{8 z$3Vf2lr~!l zI>Ph2TEum30@m`6HAJ#y0}EF18S{*z1!)v)SH9*G+xhxehV z7t@5fKI|jEPLTNm}SN47)?1 z@a3(^PGUMpCbN=vy|{C-fZ5NMA8Y#4sNF0*^AsH4bAkw%f;4>k0&o%IRabMwGRF_X zjcw}P8uveEAIVhoG6PCt`K6*8<4CEqn=*IGP8G#O04=8?Jj5n_3 zSiO~`N6o8V4KeSJfyKO@)l9GuvLNw6%j`sc>RByKQAfu|tYy;TCvFrQ=+Sew;v=d# z^^dB*AKVNHi(cGlDR24v<4!GG5aCskL|k0o6X zbz4viR7ThA5;|sYAo>lk1tirJu#}CsX8|y%*1<$d?3X^ogU`!teL~{u&IHwR1Gmab zEnl)@`DWu{s1n5cR9M(Qe17oq+3|~4&wAJCx7`hoKIH~pyt*Jo-hO;yQ zF>oIQ(Kr(u8Vl4{NKXNgyS!Toaok&x;3N+CGsqRykFV`kpgc_#EDHpFP?qOr%gaGl z3|?6$Ne8jNEkDS1p~0hri-ULP-@ZItckul6@!<)=T5ryeHwF#mCnWSwU$Jhh8Bko$ zI<#Q}wcjnTM1sy=OBI_Utca{u4Fxemm44>|1@z zq4u_Bu4iv+=6d$_wX-hx*pt{DfTs{f-bX!bimKTxXL(0IM=d_eXQ5wcYE(nVO{^$# zY~)J^!HNKbKzzTTGOQdmAKrbNd85Aii<$ov3w%+Z`U8C^@T8OpDB198WUlpaBT5JP zY-S13 z^jM-(z=_^o$Ee~=6eZtPL#Tam(yH@gxw23r)9#JYpvRpztOe1dW)xILtJ?hmZJTxl ze|ekD%YxbwtCWFno$_XH0mR-La%;)mZd_dk$B5Y`5ML_na*m!ncp1gA8 zW074n+soJ*1Guo{H9M)|``s^0Qa-d#%xxV?$QJxl%wU>gUunxZqAgwd+p*F`(X#dU zHxUq)ddwud^dC}|3#4xMTzay5L4vH~lmV)?9qgv3@4Tg5-!WTlZP#DA;}*B&CL39b zz-ILq{Iy9@l#=9&q4FVdoGEARJK2itUfA>Q;>a9qvkR8{tY^s_wD?P{3(Grq(Bd?0 zx@3DT=om5-E`*Zu&~|f+pci;v5U7BXiHH3_0zR790Wq}gf|NIb#X3GE(I*(Xk9C2;%leiq;jvQMYU+|`vTeWlZb zmjHLH!e7?-P=EI3`0U$v2QOZ{dkw@5PC9QT<;1DD{NyDXS3kXSsWyOa3WER_;F!d) zb!bX)$Mfg#whrH1bXzUyQ{A*XfHSW1BcO3M-n1Q!^W$us!d4zCH{5*&2zQ?WwufjV z;SF-!Aa&g&q`tc!^<%^s`XP{SF&JoLCrMoF`$ONcA!3xKo(%~J&xbTkm`6_BV9F)5 zbVZNvGp7Lm%M|cs3i$1t0$QoEgWacqVD~9tdu+Mek5VtNKuZXALeKZXF@~QIAP)!7 z80!0RA5?Tn<|7ggG;HMib{v3?%t|~5Jlg$1V+wGg%ISFbI|aB+3d1i`z?UiDCp87! zh4SiM;(pQh5(DGzCP(b&N}weL=&SvVMs&#*3|K3`*V#mjUPPB__g)S$RYwf_1p%eW zWHRa$WogaeQk9afE-lV|Tu({=VdvHOKLJz$e>n%}D>mVZLV_JXpCaL%P?Q`8dDg3Q z32X4Lf|1=nL|dm(=f$z0g%;eo{~;s%kWV>ma&8bMr~rAe?@{!hV1-i0R-#J@LBsQbor|X)d{pe;^`j z$TDc?VL%uhZ$o00BAil^y6LUVKWX(qYp^k67Kj^xc@oShd6@Q1Gp2C>W4+4u944>U zB6Z7GG%<)atZvPCb__D8iQD^yjqlZse2YlKhU<3>H*C7orkI0X*9Q2ETDAqMEgLeZY1q#p(*eK336O8k zeIRjjI|2sRwSoltO|BKYp__ot3q?5%IYX#|6U@5|4-P(Eki%I+lMIdNV30xJ%7(LH z!K-lkDH;>UgMNru$6$?*ad>cge0I=%`r_dH`HPch&o9=w6HC;mx+t9=UtEBlQmMvFo8eIAP4^5-ORdX+)Fe*_QTa2z2Qb_7Tj2`Ku4A}B*-F+`4vHd) z7JEr+OCYdvx2a~ER_l##D~xZ=c)A5wc4L>9yFJ$L-g=TDqT|=rY()>!Qe<9zy_O{@RydnE_?ao41 zCSA%xj?U$DNUFo^K3HFQ>Z*?C<~^*q2Y1f*gYr<$f|3%eLdw&)WmU&Ieu}owQ0$f z@{!bdt7@~8l-|(9QO24Mr$V)^d{k>JzJAYIuaQa)Pgr}h5tmv%`%lkbz2uiXUZ~m< zTC@OcA`->W(XC?JF<0_R;q>E6Z-4t!GevCMy{}ovo6NWE)0a}krQC7KWop>?OvMJv zweSKXA|!?~&kBiK78Rd`icxh817Cf0-fCW7+Yv8NAlJNIx6W=Uy`#c$C_t{6d+i+9 zTi=r_k)bWcRq>!M z7vCYhoAn~9IA!OkP|cs(e?fPoa9oVnwiV~srd#Y78@l6WQhgz{;&)YFoJRlt-;5v> zCbw$FjOeMrS@>n$o5UwvhK<&8lil7#quzB^Z(=(+nRB=3Yq$+1yxyoKpY3jh0B>@X z-_cRtgb2Rt;_bJH2o;2&A{&GWHiMAra!`tnpaMcoDN1Eu4vm4gL< z_uZ3|SEt7pXD5eU{_82We#d98&yb|*Zn1Oud|2+za!i{{%lRDBaz4kjwyz{|`WC{I z4w9rGJ8&{9umS`Zve?Oe3zR~3?&b*#EWp)eM_}Z_fh>lFC7?futjukgmP2hY=Zk4= zLVVwob=jX|UH0c#*Y=eSft5sHpBfA>P>z?vu5vpN#W)9Aas7UX&{mqc;MNG8I3FY) z{y(>%X-zh`{-B{Q=-L+e;DRsKwcfglZ0}C?WwoL_K4)PJK4)PJwqF>1%tT=px{yJ4 z?8wg33`H@BqsEAxf`bL+G|0^i`cA|entl`op^LJbJdJ#iq(q6ovM`3WvI-H_j49TcL(l?6azCzP>-L%8_zr-U}8G}_c>54 zNCexKANhdZ!oi1XU5HI32%p_3jFLeDpDSNBE%H12mKA<6t$SJ+H(A%`ER62wER62< z3!_B{6}U?mkS3~~rcXnaCTc#07g<*+*nUj+a=!;6HaqZ!TnluL!RG* z-Wv;;an1t(Rd1*dv@rT_Lf&Sw?7hx&B_6(H)i&?;d%KfKnVwmj?^r&zZNh_HRo1zp z^UYnh$aAc{vQ=}IXE)Oh{Oy-AR%C{1DTTeO5Z3m#DTHk+gSEZ?pS?F(Z{$eRHTS#< zR4po{EE4p2Pz@)k5JQSmOckV3nPI(W2`u)w^0fz$)xFbkPVlq`F5rDh7x!G@K|M-1dFyS|f(S_-3!Gv$RrM;Ui_kg`| z!aPbzzcPX#@{K6YQ!-FFbSaUwQm{Jg7^Vd;a?_X;#59Z?;6{unC2Fv0O~9?-1ob^r z*{+Q#WLq)emrJ;^XTp>ccnRfpIAj3pp;{L>d-hQ*B0PVYu|^rYVSXAx`|a-YV|SmQ z_S!hUP7D75g+3l|MlQ!&9%WQ5`56_;%##U*FIY!rE{HE+`f}{~c?gh-8~fnpujYeV zj^(?)b%*ogsr;NI3W;m zJM|An$hh&5*bSu}zoTt(L0P`k9uB{}HzmT4G^ zepY~~3*?td;zh$Pf>%O%6Df4r3J+$}oyFAke#(hk_t8g??`+Nlc2o@@UXF zyzc;tWzm!igjW}S%<0bizR6A|$lc<)-WQ4hQkfj`nu@Ph(RS-O`?9H_*9w(&%daH> zc+({cp6U1-$xl)$bz@sPZAxVwm03yCW_i_uS*CSW=4l?2)jH5j+K0uwmH@w6)C7&X zxwnF|G!vwjU28^wvUgnry7bL#iva4{Je8^}C526wy)Czh>IId|se|U!sYd&uLaF#> zq;Q&FDSlOXR-<9Dys3!`qq0NG5wOGLsP56ntA|J3>{z!Wexx|ytz;8tb!&o>f%HcG z@~0bY?yM<$snV0FUX|11YO9-5m_U^E`}Qv44A*XjP3!gTrepw4w+4-B?IgUAlW?;t zwei{xyi|Uk3jtz6B*Gy}#00oAgg3)>62J<5%LIMHO@~5ZF3`Pm zv8>sgPhxq;#7URR;9J0z(Az@KpdMy2zFNM9&}}S>4?EqqPQc_fd)o^l)UDoj)hbng z^A9lVteNgLoBcbbh(Rrav%6wSpDgIG*NPK6y<8tPq1i#d(o=2DUw7)CR2&L*k9_xD zIG#4BgSuPCE8SYfp#55#^-_g`6$_fEK(Y!6JB29I_lJtUPFeInjpL~*a2#le)k^0C zv{ab#7Q9Bw=hL<3tMk_Cb=9G6O}+fMU}F*B zTAC7ETv6sJH7#td3WjN=s6>}1oxFM@=qB>TqR$w)!pc>YHe%UFOKTIgUscaZNy;nK z-#Nw?II_LB3SU+Fs>-T1byj}VM%A>OFc@@{X$Z4n@h;&1>V5(cdvKrtUscVgA_+nc zs^f!4qc51SlcESxY(xe&5`+?|896K;t^QWf!SncfpfOF#9v0*-z15*Fn6UZ>ok2~; zKnEX68%<~?azS?>6D5J-<`fb7nHNp3$8#LIX^r6_Vj%DVJE||K&mY; z3u^SjtG{(W>)L8Ddo*1^8QVMxpVg##aYo=(N7mzQU}zh=*@pFaTcg#v-oYBK_5!#6eHqDVZ?|5HI^75k*!*9A$V2cs$56kB++UB40frKc{K@& zBQwh7BXqvgl7D!Tm3EXSULXGiF2v&9gj-YNP4GHrfBse=eax>+W^)MDYbH@GHQ9)* zv^$@vZ=_wYx{!s}&lIvxGz6DNqg!CAo}a$eL4HAt)vxer`4gNY=PwV{kLtI};ra8v zCYZm>i096DtM`HsF%;nw5}0QvX9&QldP_CEUms0`%v8pHsu@18Y6Mu9=RLxnkVB87NEhi;_#@MB^7dr=3g~ zM!){*0Q0JXHKUM3_e@FXD_hD~OomQn_I zDTDTAA$?_hJuA>(u%&PxObbEE*i(>D9-mnZa`x zeWw+)iH+Kg)nHbzgW;`mj9pm44uZGUi`%FF879Ppp`Vh<2lD`xfIx-Dj_0I-mxG=M zb4ieh9ZjkiFo2X6S{8VEUJmhL5+ti^osva}ecr7K+G)YoSpz5sqLj?~IP_xZRa`1J z97yVH&$F`(=u&%wB3)>>cUjM%ruGO$AnKcqQ|xt_Vz0{-w~yyg5aVT#_(g`K;t^f+ zZ8x`V4<4GtGP1;ldxFZUKq}aC#?uOeEG9b_Pf93bTAN}cAj{Lb^C`A|+f&?#@@jXP zVY|x=cQA%y%b@(B7+qLFeI#?!u74AW)3Nd1>usYsy6RGN4Rmp?ZfNN>&>BUS~q zpL(v+=N9OPT8rCMGg9xbFi1C9LV&>^^dEa2KO%7Lj%)m*?v-G-FK+`s z5w`bVMCV`h1chJ6TX@3Df|EhF@_G!FUHO$D$=_t3Rz838^%~ijVs7>`VK_WEB z$~Vt$Vc)}UBRN>p3S!G$V#D#%x8v%C-+y2I^zz%@cTA!5QPBP>znn3bt6z{$xcq$Z zMpONj?}=r2E5K*d#SnWIA1?Fo(EbwJr{eAZA z=!1X0YG7Rpu0xIO0va=0 zVcIv+4I5hu9_o9@g=uW5b=Yuq`&=j}Zz9*uBR{a65L5vhDtOQ3^zufVAFOD{~0cu6+P z>$Ccix>Y4I8b!(PB1wK1L292MEjK7#VA&A?0Tz90i`%I&_kP!byZ(DE%l-yu-*v7}70vi3`)+VqCXtp!F$}I*}B`!mpE9Km&5Xpl?8@64)f9 zz}uIQrtJnsYDPYof>}m7Ev^)xDsBEqgVmy0!q^0@!%nPz-C;)3%uz%bXhvB4IC?;* z0OcvpNZ?IF*V=H(lm|)|?I7q{m)?4IoX?%!_K8N8Em&3=HO?@yQUhc7;yp976kmmz zMChXl9yIZUQ3*{hKIndi5xAj^iv^q+``)K_9RTZrrq}OU*4}z{?9ZLv4khoNiFO4G zubn0!5j&}nXMrahfME<*Fa9Uc88e~mj^XCTHR{^*@V8J0 zn#_rcjfpplcS`bj8S|OMpNn+do4MAWKy4S7%qCCS>g!N5w9!!}t`C)`U3TYClfytD zGd?16D1qAE*Jm3#xz-6vt4G4%Fi_tKXniC+hRe4h)QZMHtay+dN;H02<`PSgwuq4D z4wJi@4^%`Lw~dO2^1~^Q9^M{l zTA6_ak!2WUAg42OPKiIGob`KuxEcIGJY8dX<4^jU<(m?mkA`#gid=LKeIiah33c;U z)rbACoFgTUy)R^zq?A?C9*EKo^)xPD|I5llNAv4gTz!9(x1?$HVr2vSg-xF=1?Vgx z#Xc!HJ5GuBYDNat=Vw~3#!b5JvYpP}r9%3SOX+W+m9E~>Q30*pyiwO&)$9@d1{&Q5t4s7kmT0s4VEy9u0CEIHa_}l5YVAZ4JPh;QXeb7drA?{hmf^IY7RzwjPm9$d^BYzG2nGNb^;0R<&&mT5DnXbJ35PkB5eJr^#Bxg3NDC< zVt1LA4sl0dT~LvMmhw`o?fG-OG%8NZR2gni# zRw1rU*Mc0~yxVCB>Imwcr=?X!tu4=14yy`7j{anC{J(fy(U{p`cS^Ti*k7l*nFq#0&uk;zq*IH|i2uE?1a zR-|MyCbR6iEC?(6Hc*;OQu36M>qakxb#0oFC&_=W$1#aqNBLkfogGBWfm(@2{^x)E z4_#|^q@P-hmdbIH^sbY%?9WtHk@Y`j!{O2U5u{c3TYnQh%OD6RDb#v{wBjsMzMi_r(XDfH!CN;0>+;uGCV77EVZ0 zJ@)xR+?LNSL@--S_Mt8jOi2;}B1kf1xj|56Xgc%^5jA4Cdu7U5L$|0&8z%w=>#b;k)yQDODS z^>0)@Fyi?3#cOgJXDV@27OQ@ex?vIS)xr$|j5xJbWLJYmqTwf36Cq$!SSxCwFQWbA zX;#S@r=4pHUlc5AO~Ksy&uz8ssjOx-8?4AdTk}z|H6KNb65!psc$WqnD-X|P zGtfd(uRB!jPd-d`lAlYmlDEr6Yqm>S^qSl}+hKY)opTrFc68o`d)njU#YP%=kwd*t zfF~yal-GyaIy3RT0*4Xif(c;Zfdx#GATzkPSz_=jcmck|msg!R5Ds~3eAbq?zcXiX za94B?GXl;daBYf~9NQu07i6hLu~L#apj9XF7H7_3^Pt@)l0bKm3%gNu4Z4d^&|QMs zXTX}N2bnb$qmc(IIysR>0B|^^rGAuROAtP`u>r|Lz}g5**RzZ;0s|vALu$DQHP;0Q z1on0^b3L z-U!SLPIAM`96Jh9iy%Xm*AP1KHLQ`Of3^VCdTrO-w{kj%y5Z2Th?GQ(6Vf);k*tuinqxgb`BZKXadD!0lY zn2&U!jX0e%z8Q=sSW8Fxe+LJZhYhBMowe!Bv_(b>1JqTO(~1$Mf;!S#5B6q2Go;9g zDk5bQ-AuHdB3kl|aN~}Y`(3J*#tPB=`sgFzoZPGZ+GIjR1$Mbo(+MJ>?^CYEh;Y%f z;o)AMmr0VNjj4M-}IeL2b` z^FoY1Z3b5-K@WDnR~fbMk+)^j`qYHu`f<|+>3C9N(yNR8%GEPfbWA>7Upgq$J8!mK$ydTkve&n8A8zk z^n8#J!C&=5!eWKmXS#PzS+&Z{VOo(OQf3gBk~M-~(g4T+gmuq0f;<$2f|hUq`pX|F z@h`sl`sEL&=OB=MxBTnk9h00XD6>t-<`CCJ^SY5{6xV6Y{W!+ z@$B83x8J>aUCBgz_2!!w&;NRH^20YT-Yg*nnm`HGQWn*>8f#58(S)zY*Kgi^Q(Uv1 zzGMM)&?aYg{gB;L!`V^nZyQ^R{TFlA0yUsrLs}TxG`9_eVB?g1ZljwgLt6bDTB{wK zncaCavpa8Q?Sm?Dn&x;dT!%~|vVv0p{cR}IA`H$1&|3%}p=2Q5;>AQCLJc0l z4wo2SEd*(VjtN@h(`NRxnLTZ0zs1dLA0o9K`FR^lrs}D6n{P6?rA@J+RvW$ zv)_htc6U3O*?A{3JMUy2_#Yt_jWRfoZQl(;YPLxD3P=xg9Mglg)+F~ohsQRtGAit2 zhy^l}q%mld{FQ^`px8yZX251 z(Z0@wY!*tZ^9YS9Ln#CIpHL_i$st_tfc^w&451v*Kn#QtlW^%lp8_)=VJwer&wL2K z*&3wo(~kDEqdo0t59K%8wwc+TH#58QX4c+sMw&63fsao=fjbrqB=Y_teh(vjVm>_Z zt{aJa%rGODlC6Zl9ZJ3|FG>wJbHn5z{AN#^+0$nBw3+>yezR@+nbmnevpVl*?fqs+ zXhlY7x?u4EbDqEi2JlS)0hk9XGdYi-HHHK)Nl=s83Or?g0`v*G8+%@Ax$#5z&7StN zr~T|{Kl^q3X4`f$v-3`7cHYU_`^{h~rxMH}jW$Xnn88yI6BA0q47S2pXjlR#v?#Ys zT{8p565G=c%}i1?;0z~N@Bn_Zr;Y4sBYWD&eqFy=BdnZnb@rQCo&9EA%8^5XjB{bg z^g@HcrUlC+RRW@{xlWu1l)oB&XeC5RT`P)BRU-@9GIc0Ufbr=+kl*Yn67aO6J?&_} zj^AwCW@dNZ%}fN5+RT16zuC6^%<8*{|R? z+qRRLop&;`^G?>@Zx)0(2;`}o3pyo$_l9M8A?anIK#)eZ57{w%Jx-XTae#C>w2?h+WKSE}gZRyMSGj)@-cDv12$^Mld*#a->X;>1`i8YvWxHyv z4%PZ~#`gokYCZorJ^}$wz)Vk8QLcoQbyG=urJK~HZ^B^Z7Y)HGjX!T{To-SbTr^U6 zeg5WLGj@Jg{!!L(4~9Z%q;AkeH`Vr3O}k+3OQU0f%%83zuP&&MzEZX1t(xRYRrb$C z{Q0T`D+2@SYMf3BxHN@jRq(V<<&MhBd;lDkTJ<`1STvNfX;4$;`V)OCHSw3xXz)vM zg>)QeYtw1KTo}V0+zJ5n%)TqCh zh~{x2^DQdFE7x~KXRY435t_9?hx&^wmL~2>jq9)z3#t`jt8S?Q4a(S}FIATFh1tj( zwBgDQo~s;IJD>aqgKV$F!BPXSl^UWn@~M}!6VO(iC<2BhPO%Dsp|r?mO(o0Kw#{Uf z>%|G6MD&ci<$zyH$4C7m5|Eggml%=dn9$#)@EtiB_%P5jwpNb{1!ucfP~TL4L-;<1 zu+cTjmC;(RjMj411q3)xJR9U*@|=A<1*owBYIqRCPZ-8w2;Mv3p}0bGj|yGS1Vq3k zy$~YJ6oK^W@c%(*!Qykb`0(M8Wq+<*{r*KLY?Pp|tpJ5>Lih3l!fjEiK|c_d1>vSI0Io_hEY;^&plgc@1zL9X$s32_xd8eKX~AHG zMt)tW*Mi)$=#mD2l81=e+hd$6&`N)pDzMh+fUe$FN4qP6(rbCrkLbS5*h#OIly%g1 zMd**0Lf=B;UAbcyr=_9M{kF5Kw<8p2h6%K`Y7r~T8qo7?=fK*v?Wwn+2S{L9_JEbN zQ}W-;ET`NmGa!NTQAgSF9V?YWQmPT`JO=09z;%`bTI!nc&#cBGEIZ^MztdS@Z9NMX zJ5=Q?FnqjsKw_fJz!y!%9&eJFd0_egCx!M-Y`~S#y3-xMyNKSdQ>((rlUbIB&>fCJV&(zr1?;wuJHfQv569kLpB}GjEb5nILf^ zZiBoS0Cl1r6e#8;M!P!4$`;E5S#Tev^}a&rR_xFkq!98+d<`=JR6JnUhOAq>Ld*Uu z)d7^nYw2pnp(%&{RJ~JA22_rId@MqiIp&M^3QC^0K9DQ=`s|G5jC&=&38=MPs0pMQ zzfV&&h&|zgr14xIuHg0$hjP}B75kDjb+G8t^zYQcsT$qSm2{@S9vjZh+K&~)LB(9T z6eq;%SfI{@oO1KQ3Zr5?=Nz1=Yb*C2%fHr-*UGtBC2*XxPb9`{booB{@X7w!zn)wN zzrc2oPOhxn8uD-N_S$G9a5VaE-wb!3x`u0b0B5&3e076Or|+VnMxK)Ts=Qd!zuI~D0xZo^b(MLg;plWtt2M4W;x6Or+2OL z*|6D~I}w*SyG=A}mAE8Mv&_ouBnc$J6pRTk$#@GB`lwez8`|P~c`9*}HrJ_8$gT@toeYLJVCy1q0)LN1Aa`|N1aen<T$iM6R zHphDqdcF$*A@Ju}4DX;_Uj$Yd+#P$5=pO(tJDtk@mNXwC}omh%|TYA=2E{ zo(&?QbcjfR4;_S&1Sw(!n7?a85WHKy>44fp{UqFNxn;RI)pFZ z!{FvViS#{-bVo};skHqF4g4DJo(7{~^`e^n;1ERr9Q#2l)hE*UnKWJxs}~n2ik%Hs z#JQl3tH5~e!o%Ljs{smjI?gm_RH0LA()fbmN819*4ETqi=VMfK(X&Mz*B^DTSL}*> za5)#>n(FlV!b(`x_~9!p!sg}sltx2ASU(q1RB-@i;;>T#s_DGS?}XWlRYb#sYas{Q zPrFx7?>$pYQ(BonJeT$w^!>`^PfGuTDgCRfY7hOtv4QqlP~s$b~zomc|m+A1@d)aX`sVRGIsPF|k8dv*6m#IL4#Zxp01N_b-f?eje; zry(Bbdv;FP*@{f4h^R75sm;lKI|+#(;wQ!jY=NB`n!wmcxof6&Vx?Xd2}e*>5)Iq% z?pYn*LlS={a&>p=?`E;+pb0lN&_16b^X$N+n9Dcg%rvYtCfb|ZQIz?fsxbZ z-m~(VMMU~yC$l3z4jhp6VmnU(IB=Z^lmjE*ur)6WH@d5#k4?X<3rlweb5jfLxkbo) z1tKiYLql9%o&n-c#6O|yW;uosvD_?!h}|{8Q6w`h#RM`SQp%tOG-5M4-9f#B=}ycGDy!w=r~pMGt7wGy==D2kc@$M2j>6&xaliqnX%|WlIUJk+5MKj-_n= z%n7oLOK=59HEyj=;UPSiaRAkiQ2az;5Xh@azC{3+8BQ-84&l}fd3V+ClX|&PranW* ztT)r%l}Qb{>%o?ulFRDaG4PJ2VFor>kEjb#ZjsSw5REk)5)RNlQ_IdNWOpca0@m!II{k6|}GwpXgJ4^gHAq6!Iu}PRT2dI}2_|Tm4D;ETKT$dzvQ3;q+ zu@#WgWv4lne?b()sck;E3i>-0tG!gxWH`8q#}xi<)kYUr-HRgnY}xwb&_Ne3v@M8E z+mj3y@9cxtJ|zXXNII5}MH2AAV(6cg(MO|_fMvw8 z_nH~-TWMzD44^zVPc6rzs>LJ2DI@0!)BxW@5w-DW#t-hl^sF#*t=#n^57stL43rH& zz`d0vxfR&?odnMxn_5{Hk?tDhrWV>KYa6kXJ7!9ybsWV>idtz&Of&TXqs)C4ISCtX zNQj&iR*ypL#4*wY#spguH{6F;Cif7s$EGr_O?YQ*aZ?NJ5tmjebFOJ+;^R(v6KVYwFwT7wy{Q!i_BsKq^{kkuSEjT5YqqbW^+6V<4ci7H?i@ zuehiA^@sYFYImjfeCGg&5wVy@5o2XH@I+lvr@x=}zL?IDWJ>0z*VAb>O25=#QF96c z<3%AJHzNB7+;}ZwBOp#v(9WJQynV7L9*;#)*^c)&DmFLL-hUW`2^B|p5MxS>+<>5T z9!iO^8(V3PaS@~x)CbTjP(e-gv_ZOB;^dKIV_f*5_wcT5!}Lkb5JSkuKNX}t966bl zP+AD@jh)<4c^=?|c)QO}o}HgPJ}YBgX7(^NHaF4%Q;aCK4b?44hafTtGl!oTl8~Ea zi3eK|*^wFaw*lrZQDsW*0|22?%gc=ztJZvY$8~qX`)!HQom(CoB%r(TvAL1J-3=Dbplb-ol^vrUx?X6>OXrT4}Y)K+yNHx1{*|>Xh7t#ol z*AmAK92bZk$IEQ0zkCmV)hJF)+XS%56$~c|@tpdZ7jtSqX)_o2?NNXK6K zY`ul{Miq`u*darZ?kI}zBMKy(%m@Dt+6UrFQ2^oqRbSf!h6B%dRaW+xq86k&{?bR)S$Miam@w#=P9Z(5(P+WPP34hUGdfkgYM1!F!lKVntrB_bTmwPI(WpCxhL|-f@p3bfy z0+<|bRd2W6`lR(fu-02$t9vWH4UKeg3Pa0c=V4~MRuUvYF<3sTh5?rg{xc~XhFpqh zu;the)h32XRM=z>HsgqMAk7{g2@GB$N~^QP&T@zY2v!4;1MO8BW-0FyHnT}Rdvo&c zOi@KDs>+gX6YiGEmrse$8zic`_S?`%dygB)bZNp`&pZ=K01{jXAd~Uz8yV0@WU-Rv zO7$GhH9Ty#@1PLNMP}-oQDUaigL{4V7t2-odu)pCCPC<|>ozpfzG8zw7ZyJ3BTtsyBB_v(%Z9^flnHMtNO7>BrVI9}pXY;#&}sChH(4@>h6j~N ze{!on76nseU~k2;c60l3Wr*4FTse^e#1~xdL7e+3FoqtXS>Ffl+b}J*UVzGNqO`H= zpl~{n2f9AEvvw9gxI$@BYGMXS5`hgF<=`(mjv3iboKu)*Qg8iK9k*RNx+{mZn>%2C zgurvflBh~Xp)ineVrPK|AucK*u1~|^3J*^f^tT3HQ#dGNJXbNfvCjFL#`#ZdB0iY1*Og6{)=ComiUtcIv}a3h#d;XsmLCX)A@+Mewa z&?mP(Pm%}M_kPoYvX^Q%%C~EF{JyT(1xJSUnTp-(=g%ICUdOAnMsJmCYq+hwC*0OY zrq8iv_EzUs?``iMgeuIX_6Q_u7y_IOup&5;Ezike{wC0vz$1pk#Ypkivqkua^bp)=`pgOt75J^;F0K>m3L|ifdhJwjZ}X}*~fJ*-hvojYUSmg2`w)=%k8?F zNq!{9E`#l!@8r{JJ0L(no+Z@o)82pnFaM1X$($sYTlsLX(n;M)zbCyfI=o66ych%q zmVWb%dX$24(8TozB~$Q9AHLGp6VPY+5A_G38UB#%iy>C+_x1q&s%Sl_*AJ^+R|bE7 zJ$rK_?T@8|`qRwg=ph7U6L>dM;Y9RYhjc8?AXZbFacDuA;CWD$dO~Ouhp|a+YLo^K zACHd!HxnluRKZ>pB`9Grfm%!Q5aT421c4{_X&WioLz&*(Nc*yX@M>{1QS2Y!41)+a z6R@!W$|SJsp~ofZl&m`hEcfChaHz6^7B>qFazEe^&0AEp1b3@d?;&cB&7Hbw&bw>O zn;U5_H~>MUCd3&)&I%L+p>xrT9dWQmCOocImOBWIM=c(35=LqP&P@aeKfDR!c)UA% zG`L&U`8O>%duhWrSL0D84!+$5{;QYXOBK`G#Umb5vH$8@HE6|%VWB?qS!tDYl5o2u zhbDCO$^ZIz6vWk)w6}`b*i8HK5W`HOgtAiy5T4iu=o{70ry#`j!z_q}-`j_;o;)~? z>18zro}g4x(iduzt_r1IGEPz@~LBy1-F1KuNwfXW#{yyXE?YGjc^vv!Jg zlba@ftvK@y3kcKb!Rwgz5Y*qV*xj{qx>;U2tEY|4v`-X42Tc;6wBR&P4c~`Pm}+=% zsS@C~gByl#fkt$KhIFJ0<-Cq2+|Pj>Ldl&Ztg?D)eVo~VgD9^vwh zhF?BuxsOK6T`sJ>HQbF&wBEg4@pbwFIh8;Y>v*Y~dYJ5Bqnd(N9tZF%5ey@7D}m-P zCTl7Wa%%FyrgTj&eE4#F8%GBq=7wY2p#g8G2{a7#Q@)hcT4wA&xj@^ zJARBsZZT~+u%eOs;F{%cT4?st52shuwQN-l)fF4dhCVmD^h~$>I21zlGEEGxp9s8D z#vb+5V3q|$CGFOHqA{6B>>&H}N%?y;%AcC;z4gBO-R%*WKqa+YPFpW0xeqR*km4c7 z5l>NK`p|BNc^szjxJC}hH({JwG0d4M^_srvX7*}C3HV1pyl0gUmw^{Y5!-x1J}fAE z8RYV*mw|f?J8kq7_1+~O-L<{?-5t;yeZa@#1pYMxcuG$`@)M#g-h;MI`&|)!D%{>6SvT)$hH4W%Wkpk^bMR2Tsc; zic(mm9$Tw>d~M!BJd_Ay4VSxAHgK_>E2kh5YY(;%c6qapwg8yG2$ z>MRIz+k&Rc1<4aIaLgRgw?VJZA0DD}57GNw%GsT(qID_hu8wYKp?&QeI6#4*{;+5H zmJ^vk&*QzreFx@2B4$eHlHF*V!h{ft4rq}sE&`X47H&lkAIpApa<;D?yE8SjxaPie z*vFKNS-j59g7~S@1OG4;m9@v%L~%6L@37Ec571xb{+bT^q{9Bqs<5Ax&bfYnd-XLY z(g>}MmrGtIE(8jEsINzlHIkfD!tfj?i%Ik(z64M+IF#*uKiF zP^9ltX62guQfALo+mA+>ReYeEm01}@rM|9sTpKjjgH0N%^owUyd6=e8`s?G>Uqxa* zM}Mu|-+C|iz+Ox!-oW<&DzW2d7QiH#gj(GQj|8!KQsvM~2}*f}Nreagu^?bihervn zks9MOwWRlr4rK^KpD$^pl!StEpHmcInUR}NFaQGS-DGN>oxc9HcmG{itGWSIHgF+5 z*><@mOyPBYeVw=>rhDncwfoyAB%|g69VvF2XM`>$8PMF+KF2uNU{Ao&Mj3Gi;UlyO z7EWI~1$#6$0JH^@o)m+cm^1Jd)K74YbejrKlCMKFBO1 zI8`DypsFh~{4fnE@CvX660r@)w@fJQ@_`ou!T_=WC!(t?eLPtw>!>VAVVbPsCu=aHpuXrQ$WkuqjmY2;f% zgXfw!uU)j{2*}mUab1vLDGqc(bZu(9tPp(GI3s@?_5qYw;4B`#k)LwjkpAIVkK z5TIN2+T~^ULL>Df&7Ty_KMvh?`9?caEkI_S00y9V1{MnCkAvrfgXuihn(veL_-M4p zX!W9B^_21y3=zle;8ZLNS#Z ztZ*m)6+o}xKnOB7OK^gFxtZEA_*||FePu#HIYG{562Bi`JbzK3x;#~ltw~LH6>?(} z?LBD;C1Kbofg(irT;GX(fFuFIH1MersD?%;bV!&mJu8BjAfx;{fH%VclFf=z`@xmS zI}yCcrbDjFKxZv-V-xLb%Ls!RaHr&UBpDBWWJMte6d_Dir0GTyS4cyE(hM{;gTe+B zOOJnuD|j~2_V9_kzcVq~TaDCMM?2L>ec8RKk5;2WDWa8cK}WC>JH?9{ z2Trt!8T#3GC$GOfeNrm7O3i*sRL)AhsQ>Bu=4P`- zxq8jr#@m;A*E-!H2BG!_vf85_>dV>p0nMVIEGOMd zhdz$>G`{p*Jsa9-z_?KhXo93&!Q z3!oPnCE~Ho@!YOP1gM&~eIvYQE&BeJ!a}hBCZzbz6#30k*j|foyuZB^TOtFnWsw#| zeVK0(XCSXUM@XU&%weg(WOI-G%+E0r0k46v%HitRA|zH*;qOI?*Od4tDefx8+wBOn z$^P{`jwaFj{&mc!o2bld*!a_`)gWgsPH4NyYJl=quGnZbeEqC!an)8pb=Q#plhGl} zua{S7)bj6M(F3LTj-n5P%Yv(P)rRe`%!a4*7`NWX!R$)#$rg`@(n{lp%kKxHBe|X|TFh(o^6EvX=!@%=5{ogg{9S4O z)A@}o>uE0@&#snNH}tHeIj+1WCelz4awV5nFx0A}sH}#;_02HDq*)G@Wc@xbTKa0) zl)7R*RDbqVSB3-maCRkhx5+SzCb-up>3|$xSRAlX^k_+iVRSjlW`m>`{W7?o&#tC@ z)vUgMrLk```@?j3%XCJ1$0aXMum0!%d)|2G^EsB^w{QQ_QzI!R(pdU(sivxjL3V?h zKhr~Y6Y*6#i9YgsV}s|a9Z4&Jj5nQYrinx0r?T6Xg5L#)rIIh((GyBL*wRa3O*cewj_il8voQ$&w(sv9a&7?1mii zWFkf|eP#${I+o(v+zqj5ljsI}8#|NKa3x4ZKN|1)dOjG=^lblIHj_QCC#zm^*X4~~ ziD^4PG{4C>7MdUaZjPH=3_1^b9MjhSDRft zjLm4K$hQmE$)VglPE@e&kAJMRFrN*E&~g9qk7^-Y4P=ZD8SU)7{H0eUYQ}Af8)h!0 z4)iG2``=zy5LKm`Dt=(rq>l z*~)VkXb;y^RvCns%We4(chukm>-i`jOr}j|UGIodN+NMetv^8;Z+7#HhPMq2;gyqg zGcox31%<0;voEI*M&97~mDV0!bN7erxN_px{W;P7A5>_h1If#v+^Qa;NA{`pw{^v@~vDYIqwTdXR5NB?Se4IqDUd(D+( z_W8%%;Yu>S-7ciEp^YYoG_cXs*r>W)m|lZr(R6wH5(|)8h+&dY;t3@4>-jt@q^B1qzzuaYXP#O5* z&o9pZN`>CDH{ZT~q3&(xT3S46n_p?;%=W+e>+6$OFP>c#;CJujri;_Rh#7GTv)k#r zS1-=axW8sojMQ3Gk`_&_J?H2{5FPRtwK9XF4}Ns`De&a~FFZ$-`o+cnT&k%58tL=3 zj~0}EL=);ym4?$_7#E8xx0&H;P1sFq!s)gqoNjBvX&)a8h^YoiXbCw>5JReGWS}Lc zM4ojokiK00OtLfwxZVmpfNdeV0@sUZF|p*- z425`ul5qg$ZT?`(9W$wQ=+#{1ng8h(dV9lvITdXHI=5u<4iQAK&R~$+5-CBqG1ln#! zm-I`1t*1aL%A!SET7RFZkgqaMXIFzsdV`+63{p?AMTjt$dS`y52U-O{^*~OKldFD; zaX-tHW!xV|AGtLeO#1jsuEdqJ{cc1l&PJo#zBsrR!SU`k!V7t?w`zH(c3%|0Xvq4Jf0Id;mM*99BB=dmNx>NMOu>UIuOn z)cX|l#)K=Ny0PORc0(aRrrdQ^w~=yd)9Mxp4z}&z%iW+#S3;;eAqZ_-kahx~9s_JD zc|WcYHrqnAv$lU%A_CMXp8iEX5z9S0fAj9-TLD`-zxeUw<;%Ax?7v0%V*R4_u_c-9 zCY{#uTFPhyL_{_xDDt^d=gQKqhTe|n2 zpProm@b2`i;>YXW+QEPR$N%V^;2|xwCrL8fG&fUyd7io4FPRjwSr%kt}eA#Y({$6u-+zh!HnGZo*W`dT}@ z%Rj4L(aO@$eL~IXovAZ?>89rV9Y*^6RYG;FUXU(IxkjlzAqMYd3>pQda*7krK-oG< zNdr9o?z^*B<8eC7mh$ra&6(u!sV|pO!x(gId)eZ*(fmCE+k1`%wQO?Fl)4uqej-A1 zc$N%Q?%zK|*Ej#vJ5A@wHZN>>+op6or2q~;T}hm#Uko>1%SPp^oR0FI#f)rKz#8vr zsf0D(f3#H%a+2}?7!XVymgH=6)qZ) zds*5;ug9b3gu!0RoUQz@>FjUx@XAvfP4>zreIc84v$bBkvb_@vxa8PL1L{*?d-KiA z24>I;5+5K!@Zvd86NAE37-z7KaduIx=NOqo!Jc3~cASb@ zxUro!$eA=fT7JVW{%QG^|MK@%@b^8Ln1A{E(g?3(%hlWe`{U1RbaStz)vbzaqbt^K zExdMX;kDZfuL&}&Vfij7B%}r<26YE86_V5B092F7nsc%S5Sqnz&WL2(51Cs%#ijV`V;&if0_oWc{e@wb|`Zi&%hSz8jH zJe9h(Nn5AI-b-n*?vK)WIXaXbO>axRl{cRXh+a>1hX0b|FK1=VU$wE@eal(X-8(+0 z-;@M&8chdOPY68VtxDRe-qn=-@Cw%Mfjk@qH6IkU7Vk+LDdO-bf!UA*EoqqU<QuzZJQsiQw+0g|}lO->r3WBBh zqr7f=vQXtQ>aJZqn}K?G)zhyoy%}7W5R7UNw+`NUbXmDdB|?xkfo6y=@G=?=eo-&k zoK6&xsDc?Z8P6}TT0HKo$O2M{L6SD7I;^tE)W^nm{D2mws;0^7xGpDrh-NpJcWuZ@ z>F$?2(`j;AgZ?gyhO?`tT9qwVw)Is`u8s!jadf06{77vHE&P>hcQs}(s4JIRZe1aD z`o=D&qSMBysM^`=W@-wZZr)lhtfts$6MEy6C4;sAL8JEn_KwLazDGCA41L6c^Y9ki(Jd@g_9H?{vH6o() zXkInc972nRZQZT#n04>;--z%u?mSjU;juajkJTRQ3veY^RD;BZ_L6$#FcM&WDtw4| zSVFC8x@ImJP7b(V_>|4Sh`~C?8*UjyJyuz$Jj#L`r<2iOZ?B24)z0HRa^cyoyr)@z zG)qxj`h_;g|4|1UO%^Krwky^u7)&Y{f-QMSx^OZAmZ zRp1NDRAH2F`L|NYNL57B0k^yCa)IPsuX8x*-HbniWHTQwvX-mkB&y+}`IV=VrK~@z z3SpfBd!;lYzMoA7$#i3PV#%~VLZ7SktFQgT=|BU0OJe$XE;;vfAQ_J5QlmVPHm0cC zKhMd;P5YH>+5hkV_}~Ad_kDJ|C_Y%rJ(fTJeLg_bK2?LpC||DBOdRlys)`mXa^+2R z>^!5(nqMPE?m<&AXsLP_G@)8n?$auxsj3Mdc$1Ywb9Si6=rPwpP-FVKW=s`x7 z+VNyC%_;-A9PBcEm~CK+KeqA~l0-VmuA~;6B;@n8;*W!aKNh+vK>#%qU*X6e4KOFj zzNgB)CrRu~+4T1MhW;0mhg);X$cguVN(--+llzr^2vraB%hEj2L$U}cU==3vdio_~ zrOb!=W}5mo8&M%Y)B{gzGSKC-D{^y$`&QP4RKpMGD5`hmt-*I69-R+}$$TT-c((Gu z;`LUC;UxVa#TEo`$U3>kXUj0DvXE-`#D4rRNX3Yw{h72+_yS{UrlTpUFPVPzm6ue> z=1R8Xae8~mI}C3MaVNNeH7KPX>B`g&uFDMbfw9*P&`^LUlZNpy3i#~m-Zm@`GV zO}|aCh6U=sM~TQpx}8Zqh?2^|Y+>m$Xd`9olA$6&h3R2odNZPztiWltRHbS=E78%9 zN#wU;1}2Qe+en+%zl@QqxF+`HhVix7J6t3$bvdcOD)RP&U>K}!J31+;zUgpn^R6Qp zUdd!|qbIxEbhzbI20)EZ_DR{b+WgjNVuewxCZAlVah5MIN~AAzT~FnBBE<>1UuuNajUTh&@aX+${IRm(&c@@R z^kZ?TNTE!Mh%FnZSck3dR#h`xvm z%DciBR6T$wck0b=%H5K7ONWs@1&Tvvb`{BP&xb$EdgDZ`{8UXP8x8eh33wb*>&0@+ zCuj%LuMSqWY2;W&R$_t7<3x$tTgsNk;wLQLgv2ch#d=>*buw1aB~#h*0YfRON3vPc z|C0OOaq`HBmggWUfmFbiXTvjf?v;&B^u^Pac(D7TX`<7si55Q1}9ukRh(Z- zy_R}@>9U+nc)6^yWyX4=M_$X$)PJgOP+yi?b1@suTd|%BnFFaVE8a*Sc3S*6vdWWM zqouBl@c;V%pwXxTMiB}*lB>gO_Iuy*eluBnrA?wld;D?P1Kp~c(S^W^hN$YaR*-k( zlErp$VD+VTviHNyV)qrfU2KOxi++1lG+wBoss-&=AM1)R*<1CV169x9<}2m1&#uQm z4b&1^h@GnC6un1f!W_|!DLz=G_pq!PEUif#*nK??rAqyk9KaMMW=Ena5A~P&MT%{u zG$8m&y#8r6!_liGr=lgG;41Ov=yCco;wy0NbJ0pmo}r>p^y7f(oKjM(lrYtRY@U}C zwXuPrgtDbzeyJ@~>z!O$#euIWcqk_aZhxUWpHD;>zv@XjZXw|^cLQC0sRjchMd!-Q z#-%b;C`U_O>cHz)rfhLueAO$D3AK5bWx5Ee2orKnA7vbim0Q)3H(8L(*SS{N7V@31 zDR_r6ON9{`Nx7x;uYa&#MoMXNs2rlnVSA_g>WxjADdNx}c5|t6lcyhm$vQ6HyL@eB zH*a#jx4gB<3*YMYHrXrf8rqh`qP-FI6qe~xu8zh(89zDXy*1<$TYY0^KCu(-cKsLa zY;?dmVS@v1nIl@44ZxeeC{sQZ~A za637Ogn1Tvf|#CDU1+U3<1ArQajmo+aBzbBDxfkqn*qx5#9yS#|RZ2F~E+80PFm6ZE5Mqn&e-q8?&*${OR zH5$58+$W_Uq++1d11V)AV(?7KM7rH{lOS)@TrXm7;g(Vv^U9N@T`>pR!H%VNjr)2! zP`ENG--;}u*=+$mtL*$eD)H`FSFhx){M-0yB>rt6Sc_>p{s`vE&McYr`&y;RYl2Z;wbp%syAo>(cSKmhh;8Q9*6*l2CR9kn`5jMZsk z+GiY4)=tGcxcjb^Qc9QSL6QR`n#7)%Gfo23BBYN|Kq|fo;315Pwh;J)hR2jbFIt-z zlt#bX#MrxFbt$I2mx*bF(lk3wjM-^o+N(J5Z7?mUEj}tvVp04Ed@I^bo{?k*v`r4` zvByeD?Z9N_LOZtrfAuZX1MenCtQx|7;CXjAEsji!^_kPMyNK}@0wfpU5U2rMoa&o? zspVA|7{=B9ZAy@q`hd-+x}3*fP(edznOFg}QOdC{ISA>On@;;$0>gApo+~cV&EXPt z2IF!SS1A>EAA3Zc7~f50koA}Bvq-(QgzD07FF@?#M;u))yr($t>YW&Wy1f*fv&-Q; z8BbR=iDg75AI=A95wt&)caeyo{5LAIx6=WA?GqwShVxY7b5eMT|8goWM~MXJ2(BW7 zm(YZchcCj6rIv$JHV5H8*Gi1|UKe>9y@P`u0hA&OSY;az#(Ipu$a%YLf+jPXeJO8? z%*e&hbYxnC9w@(S0fW!$6T0K0y6WuDIvi63Vg*~UC|JzWW8ylK+Z%}}{4%)FnbLC8 zNvr_i)GMkttHwg4g}2{4FY>A(1(4K$?M?pny`{79FUQ4dXyG7Txw@54v~ovhH)!=< zHqqml*a-!h*DU93a;yos_^p4PZt{no!v}yY|5s$XuEfpMPk)k?cO)xsM_u0yAfJ(f zV>s4-UT4R;32A`_mL+~A?7w;X!{pt~Pv4w;dwFvf{F~#=uBU(g@%#Al_0K0C{K=2c z{6>lZn<{;Chr%dZ#BP#uAka?eS7ji)Ik6oLc`?@q2fPs z6AJslvNAFh7uQX~%mNp7$2*z=ijz^v;$;x5 z;p&HP&9t+?NKz*XhHvce}Zy%RC* zZeaCwNVwU~DlXWV#{OC6*1O-BsioiZ$>MslxSlMoCyVPhWpNdIQ&+>QdRuFKf!#Y# z3I2P28U=!cnzDN&k!8Z-N>tSY=?t>qBn*UM%JVp=EicVXs=zYp!rWDTfovHcpXNQA zUvLMxk?RJwpWA+HrGyva*z=7vvpv&|yx)<&P#i*!$O@|np|hXtwztVBO0?i+%J8H| zK^}*$?|ZqQg2;y~P^g`FSu9zv0eJ#`O6ejIClKKs(qIJU*Q?8}i%NXYCfSqh{)R2H ziV#}+$!>c)&5r_~2&fxU=8Fr#a&zCK#5W9WEHn>_KiDe`D~z%B=uj^x>QP>K8%NRDHceDo~XPY+i)t)LJOr_jT4T-98%8n`{ z1rB#~%h#$RQ@)oZ89x-xC`H&DjovVVtVEwpg9jTBC*?s6U_W%&xf&86s}uM0HF#;?nv6WCC2n5av` z;9E8=1zZ$CF>4TE3H262aw63*YG8|*thP}wd`C;`vaH9}wsSFe#U0hM8Z~=jY^Fs4 z*i4mrm4#ONM(Mr`k}lUS0G$LPRxDo1Qm~_i&P+dKI@a}j%4Szmp{r4fH9$F)6xfy* zNIi`dppB$XN3l)m$_o){#prcCNUK6aRodiMoD%98(iN%G7AUJsq0%xkqv|WqZy@D6 z87U`8rHG^)!-1|YksI_JOSz<~nJbs9Dy5?qb)c3nKVwbn1hs-OZdGU(1vS|1ly!4; zIw%*@%E0%RSgjPjWx_xQqN-w^QUzDiFKT+#eMP;$E<+kjivrD9)zdIrH&RahOVR3q zUVvH<2C8s7`!th5TD*RuN8(N)V(DUNdDFi;=$^2*T*8|i7l-G1r{3tksP6ka z)AiKpb6h=rcI4@;-_RmUuYOZwj-I-(?q`sKCU%NH>63Dcmtk>1;Ye6_C4!+CE1;u+J+pBo(ufUfRD)WV9k+hA(BjmDX7p;5uD zD-9QN{T9u3_x$2b3nu%)xf&Uum#K zr>!Wd7jNIZeDUnBhhM)yr(OBs<%_fP)7KSp-ml-iIr-+<30m&6mv2y7&;H5o`;p2iWbgxsl-qYIqjJ4OUx?Pvs57 z8)8%D!$&e=VFJml+zAXXj)}0qLdh{|XPNJD5RyY;<2DJr2p^>Z6Q!NiPCkGI@$|S) ztM6&`-PP*rSTHCsu8k~zUPqPF>!@;iT~tojik#eoJ_sjiO8GU8eJ{+!Piol)FzZ&V zAUdPi%2=E*0p@;&b_%V4FATD)#~!6AXsAvnJjGoLPiQcW-`m1_T2=R^Pi|dUR>y^9 zbzE2-^hr!NlM-AqaeYiWB4ZFK+di4P#DSg2N*qeNoG5odMouXfchEI6NigGIiEVqe zbVu5<4ex1TJuR$zR2H``Ewkg&GCMA<4s~AvRb7rBQ6))LdSHbV;YIM5r>2+WUW_3} zfG=}$!_Q!q_kz&#sUA-=BTmV@2$I^FWdqD$Je^rjOY5FALA!ZXnH@Djv!fL_CEbt0IZOz0u}+`)D*D57@oaA%2& zsDzIC6y@NMvt^=bw(`TPMYj`)5 zY8chbt@TVGem1se0`*SH=hSn8imV((LthXM6t|$nOcyynDyl6YtP(=sm?~5qGyPUY zk%0nemPXRv7g7X}&I{mNfkf5`1PYU~fIdzRvxfWQ%kSm1dw2Ym1ovSiuUD2|#W~0W_?#o`^nQ4Q~$w&+~&I zftI+MCG5FMTe*&Y;x!ft7Yc}Ubu8AiS9EQJYO8#)12O^-4oO8(ShkYZq$)(l1+~E8 z0<3nW4?TUKpsUK8sYH;2lrbAk3v^O-c>#A`^g+^3J}y#B{vc5C0uOpKC-WjXEFqN- zs-i%OMf6^VccLIGb@GZN1r_NpQxcd2pj-y-avTkdWSjv+V;@HlB!H}}Qi!f}zKN81 zD4bN+FsKf>Rf!yg`=v0ZD1y=7Rdn$J(0D_y^Z+9iu@IYWXh;%i1aP|)V9;JQ>-3yVF!cu`{TqVBIl-`~%B6!vA`W=DeW(!)aq%_1A({BrQ?#flZ_5YQ<*DTWrov8-b3T4!ff6|+O zsCNGLwKB=&;3yu9WNgX$`!C6L`Y#vLqfZIb`d&@im0Cv^I_v0yW9t0$ZTQ`J82sgX z|KCs6b71U~pS@3`oAlG!*S9~+zW;XeZ?Dr?W=>!I@{@gi^39LYFI#e7y#6lSE7J?x zq`s(z8hkoUptAcMqPTFH&}R2-uC>aS5+_92vp_z`9fyQ)Gk0?8%`K1v^F$bsvoN-k zD3weaDdUY(-@`v-Q;uVL)+$01l|1B=2Y2cZDl~?Q%9rH6pyWc+9Po!_L6j%C>Y3*N z^+hPOZ7TQ{J#Yu{Y(rM~XW`MVJ!GG$zb7w{z0C`>>8*G2DtrB)acKp2yR?G4U0Oj0 zjIG?lAeVZ07c5*r8(2sd{L28yhd`ml0( zdTG_-@=r_aF)S^=acTK?yR`hfU0Qzo;=A1UrRpb3OxRLc804SDsR{6eN6Z4OxXgBs$1mbcPz|Q+DCPP_WPfP3fytH-~Ol6&NGAH=eDYRg5nGAadoXoOpFE<`G~I!_9-s8&pMbE9o&%_Mdx991_9e#GHT;r#%R zFw^$lESkO-GQI);qhi|D{V7ttbF*5|qx+!BIZFCQRoIhLI&`S&hI&QaP_*b*G}3)j zU6S4@KS#&DsY;@oWueiHCc@ISR|(I?>>n&KFJK7yRu0&d zq-s-jKf>t`I1)^8uRnN+iWtE^MWZ9V!qe=yEQ32P_M79i^}f^j@RMrnc=>SE#bOz? zbU$ouqZxXd={%Jin`zpu-9;rv8@t+SPOLSy#6q_0q!*^KB`C7}#t!IK5p)(g;oJcD zVsv%FB|ucUu@4-eVJ9dZY2so{`&JTjw3~qP^DEg*X=udODlLFx2*BW4lsDX_bojd~ z{%g?@J5PWqgcX1+dx(@V>MeSi5I2Nj-pt|+N=I?h?*XxY*g3p!HipH*J>?+WQw%!r z_E6ctY&;5O6(+lCn<1tBWN4p9ZA9Aj&ZwB-j2`W?vQ@x z^BF-1Q~F%f%%_6$o)$6vqIx|D?yc#Sg)?1R|!+4Vp&&d z>ua?_h#wymxA|~M|5;_*=oK978~8seysk0Fw(Ol8g!2!I9w?F|1A7IJR zr=nRE@@;*4`I?ICZ=uJcMpU5zDqPcxHxjj<{z3SyOT|UaSCOY{1!q<;LslMF4hn_o zd7dfC=;3X_v0J`Jol%id`S^u(M!0Js(nqkI6jhk-z$1GC>rPnN?uOOP0^8aP?suI0~5Kz_g!goyM1_MH# zpqe-vjmN)a8~8vK$X%s1>Bpk;X<@!)Fgnu=T#)+KBbaO?WUdqMwH=yvG8oAkWG` z0_YV;1r_)Ud8tK1%ljmT%k$N^6-X2+q^~AP##;^Mjbd`uof0lm$Qvft>Qq^sg96oH z1sXzyTnp~dtjq{ttqJmj1nEH_#B`QvMb0%!Ng?HD3cgXpBWkG@zz{9vitv*@h$I4q z!M42G6CHuB%5#Oyxj9pW#L!y4{ju6t*3-Bj5M!%VOkU2hwC2un`_nmry9Aj zw(u9bm5S91%%fl}YJO@V)zuw5dXH(Vg4B~m;=u+w_gqKLnMU=*ub1@5Bqs<-@Y>S_ zd_*4oZ)a~_t0$`)D|LivN#Ss=3|0~e)|sB)HRs1)Yx zCt;7jk*%24za7lJo5x@EHefeA*C)By+r^1grV1$rT(-!M%wwXqf?|!9tz3;~H{)49 z8mxI1UcETq>{RILN!W(!xiu|e%Nw?Ww5Z?IX!}1O(r4E1^KACZ()`~L^N;7ZXlDsx zlV$1=g$}ih;IH`%oW8RYiK10BUboaxkGsuYtm}D{*KRWtH(lNy(NgHAV%z{e43|kB zm@aHbz<6YYDMAW$T^qA8@uGw@9LJ2|7NB|w!dH}W59-TRL`w>BNwZu>Y7agOT1{)M z6~jPs(_sK2LJ}EnoH)W77zQB6!#gW=Rozl3+k>cmUS?%8{8~faW!0Fe&v9!DX7k;v zLAO=WJ`!#laRUCOM;SUynnsWU{D6QF@=kL@0^%6 zdu$romWOz`neRm8(DQGq@`SUM3J&PjvJhIDETUe6H2fTQBfK8q&6*_NKgg;utfy7+ zyId8!S#8nwQAKe`Or&4ett zZ|YvHQ}w+{e3z7HGM{^8=fnoo-)ckiDjMeM0^TRWl$nhc03=A2$^yZ=A(pfxZx7Oe z0R00#DF(83ut1nA2FV|L-}A>;z0>%x_cj0j{IK`zu=nj@@0-KkiGs~s-1b!u@0qX} zr@BQ9^uwjkM?5Tvn5@i#z-E03lLl?&ioUV<@t@PioO zXXDY}!}wFtr(22zGT9{xAWiu)!b>$4>-A<15b7Su+PvUEL5Ci`nhQJeTLpqjm(KAR zH-%UiH?=?kT#?4(a-`Z3_inzol?~PK*cNHryBon^S0xIYlC{77|Ji%DEjgAmJ?z^3 z6v^Sz?7e5Is`Grn42QeXXaG$DjV8LWF`S!Td5G+;jXLehDgccu%7?Wy_E=+y;~6b^ zytpiD$)_uM$<|nwulOc>0r3(1{eMIrDl4-ptI^opBx@v+jmlGGL}bK&`2O#s81Q4h zpgkeGRp8e!I=NP@`o4w{Y>+E$ULa6%7Hm*0ZNI3yIVp5FSB_6O^46lEe;qC4%=#deduC6DE10F}P zHM&T&x~5~3Lal3~iW-3RG{UHwAg}_NKA5e1Q^#BY4lpol!|QlR!dtt5`?d@(*sg=V z;eUsQw-xVccy}3I?=HjJ-R$-C1o`pQhgX2X?{3|dMlv7H7Max^<)mx za)3Dv-{OA=iPv;%l@SEA?UigHqR`jNIkF3=hde0ip(t?vlO@WL5F$^9?b^?G7V)V1M(mlS7ZX9~JOINxY?wGCxP}R+5tIs(2(+pI zqIgt`59K64%~Bj)MYzKx!Y;Y4FJphCMn^3;5Tgn|@f{n;7+08(c1lAOm!Vl$!(+>L z8iE5GyX!ZID`}WNZ6GE4PV>jU)BNc!PXzv6&y2uAC;&f1bOr+gMyC*DPoQiFP=N+TUIRSp=^ zlho90H$nCaOtJweE?tNPStbOteULZ@x(_HBj5hoP32o5OhH2<|iMv`C3R-9b_MUa$ z^T)J*SLe@WI}kXJF@WZT%IDTj7E@{cH5^zIQze_vYqRov7$S9+J766LH)Y|-_WF9h9 zh_50kpLINmJk;6HdxSgo&TvidHLTqsslqnedvxB;mR>$94vSIjVAj6l|9Pk=>Q7BSi1wzA5kQoAUS5Ls9pVl-h5Fv4Pqg&bvCAaGmf)>gap!Np* z0(c@V-3NChs+uE&Y$DrE+_+(hZ7xP^DIaX;(z~w0n~k~F?bvLsL1`XteW%Hx??CGg zbT+UyC=!R&50Wip+>i|i&lTkPL~8j0;W0~quq%q3F!UXSNZ=({E+$Qi;?lLrVFk{8 zPmany@>@GOHt)2<=|HV$*BOddCmi&GXmv=sWOC2lcuFl><`Jg6efPCEv>3GQzL)Bj z^?Vt2G~W2IQT3Hb9dATONuT7}>WbtW8$9r=HAV7Y|KeZ#l~!!M>H*cqmYXmda4dm_ z1ki@_pUcHD>k|t5-Vsii7VnJlfMZpO2xlHu|LR$8KmRke_*-fZK z@qjNXerDDxmgbul%4`xto?l0&U!!P|WV<9VFGi=%L#UK*Lq4LH32Tq$b0(3ojsULT z%AnK=&6lFYs(>I7hdMxa{+CFL&D4XzIYz2}ameS=(fgMi zM0jvaWLX=HFON^_{IrS*n7`U#`OxokVZsnpQ1oS#&*cd>oFG6vR$y~hwp%^BY(i^_ zdfw+!gk~H2iG%}Zy{%Ez{LGy#?S@zGWNbHFx1+t?bcbj(jSin;lP#&i2IHhD;rwJy zZ>e3#d$i}Ay@9pGKKDB5%6YB0MXxO)vT@_O^4dZo+b-*Dk_k{biv%hjDE@@{0HO>` zlxmZv6_`#fB%bv&6uyDT2_!&(>++3AcZC7|T{EoN>n~4yoq!s^3|?qe)<6?tigA8 zz}tK*PruuM>vtP)eTS$gIC>JOo+Bx3M2Y1%B1OL11Gi9Ygs9*KVOJRYV7>!E$~*QAF{Ni6HiD`TxSrj+`U zN$5``lm6qlbEICzv?``LSTE{qsZdS6!v~d)Wyb-rQk{4T@KiLEs9Y+@kk?>sPUYh5E#}oVb1}+&;clfWQ^NhnLEaN32vzSWSb%t4td9htDkCXppII=^* zdZSMl$FI}r`2+s(OxgA1qkb`KQX$%{BW5s3A9Nu(l%Jx^AXn0hXAkF93ms6uh zr(`{p8x&$9N~^%un%NvY;vAY7bB~8hx$1zbTAO|VwmXT`+IF^j5Ho>))O+ON9VOxt$9DN+ z!2|LP_q4W}cyXG&UeG-0&EA^+c8rFzS9Z6t-d39F+6;!<@&P1Eq5g9dmUNvS8T-n4 z)k;jQmu$YO=}W`$;mAKVtnpVF#5uyvW68s?ko&k&f+;V5g;BD z%#ONQ3TA1m+QZC#EVe3tL*I+^AsHZ1TTg=W^`Ruep$`HuVBX`X8e4eF6y-W)aoPYe->da>;-@eD|UnT%w%mwp@HFz z$hZ{dT+5HBPq6^>7WN!eEt*rvQ^I^c4X;OZY>sB7)P(Sz&_51sndUmbVgls@U9A;R zw?eTp7C_ob;H?>-RR(4)gzD+SJu*2atT0Wl743tJGZVkBy%Z>!`{6tey?v)sh|^HJ zrg<1$G0VFuXfc7xz|#=LAOq1jA4yE1D!>Xl+mOEPX{K|Z`2quhMrhcSLJ{U8Ba<0Y zg25_^>f}+V8SuRF$ba?hdrO>*cYBft8>1X5icDUb%q~>L`LZed>Hm*8jq&FKl z1pERJPL(P_!dCLJAfL@GO?gaAReVKi(-OI{2CLh6G6Jx2rraouvnG=3TjkSRS%`UV zxhJ5yCe_I$jz;~T%pS?)3IeP4ji}nW z7{t6;gv1ZAwTP^lOiVM%q8H#PGO?A6q_Rs2o^&fwisw``yGR1i5*GIz+zcwn4-_?q zZowj*ge#7TlzC+aK`?+R4gQ06UeKIFshn{_K$eLXf;`Z=Kr1E;bPKhBrsxde1%p@Q zQ<)~DQO!F_zQ)ZY=ukuBW?Yb=J;a9scmLNYMI7tEIEYzb?-)3Di--^nT#-MgnZJca zwbuN-qXzoP#njPfxd5mB^DrE+?i}*DQ=@N)rmi8Y{&xHQfmWNN|MxBvS0`}aWFr)V z#*lAoKCvFsBA4jzq=7~w0sb5;ae=xf75lRaGg@-7&e1r(oVIFh3PBLA`7lnEIQC)6 z9EHCWF0+U&|LY^Ev6>liR9XEDtocZ~FIlhXOv)^2D{P67lP@Z_ei0cYcg;=#Uj=8fK#R+25f}WDZT-JT|C5DW-ogc zh6+zGW^Er6tM*tMSWU3L1BEFw!^F*3@LR4imZ64^@*DnZ=#H<6AL4b^#gCqN4h{%4}sUB zAEZ8nX?!qObNt8cSt;k$6t(B`*spr`>$@0nq1x@cuB?FDS)=#vZckz{oNMiD`E$6$ z>KZo8lGU9>2QGgSw1z6S2KOMc3gd~%G-!R==JjDV$%IN>fiGcxCdYXzp>E<)_ zBzPA+qsY)f3qE+?%ByKRN{i>O!}#awSB|ae_qFJ!cDFw4XI)Q$o^28r1h&49$nr*u zbZ1b5L4=5-PH?#lE0F+xSb|`UWp*j>+#}VIE(!`acW%s_pb1brT3G}hX-|zv&sN-a zreGoX*0+}MSSQbQnKI1?MUajLk$+G^N-zx-wu z>OPDsWdW1a8s!p|#UVLRWsw~w79J{?NC0^KGuUZCgE%IHf{Tz5p%`p54-_3P+&@F# zkAAHn^_^LRW{;j9UC7DaOM`2LC+o8C|A=652?OfKMuNn!(8}Iime`>Mp#5;O0X@tX zK>&BqOhyP$H?Trof?>!2b?Ij8fFAPH$AqAeqpZ)rLCxz-s;LM5od}<$&Z3vS>ZFtej=%5mMX@1jJXJBc2UX-T&%^tVX~ zLwkZ=?WAD19x21JrXl9 z(s5~&v~?80(}zJRe}C#KHs$tg2ZyzTSz+@sF2(FZd_rW6v7-t zLj!S-SW{v!C!xjhNdY}>WAyw9lMeQfKm}b!=3jd@ToXg9Mt?PG-|%>oo2#@MgY-j^ z5doo zpM&j+6%9oGO+J?Yfh^9USI!S;oqhnL{0+HNXf?(Oz#D8DYA>H-57sOC8pX0cIFs3R zeGMFATgeavDi7a~KwRP=W*56??}*b82DwlqnGbr2|1QF)o6YSOWc+!b$1=KR@QNw; zqJPghrHGS@tCh<;42!y8(9hxL_E4NhsY0Blp#1FL+TikN<2Rm~ajNp#01z@$u50?@ zlFSBG>xa|E-AdF#K4ao6uO3NwrV;=jq=fdR5hLOSl84aB!j-pDQy2=l4g6hM5ZaL# zm*VtU{G!X!-kpiw;F@mZmye!dsq_k*hek0h7}$ln<_r%?{_`nh7?eoMXbwKcCT<5N z<`jSF9sypM!#VIKv^(}9!UObr)XG$31wz&v$7013iR`~}1%KcPp!eTqDkrDXMqJGn z?Jk9GnwbwbsawAw-C5D?GY@Bx&nLJ~)!};slO$JU#ajoWjwWtB5pIs0?kRI<4b{KP zqu!*5nTwxu8tPGY17jG!t9lWO#hH+9Mk{5OcSrvqI)VdkPA^1&GUfckZQ7l~SJRyn z^hY)Azj#V9+O)Thq(^@%Q9?-)Nm@#?D@QWXQ8D)T(>&fvF08Z{us5M2Caj4=ZbGJ^ zFGFE>+6>|bn{c?>xF#96&kT#<@(0vjIQ?An6i2^GGBso_>(3irx&zdiVv5*5+S z38$gRAhztYNe3E@KW>G4+W}VS4W?L! z3Tnl{4ZPnnfOIs?Jxp*6Cn`d?K8+n!uyf+?N@3lofh7z7C2t5u;nZQ zG^T0eWz}a1gEfq*Os5h`%?@NVx=)VV7on1d!WwYNA3$;S^L?F)g=!CMUa3i;Vn#P1 zY>q#_acr299WniL8;3yKn*a9+6Ok? zNYj0eAcOSnvkT^6`!fxSH z_8GESof1hc^-!{ZfqR@^gV^rzd)nam^+pjIdZO5{JDYHwnTJ_rUvZS` zP%~Ur``OAk>iO(+c{BM^YFUjt??_SQKu6?~ri<;xT4j+*HycG#U0G|ipE57GDTVZ` zV_~}R$)u*K?fOFrJVkN^7W41G2FfI4FmIkoC>nO+k-&ub9SW6Vc`0z@e{DqH6!Lqm zySDra!+^x9qnMiJb505a%F16yLv~lM@#jzsxUFi9cL*vMXIN2_@B7NFDjgO#$gE>+ zgWCgk(7SqB7ulTV#;QN$D8W}B<>=Ex4uxGazLw_B4%)3-McW~4*@%x(5_Lt!x7Up! zuNb3sv1axdb$R?hkH>M-Skg)nW1E* z+FxrJ8sAPup$#EmGK-)(xf7m50!~G*dmt$@Y|Co1F5nTYMG}TSmLA#;1e#Fd znS%1x0fbTcmQb|Fe+Ilow#d(XkMpt zdg3p-OC~AEB9*s%n1qk;UpVXyNmGVQKv7Qx!RO2y*dfd*A3~zXmy(kOYm6fxiB>^oXCcRMblnDPUE?O4?VuzU zX+|{WJNrwEb}OF38I5#ClhBGaCTwft=3Uyl=wWC{7lqWIrdaxGyfOM+WJN6Hg7}D4 zl97Ws6V;AGef^-BHu?J>n;>qbT#v~-v(O^d%_wpG9A6wWEQJt!DaTQZom9PeoBzsf z{o^Zv(TmK}|9L+raqTV6Y?kyKnj1Bj1#&y!PYoP0rardvMN0Owpg7B8`iG^K<%b(< zq=utyk9>ezi5y-fOZXrdLbpcixnVMX%&3AdN;{%S4=?NYsxpjuvYO5)n(otZy*k+* zX?HX@RZgu^P@?4A*?vBXizeuAAjfm~XOqmskUz_`Jp;4EB;}8y7u)okQl$blu#HZp z=*v&Gr0|~MY(=qyoHOFrwD?;jIbEKJ?Fk3RCvuQ8%zqE`1l5gi2k4M1emjSXrZB1Zqo#i;a`8 zOgF5L6OBM!50PYV2bF%tN9fttMthEJ-%z-JSbf4pBpzNyf`24vG$#PK9u{F~=xdF! z9~vAa98ln1zpxo!4}(+@MjT{XYqTsxVW|B!E4TGF<4UOkUmZK)NCMmf%7zF406z^v zF6a#r0$h#1uDnH`U4u;D&dwgrE)L4fN6F3#I|DR~23Eq(JC7?kuZzj#UNx;ax6RKH z+{N_cg=xb3fWDbP@6dz?i01CofW6~Rh1!54|2f1J=pVP|b4oL%DB`1xl%hfAmYceQ zI(lw>W|trYO+O&-AtLYIYoqUz?w|65BiC_@?V2^+YY4 z)OXeyS`!BFQklqY$dw~Gt%*(*Y~tkG>skAt-fZ|OZMiNJ3G@JCfmM!4=&U1%{9fJv z8-ceZ-|{a@mdt8)_*rUB1OrY%lm>eq4>C2t&KPkr%OMPU@^I%9syW9+qo$M;=xZcJ z0X~k3*d{8}QEWNd_;8luQg4egIbZ>Y*vEV2n%OT< zNYk|HE!92J;n>>uWjuAK^m%*ZZ0(zN=2Ing(0ZxWvSV3P=aV6T;!Iyop z97SzJBZo%{W3BYTNlejI|5@6Py6dYw}W z(E-)883`!T;NY+Nt0$;yO+^;Zze~HVi`5~n9_yg)Dx}uGOIMw6E^dvn+_ZtsX4vKX zp+gKy|A8-_%u0{*B{%*`@p3?nXGqY8#W`LxLV?l4DD#EJCTJ5_NpJ+^gl?>HF4bQM zuABN$gnCxdp@Q;Z&Kzs_KUFjZTtHg2$SWkB=`{N|tj&F<&RczE$F0oUm!&vhiajd2 zwgZOBM80&&()g~=LtJA)2>?s@pb7uNFI6Vsr^N^AW{PF@+Y7*|D^eZ$V*Lse6;w#| z7@8!8GI>iq^#U2Dz`}#fiu8XjBk~Wxz`RcP;6y$Qg)3?NitlTh$`;?qqwG^f@sl~4 zI5A^^!Ty`h7$L1soUDQC&W{%m1@rmK;71fmAb&ytIPMm=wt}QT$U;K?Eq4RAAsO6` zBq?+MIRg#6>SMIb)At{=5&8u>T+ZmjC{0+HoM$F;A4?1`yqDK7bC*^6&HrPfLOO+n zP^>OChvUxc%t!Qhr1z^=ks7f3gaFu&2~FnGETzOEPy|(U`ZA2*4n;z9;-C(sFzf$*6bij?WT zYF*t4&WkaAhc20JR_Ne{;aSHZs=}yYVY&QhAFvC23JAdM18CacW5xfVu7V;U=tD4p zICfv~3@Dl^;TjA8-$$Q=9O-9z2B2fD@xtqHc|Mb8bKd-K4_dsVYjEA1{RuC4`Q+}~ zS_fh@_t7d+lw|AGr5mnGzqc#zvTZ0%A0Vm5UW?_QjPz+CN|Ar1W?3dWM&1qlh!KXn z6^xZQ|9EBj)BlHZe>zzdK-vl+0osR1ZVD4mtdm4jF4isf$% zqedtZ#Rt2I2OG>b!UNc913nfDfGnFtt!E@A6_Udw))YAFc_?T*mnLeSi*${L7Ty9x zsxkW2q6(!y!X^dd3RXH9mBt@gO7+J`fFU@~A7(E&3q$QxyU%Cq62M^%MGI&?pzOe# zlU+)RoH7Ow-ZNB^l6BxR2&W;3r;9;TX7$GF>PaykM3;$XFlQ$zTxbVM%u)FhIHWls ze9|#Lq5N?;L?py2cXt5I86HXOQzmmk$*Q{) zEN!~`6m3**yu>ZHKbOaRvG&sTe2F$eSZDXMcfgLeOSpRb>KvLK+BiI0ORuFg_7Ca{ zwDPcp0@rv*AcE9mVp3V^VTmq=xc6Y3d6s{QhK1#KgM$(R3y7Z;7V-kcU}(%Dt?Q>u z2N-a`EUhxj-bh%5tgcm)tkDHxw)tE|?WsfgFS6+3QSx<(n{a8nX6<-}eYX^o<5`Ej z8(lN%cW6`eNGpw!YbXWR(?Xh{8=yc!=NX0ws%F-spyXMo5f&W>)Dubt6v*WB=LG^I z+zZvQ>)SBP-?;T?xUhxne!nlE>pM>2okX<{dY|h-W)!D0g~JUVI><`Pm!G2W#R7C1 zzEDSq+mySYutfdoX;;BDgDp$N69fHOmWWQD&rhQ?>w>;=G8uH6dVq?u0iLntaaBtC z%ez@ttaQf4F{L+6;N?fQtCuWjI{+8)K}+E!%V?NOU|EQQvaZ61|2#6B+O1^sXBDh6 z+Dlwk)xSGy9-M#p(+(9MMuQ3`8hNKo-bGjWj1&s;E6J>4navEgjou<=dnswG5GG!F`n*R_JzHy{Cy9t51G!ESbqzV|7tg3y$>2j4lVkJg4;Id;DT5ob1RlcKqkp!A3s%yx}MDbe~B9_L+0TQ#mLw*|J@8bW{`Z?DLqM!+sWE_ zheXGD-yR(qD@Ee1&6HVw&h^72xj zcJdE8%ut-h84#MxZ7ZBsi@RTsC+vo}cFjuoR{<|5!w?xYspdwN40fa)>H~X|NQr2H zDglx9)Wdsp16Y0)exJL?ix%B9<$=keg7BxHY8~;c#m8V{>u<-#;(KEHTThYt%iPLe zGR?rGXtll+vQY)dtfw1Fi5Fw$=zukprHqH-_qwdCH)lyu;gD@Ihum*1!KmZRan_tO zw{eg0N!`R*FIYX$xL61Kf*Z37BEaf z;Qve>W(N;Ww~}V{6;mmD%Is2Mp0MALUw;CJ>C zzMA@DJmrq9g-J(keI-GZAB><%w0C2lFmkipzf!IZ4VCoD6@+qI`j@jA~T| zLx9{c&b8H721yms*8EEXW4j=8yobgA>@mWoMu|BPnRE;bM`{R-SW1sS_n+}1M&(hd zhmhtYYl>S_B+Vfv=<49()8_7J((|uylz|4O=&4-dA$ej=6+4Okl-G7rK88CCX2yLl zb^R4P>^LLZl--X}1tw*+>`)%`BHH$r;6_?uJWaxBl1}l zBZg*ir+itRr`72$AiaKJh?Qjb51$BT&+&#LEy!N7?jmX`6)s6=N0>tqYIA=dg$N42 zD?CR%5Ol0M8D79J2gu|))!Uv8181FOAWns)h`f?K9@OLc9#*V~!&nDlWCP!q`Vzp~ z9VT$H3L~7Fm*?Yr`Erp(!Ag&HKYI1^p%ZNsM(Z%Qw0Tga!8QHQ_F=AMHMisxwn7v~ zFc9~|L(a){8LSe2&cZs1Pv<<6l>gEe(% z@2m27n>4(ZkLMYWOvw$Kg@J){KyN)L;T0jbu!p)u2~h5SXetWS?6Xkd{!rhtrWzEQ z;v7e$iW?lp-&X|=HIc$x+qKZ%O_dFpZ1{@<_Fgn)%d~uoG5b4L6;z(Ec~PayxfmRx zE(c`@!OdCCBDnmx*)4wxAfta`!pU~fRRK5c*zkPjX8Gwv;LZpj=218*HHCKAdh;sE zS^=oOhFQ{~XzB(K(G#0rD?mPOLi-|8F#2>&u^OFG)8T*$>-@GBeV^rjJmf!*clQN;e_$;) zqX4Vu7*A>nI4FsL3RsT`HM>6yUL1wif6I(VjP0XCr%=_LQoB5ns0G6PJ}b_y_k1C5j2De z9CV>nlNDJ^BoPq)QHFMtI0s?E;HnATAxib;tfZ%BPN1`chj*;+jMPtIPANV@#ws!p zzJDv;Gw_a_B&`l8U6e!@rCO526!l@$wBe)F)gC&&QW%?uy_CXD;I|4((2gM6`G~c@ z%O#JLM$|=0$tmr}RIj5F(S(@2O5*QQbbvU$m2*~AeQSb(B#uqS&S9Wr9qF@Fb7+c{ zBHc1rPy!<#LP;D&7^S69S(MpC!qcOrOE&a=K)2?zt>JK^_mbmLA%-U?4`aLug_`Bb z>y9azd{9fVOQ^6yaItX2wsEM$=)2g|Fqzav3)?@O`@ak9 z99i2-7AM^?yOr6KV@wxZI+|=30MetxlcWA6$;$_?$f$O>rTl~+vI}bmW5oEpvvGGF zWEq>VYZ)6|7Us0<75&sAFmbG=eU9@?_iDlm{D`O3s@GPH{qBTPk16473YT+2cz-Rh zMK+V91#7ROol{8pg~FYOI#KMa<2YyC9aOd*N5R1hl1WaKVZ;Z?mM;u56;}5)4S!@h z{)U(XHMtB-B%|h@jGu zv?WIt;Y!Jk1i`z8T1m8Ly~2d-nwBbAsOm|VvKn%o1WQF9y()5%$TKRUCh_3pp_=ch zIm)2gL4w1%tgDO!X{P@fk#D2PkSmdQrf!w#5x(o|AC0=%VJY*&CrvC`GZ(1?+L|^X zCUEr!nFu&v>?`jr$=HBKTJyf2>FZ0~v>AZp52^W-oMK3+*#trv(yc9*t}SnJ65o*s zK&_6j?9O;uBpO|#PHV2P$k-GNF@uDsYlNq^urn&LXuYkQl7&OVsk_vJ!geRj=OCGE zfa8ei{}-kKrC>C7$kL_in_g3KrZ8N}c7wHq}|DC5Wb5w)=Kae?iQGIg40-g*_>Ps#3rMm}W4T zO@ZBB5r9HATpK3QfzmHVQ6nT-B>2K@Gt@@HXP5=3bXfJ3=NV)fy8Sm?zk>W48 zsA|}W$VtfFLsJZE_BXifgxl&POuy6;yo=KlVU1R>^CLxzKHoHqO9DwLy-_aj6VFFUwr?cN6ioLRl+ybviip#viha_s z`eZz>YRb@0LQy`hA)bs*^EW4VfLIdCY(k%gG$ER<{}x=B$k0tI0zS>e#}L3)l~WMHCppE>O3|N?>vOp1&NJtvffDal z#bSkKWKZwU4E0{fM2A~j1B5FOf0NFpmS8M^{Q$ic3PA|eniabxB~gT8g#c4KYJAzT zy@bjNvYOa&obe z0%N+^2awLkEig*zo=h|62r1xoFZzACe*P{Z6C`nw$1p=57t6rVBE9x7*lDrxfcW*J zZeEuw!0X|m_$?)e@;*3;5htHl;Wy`=Qnz9>cX*1yK!7IuK3;|LB)C&|ipgR(KP=Bc zFQ2QA$H(L0QQ`Q-M+u|D$Ln+db@Xt2&h!hLgg?-8%D@Ekuv=$g6RV$_ovk}Rws%@5 zN6=HelIlBjnO2s*Dpoe`SNoj#30XHofOlWR1p1Ja zaa(qN)LhYY9jI;|leR^BFtT_Yu9CImT59!jp_@juw8P$V`gV@wsXx6CyUNOH^(kgW zS*Z?uN}c*BYp=|19XTaDI?|N8F$d-@Bvz$bvg8c}Qe~~^H-b?1=ydeHI8Jxm#{9+9 z7J#P4cfButrbR%oKS-sYL?3VVy>b2~dEV$Nm51dc|4`^^UsFZXy^o$!CLqAwJ7(W} z9yyP!w&x$ujdwqGq>8+s6kwWG0yw~rw}2la6%QM zB<^T{vkgeTItaYkw!h3`rC2E_{z)whuuqei*%e(ww^&Q-*bDml4)Lb8iC5HB>l*PS z3v;hW)>KcCtl927`DJb^M1<`g5u$lVt}I-%uaS`Wh}cdhR3j+kPrwv92ph!Pw*k<4 z&;FaiN+TYJ99=#AYN1IFqS;Be?0?9m|B!$?!(Zf6Oqa1I z=`IpZHW+_IgjDwwEr#hGv|_C{{@u~iNWp8r3K>J;(d@?AqLiv#kU|@%uv0b&wZL`d zpVq)+aF&K7>g(=O(7m3l?ci=^8GAX|*vDQ^|Lf*bV;bderaOVJRwGBlV4b22UfV`b zTD`W58o%t=G$NGw8w8a87H@dr3g}FT+bS^>`wOx;)v73x9Jo5ln7@g8o`6Es3H`mP z0-4~zr)wz$9=YaoyNK>|@wMQkZDSvGZTqjAuBCPOrDbggeG_!-#wfwVt!b=_N6Taz z*ra+gmUx8G0XcbznDZng*1niqR6rSQ8d<|!A^valP_&Kz2rZGqLyYv{7(1^Q8@Cyx zd((}(SCox?!u9`>q8Auw-Sz*Auo>_Mb}j{KvMgluADaSBH+U2z9y!PklZb@@POSQ~ z;7*3{C*Y35nI_;^}LfD)C*?{^I|Y{eNh&%j|E{$z`@ZkK1@-5$ggOPZo-l zDJiD)4gxIz;-ax^! z-2S&Z2M35}5#Safq{XbjJd@jmuAp`skqP^sfT$CM&H^FX;SZM_;9-&B6~Lr)5DP2# zuNsz7i?1O&KzF{nbDCQlUtCvqW;Ql8y?Ctc&A;*2G3^Hrrxd)cz4D4uYlqPYBkz_S^ffvZuOPd z+TQY-uBN-BxwYvfb!BH^LtCr&F?FRCa(~0dWE*L~bO&jmIjE4tf|!^=k`Ozy7~uLO zGbg9vis>IX;AWeyHiliwsBZ*Xj1uqmA<%Be4g4ROhw2ROEeZvR+4pGN4p*pps8E5bs%{E1-2J?pz z%=7`#IGG5nhbiV9)v41wK^UNbZa1qbTw;*J$XWVe94TrArbI?&RxXY8laK&!(W4kd zx+amj()%xO;0ApR;O5tg2^U@fPIC~Wa`4Qf9IR1;MI3=6)P}iYklt?OseTa z!IYhuK(-;@yAY;qTGZ_%>|EW$fIv*B24QHDK$Ov87Kc8A*$ED!CyITl{WnK{XI;|# zCx_9y?3a+tNn`8Gn{Il{&l6*BDS4)K;%%!ULmIV$l~TL4UGe&b(8BWcjPa$Cn<<2C z>|v>OhcThQS2*%4=yucj`G7Q$)8$YuXxuzB-DCsaE?hRNE4W4hC<`310GM)YE;h7P zYq!C$rE`CjvUv=9AUr-vfC#&L^zel4Jtom*YNqSaFoJKS5eGP-8io|M#z2H7(=m!y z^rp9n>65Cjr8Box)?H|Q@cP7`omj&iMvgC@sWQAV1`>uy`A?>q5FSEEWWW+|zHHT3;AO^c9Nan|T0U$fYS~Qaw=9_wIhfi9(Y8r)?M9uEAAf@H-VLmxdtt zM28{7l?C^;=`l*#QThb?zmP!M#%lc+`2l-UDW|R{O5z|)(KUG2vWyp)e#X?5Ot2Fx z;w_jmejUgw*{%Egi=oV7J^{H2eG;1-U(dmJ`Ri1ZMSCAO6Q7hpboo40HQyI{$eH}Q z%a$QFouoVb+LrQzWAiY8LpF7b#0e?(;|mmPi{v5}7CQ>sE_nb|m-gO&)Ay!>6uWc3 z+j|SZZ{P+--)F&H#?;^0w>W)v{&?=3UDLzCL8%+Nr$8H;4jNTGdZQ(N-HYV#ZDHVf zMt~3Diqi&e5e@iS`jb}rc#e4rCxJOAv@L&jLf(}fEBcGeFc8W%dn+DS@XdS78~+Y} zsqF)kJ{xu{(J@>zCO<^Y-JxU{=;CLz&DPe2*9|}4jX2DVKM#9?AAlJ)IG-o>_AdRw zU^ShGKS5yq(!oCm`GOv`y<=BjwaIE1VVj;A8gf_(FR|!ucZ$adj-q6e>1u^T3^xs= zVaihihG^r!z>abyB2-B+mKGdwOs;3p?rN~E22`YrCp9tV)fF-o53@y zYahH~>%0`rl*1O4Ll#9c0|GpgB16N{1@MxV2Ei7q14gCb5ZMg{21YRtUt;h-jUk_DKDP^?Sb6bb^#^Jq1 z*1YTDzfD7Mkg*eYoxu;7VU}HqXKq>xtQws#W7#(kPr<$~K^T7?9awJ9@rRl+=a_R0 zY7!(|8IA<-Gp`yrTBJ)bcJ4UH@L&x%LUHxT5^Zo+;T&0CL;V;{=ZuG5O+T$L3u-h2LU#54Jp5uuT< z3lkDetF_MGclH?Tn~mh2LaObq6ZYIr={q_(W5`ZhnC;`G(6+p~GFLdvW_l2-NXQ8XI(XR<{jAUZ38M`=b4(9y`QMs{H!azB0SpTe4eymd}~{NGN`%D=uNJ zrEpbW90-k^A&pRHAI#R$bKnw2fq!fn;tW6@v%Un+TUxP=ni!4Htw_Y?!dKuJ?bGm= z>Q>-b{+r64Jvd2dz(?wSsGT)X-M3OK%Vz>y71h!jqFy9S>aLY)Luc#nGR%(^j7art zDsDiUKW-avgCI}l^x6(+eLmvkKR-SKa_p~4Xdq~1<~ubaGVip(x;Kb=1|*#!SmZu~ zWbKMz91m7eLFa;QTi@cYSmL^|^u~5>q=^f-L-&qsT4vcF*#Jwl7rvanOwzJAL8|c3 zfy5jcLwIxQJ8tywRk=_sknFdd4^{CTln;u7@g^gLCIUK^p=Pd1Y^9~-If9Irq_l72 z=u$XwC!H+e{sIvDyWQ|otFlwX8s&aPAWK8G%0*j<;bPj z^RYi17-i#Qp5m)g_<~dp&I7Uf7}kNboQ>?_6%mJD`wmQJ|06imBb%T%%`a=#RdWmUCrb|Lz9B+d}@#Y>gTrsS4QGC`{FSSl0A`f89C@HQZ=i#j-hTDH1tD{M!vn{~`}WAdxG83d{Qpgl#R? zu0^l)Vx;>*Hu*#pGyHqt5!&y>Ca39pApdg5LG``{ra`$w(PT*N>7Sz>F;h9 z9Jk%5pmmM?jJVdk$G>$8dG$`k!{?*Wet$=Xk3PuHxf&tuoXhc3%-37u?cfHF>TTq*Vol`1HB9lDucuX?I`UVB3wSG8Q;)ub~X(j{?`$ z@j2LdLGeT}Lrx;TSHuy~OECc_V$#0zG#Z0FVUnzvWoWerXJU|lM_B7!d2#RdSMr3& z9&w|&bPTT9)lcj=VyuJ+HFJLFqJ4@4N$(Szw@(*nf5Tb7ia78p(ha?MY;aJxz;Mt@WU|}s z&OKawE3u59rmNCtH!at^1!F=Jj&e>yH0D$q3PO;=VjD)3x@vu==2!_;$_&Hi_l+48 zA`b(fVLH|@2fht$am7b+LrR!w-qZ2oq}%%Qz#pG!;BS(`-nghau$bLpM{zc0Z)yVm zcqF7}t)~OY3nKkCK|Q2L3gjq<{O%D0`LrT1;G$OH!Uca_lVU9HJ+BSR3B11|Y}C7b z%_q3V@`F7o0M1qkBJ^M$%EEwg;eAwIM9rzcYSF)|l-Yax@<{6Gq$KM)13%qprtZi6 z{rz>mFLg}^$M{6iP03N%r8|ya`QHEJ4*jVTRX)2W)HAxw6zDbf;j$Jj+VVSSVdk~MC7LVI`Jj}U4WcYBV5I- za!?=n?wME+BM3FI_--F}NPK$k)n_g>ov5o73P_g&5DU8$vR|9HqRHMR%);pUr{5EA z4)r54=v3Seq$O<)5p-M5W)ql-DKoS(om5b000PoJ4H@O*wQ-Bgrk2O@0WuDsPk_(* z4slYSJ~nQ~YFzJyDX-U4+=S-t*>(KA@^f(C(|N&F?%v8JGm#DQ_Uis`V`n=VsZwT-^WThsOmpvR<@Etuh4&Kv8kW6pNy{{T%uvcHK! zq5F;l+&<7*!pBFc;{#1`OxP%D3kaV9ZI{qVEbv>#NuZy$0C7o9UJMaM}7e1g|ejGbG zQ0536lW-GiBrKFchjvU@_ayaf$fCo(u!M`SEhITn2;?TU6~Xl_$2IS9MR2(0-q^1Q z`xW6kw<2r~NyAqJZ&bLv4ype#T%k&j8xGV$(md%6eM3mv=u<6tV@O)vuudM7;#^HW zk8x6P{^4H_uO}jr!P_?se4@okMiKHNS&Dav6j50&8*Ye6^8-2ZDTCUYig?ZiR~=K_ zBFA(AZxp{-Z3R^`sM-mDj)s$E!D921A74DbI5_zJ_qE3=Fmuo+RM>LJk&0KR_~!+9 zzmnxha7cz5f*;kN+CvP~a#sBTxV1MqO6&+#wD9{U*Em69kSe!0S^)oaLru?Qh4C$~ zoAh@IvYE;~FVHdaCXWP_@FG+ym^rC(&g0<-!3g==5vr*vz8z~XF3)kzLU5UK8L5dS zCJiBSiABq!u!MRe=viwdhOnE5=WnNrs}~n#+~rI4%Y#e!`ox3uQ6ZLd#`8(A7DlHf zs?v5=S1i~0S1&I6FD@<)%jw)9Y_fLMj-itE>l?!(dA)ELqLyC!$udml!+N!Ff>a1V zhcofzHT|1z-%>MdplZ(B66xrG?^3sEv^?6-T@CBdRU5*q@!ZZv26EA;WwR4v@JT^N z;gXKGAK4!0d>l7$e2`@XfolOIVxt5EI^V8m0V5s@Xd(GtRHK5mfh_UX(Bsxex*fEj zR*VeC4t3!J#1c7_DGyB-HGzn{_#_Kp$7$ExbZIzLI9I#v_a&w3$5D}<;JF0>;l;N> zL0Td5!1uo)+s!{eubfgn{q@oL)%mN7r?s^B@zaw>ubxpz1TTQ}_|ePfy^E7G`KZnb z@|~$c_q3gR+Ri)Cc7A#W&0fRu{GGSg%}-Davhk+hZe17c$Za?=Z-*V(+hIrU4%-X^ z0l1YFK(_`pn&9RLZEVCL)yG>PuZn;ULk(9D2rEEwSB%mx(~O-E^d50wpc=iVfaCeL zOZ(^j?#TLnN8azq`yF||BkyM;J8~PZmb1f-?Ch{3JDqoA%NAfmh#gxGeN;T9 zu>qnYLr)?Pye~uF8p9qq&l z#o?x0n!K;}GQaU^P7Rz@l)RDf+1Y&fHjHmcVS?L1S*E;U1>k*7dQA@?JtB~iZdd`f&5m|6>_-pGJL*NWLSnJ!d;O*s$vu{j2Q zm{lu4iVuL&lB4PL4NIQzX%DIx^R`*>>tFo$|4e%^Q57!nNU&@vHd9QOa=jR|&*JCRiH6;qMkLdia|Q27 zeAB=B+23$w*l+?;L1M?oNKo3JGvVMy84YQ$<+_9kb(Bfk@K7r5Wc+E#qm^1PJOU)+ z#6QTBJsE~vVkQeOXx37DsM?EI42vl}1FQHj9^IA`n%HP~(n`QN;Og96XzAz;J%t|5 zeTUR8ksvZF;-X}hk)2dNAZJb|KF35b7xS#Ss zZw|pP`9y9NURBoK!5{*E&Id*82pOVGsA9X-eUG`rWnb}1Ee8dk0-e+=UqkM{q@qOf zMaZ3321SvO^EA1Ub#yd~$xMWUn#kEuEVti7Z5gcxm_$5b7WkuhjJyi$uaz~7ZeE;_ zg=wW1>suff;Ehz7qO2Lqc!5Ky0$wak#%P2*8ZQ_0RW8(;$hUin4d6E4W51{U`WOG= z|Euh&YwVb?eZc+05*JNyiYK%JFzsj~j$-X_tum!*1+iUpn2%jyvTQ+G9M8iKxBcfK zX4ho>;trsTO*hyPf3fjao1hqcp(Za>sFP!2_8)?b37>x*oH6DTzg+9Q0-;eWfLZV5 z2v1jaFNb9T%qoThI2s!D&0L=-+|aw!uWX>yMmR`s30`eAm9O~fmT*?btGi35Sfrhz zTTu{#+sANS6ZW_ddgffd~LLZr(Cm;)ov@q!qNzyQR1AWdk9Z*A?hyZW4+dFKQ_k=$YR zzXYKjIz^YjFJ#|5iZticggsd`=-PYX289*2dz1$Yav z8EWb_>!?Hid5{PR9aPuS>(p+*_61QTlgvqUT<&eu_3x$^mQ;7C^fI3wnE8AtIA|j1*JArd6=r7+o zTXw0n!)x`EawKa9S(ejx~QajR|!23iN!YVgybEXp-vVL_r0Y`DToyeREVDO-T^cVX=BZR-2q z1IpG7sXh7*g7(iq5AOIG=)oO71HHTCr5zg3j!j(O0}Hxg8K7$NL1GwL5xABUWc)1C zb};8m&qzE>xd76Hpn8fRK7vmosu4G#9?voj|^yZI>>Z`C(r{CD9X|-Yyu{9IL*j~)8K@L1F~_{y8EFOyS|AZA?)c#Q1w~; z44~!-D$x6N56rqk%l}Gg$Em+i_Cj}pE!3oFvWwa1oq{JJ)CQ9wkZV(Lp3<3OWWS+_1O{g}x6qT}Rw1~St%gf8ajgoj5Fu4@a*ATYvsth>{g2@Y6&WffDKd2Z zA89Yr6n+l~w0>H)UXv*mwBfA^=1Hon0blkPVm{T*%ZdL0!Fj4pn^Pi%DU9V4X&vRp%9}oB-m;u1>w)&8^OGk~w>*0yW1p`@bS_W4pv;4INkWYe zD&=70Rw;%b_=EGg^arJ0&+z(&^n_HH6albgL}_on9W(!ca10ke&>k--I16TUqFSru zj5u&hlLFe3h!PjPYd;oZrbd1EUW_o7qp1o5FI4P#a7PmGT;-=tHNJkh<~3FVgAnb- zJB$Um%n=E!d|z9Fo2}P%h-Rw8f>$D9!UFN%lm3i5Tc63dj0VTYuEzlD*QJ=6%b|_QYuk3 zhofw~yQE^l!L4#r;${!7*Mfku#5v9B`?Q>$DoJ>R9Zp1qiu%r#wv<>DVZK!R3aMl$^p(-akKa{7JiHCq9VAED?2-GmymM82aiNRgs0`g-zL7IBl3Vg^F2cC&2 zq7w$ITaxYDCc=nyIpSc`o@8y$DjBP8Bm7!42U2zrB1M`D4x|yACZwrSf#8iP8up~z zL)>wPx%;-l0|FSyXvJHSKdtA8pj;HRZ5+knmW_k%s9}8~(qVAvhHEf8nz-s~enCss zQ!<8+Vk~~NO0iQ_EdMX*2E2;F8po=``kPIfp2f=yK-yQA^CTt)z@})8dKXIa3XCtd zss%X*>6Cbnu~;n;e!x8;j~gq_dM8osci( z@o2iF49-H)U?|iCOL5ZckO)w`R6L*g=A*J-`Btj1B0bJpvA9u`^D5J(zyOrxET+r` zBksnQHoY@jzT!EH7I5;jSekohT4~Nz&7PXOfvZO5$&44Gu&>;rX zh=fUi7uXF6Me=?oUx*Ps^2L_45Pgr>gvCln;gl+Uvg(Mr#5i&ujCeUJm>0=1opBQH zalzz@-q)^21X6l%K)YZ#>Jy%c1{cz&aX~f$AWb}+sr?OSo>h)>M;9ISIsK&_Y8f^~ z{Lc-NL0_Oh>HSWI0i?3&b`=YFiO$8O6w|jD!(`&L*Q*rBRf$OfilKp z#ltPfUp~lost+pr@u2!4soZDoVD=;hjQj-i$FX%Zq3(;rd7^zzgN`iX;sadRC44_% zgs5qmbZWDLWBeU$+|tY<%LS;kB)7Uk?ymeqL&xR(3DfxS2YkUJ`n`jX$VA?8H?QRC z)Ys3%99s!p)!B!;X-UdRWe{9l$AoZ%wmgI!}3$te{fX57k5xCB>}0 zK_n3pBTkd4QQoko6kf_4kTueT%il(DUbLD&`I2~v?pJaxDP;iV;7ujvJp%-IgTeMQ z3n`LVus}(iXE@0f{doOMz{O5-2V=mN0X%s|{!k$$KrR+<;p&QmDCCWg9v3Zdfkhoo z-Y7`uBqW$peFc7gdfB}0J_eYW?)01`9@>`OwzIm@KCSj-=aqK-vK@hk4L9hZ&4(q_ z*}7!Ao#1`)Mvbeny8*$m{=Hne8*m$qr|(doin3D^`d*<(2$2CGM(9CT$P>bjEH5?^ z-;7f$b(}cxeLW0Q!%rL(w7O{&y51^vtm}A|<=gizFXXK9M1t!!i2)?BLKN5fNFJI- z0>axU_5~D+y$z05Q6t&dZy%50Yn}u~Tkba4X%aZWPLse1c5t@=*9lT5NX#_F?i&~m zdeT{A|K z9Z(d<3WC}w+O8h>_dAM4z0&+sj^bvM<^Amk>g3@!(o8A-xt=e^{|`3gi@g@p5# z0UXj{md825al~c`Ha}D+)NK}2GA-cUBcuC9ogd99J}d9=$M7jf;d}OoS8gNYvLht? zddPJy6N!OkU~E&4dH4;KO~TtuCLh09hMZSMQO%Tcpz>K%2lTFNO;0t1?t48)vX+k1 zKiXQvf5x+pQ?VqlGwgRbZF z1ldf)1K95zA_0K34pN+y)SD1)H(Y!slhE%_v?TGd2y#^4kyEVPZK zW-RE8ai6FclZn}L8ldu`jB2$g$Q@4dPP--}uN)WES7B1*b{|#-mhO?{XTcJO?)CL_ zDpj;4yqRMp!wJ|zmlRomX`9O1JfXoT?}G0j7BhsHDEyxynABULf`$6hNLG}E5NpX~ z2O*;nFCdge~7s`$vzOT(`N}wZdMO9bfm?lfxLh57P5j5R|++`E_lLFC~Mw!v6Aj zc741I<8lb~a4p5cA21p39FbXC1L%XQ;6wNW%;2C178Nh!WIQ4dM@zAwPm#2~67Ea> zgjn`Gl^G`q*cbr(K-y_TQcXvu2`@;qlVr*l_zK26ElNpq{<{I0A@2_W2c9;d|1E$E z{3DWbw}^hJLzr$!A7BYAWfzZrth(WHI3dK$bb-_tJ%=FQfc9e{Su9l5-*hO|b$)FNOzUR#pnH&06)%}myj$E2SWQtk5V;pKqBdF67+ zvzZSpC-il2mt!>x6ZOF2+#5XZT9zwJI0{XDE7$cmIFE9iis3n5A62B4KVOuQKGWV> z0~M^($Y7zcUMwhFk;M;%YXW&|N6Lp`Ber zdh1v2tjE-Jk4@EWk3~Y<8&mDs^23KUrr}f_9gOJcm&mCurX;S_R;JLb=A_!xR{ z6oNc(K*Q|-uj4mZ!FN)*Z6Hl5y7)JUhH02jZhN*`-#(x8?enR7fF#kw)Ygf&I`k9Q zaU&gp5aId4iG0@zEzgBUdWz~t14=r&4$sSi=j9+&0?ti9{8s}c9`eI(aNqMuclYyY z<9xDPV=(^K>Ev&nP5usOcsF&-Anv>?rZdRZN@H$O21;0zFMavh?M zOQIx3#3hUohSTedlV$7lJ>2JHa{HFPpG-SpDv+`qUWGtp4aWlkcYCrFTpo=5n~a$X z)MCq->uC|cZy7DI2Ra;Z=|W@d0k?z!JYGZWNz7Rln;@UhJ_o0_DG5xt2nAsvqA%qZ zG$JmxywhMEhfxSqE2LOfK2yOc=GCW42w%3=Zq?v}%FAk>KSH{nY<&9H7XvL%yO8(2 zDho?xyL5%bx3N@nyBI2>3h#z*hBGouS=N;Pb*P?oMfX`3MD1`*sn6UwV{=SiTAS$U z%CXLNGvI!=a{LafQ?ZNT+<57>gt$({ty`?lPDZEEx~OkVb#IxlR~r-HTQBas6+{6o z@mT019jRs~F`(>eBg>McMryhdP7=`801;?tB$fdfwlEzE%DOg5{W(?*b#2(XZ|#C` zGdD(S%hjc_75D?s(O2D>37aD>9B5BwPn$y$WBkv&F7+7tHhL0I# z>tjaQ>O9Jxm!=S33<%8Nn5k{r7)|8RfLu%fWe6-p=usf;C3<8B5yaQ5IEm~8y$Llm zfu}Pk!O{Ki3 zj!57t;(sFFfC&dQiylW1;!Pblp+9X1;aClW7JSzRZL?i71TJ2#^H8Y*5*qQf@C^bG z&p|p7OM_MK1*J>w(v&CTe;lnNE1c)&rRR}SJ-rm!!J)}85p!N;` z^#B3LU{25B)|D2-OMYkW#|qGdqrp$6(`zI&&haAJ(fO(5k5nZAlu&_1Rm$BjCu9%+ zs#)RwYhZ$`^89J|zml6yf^dsaF=InNkE-}qjx=8&swuvx^3JW^rV`U`Kk4ywMDa<= zG-aF4ds;CJrO}wRpL|Ib7bw&C5dn7}0*sGb){ik7p+@M&o_0*djCd(d1Z{YOGeAPA z8*=_lvEi9tNop7sXy0%mw}*08?WY`#q9x+R1chDXV9gC@0}|R#kX9H?VeP7`m_;&^ zVg6_MyawsGjUjM%**G~tcoOiO6!YHJ5bydmJDaS}^9AP!3(g^bs-5iBhU;1hWQudO zqf!4f8-?RFzOm}E_vIYn|K)7J=+s6?^*q zWrOW-W#`@1M?B3ABh%9nG{YY1`w@hxVH7!u0~?kZUlIgq!8mUlQECOiGk_WZlGZ-{ z6_|S;;b$2J2!sgPBVl%4ju86V-5rHSnGf^xVxRHmFNGFOs0kLdgp&AU|CSln^ zNVcHXzJOfpkAK>Gbb4|05VY<;KKbdBS4S@&H6cFR81K9A*Ed2$8_DEf0ipW%6-e_h zj$fXfoLv#x;_R|JwS0}leB-?Ny`<^>6zm}Wz-e6#9ACfVYJe;6xEkQf?$Y%F6%($` z3KJ-9#Q!r>;lz->2!K8dpkbvOSO-v26}g6+3d?{F1?*dakI{9^I!z8D_2zoMX5(^T zZd?w)4&v0|;opVjpzYi5{;A*boBj=bjHdr7z8ky!yYYg%em7ol*YC#b4%rMuV!$bd zZ$+Z+zH0}d+H`DR_d%u>pajx09UYpi2n-PgClTz}bfp!+b49qR_T6|+0Dbv;|8C5W z{cbvowtv*ew1uf>|5e?Qq$gYsaLe(KAXb1Qrl>QCrVANAXg*d;fq2YPW^5=zwHck+ z5;$lK5R^yC)WAxeJGn|+=q^df33vo17Pwy8+2dnLFi%x=Sv?)n{6+&S02gmP2Xb$X zAf5FCV!G!#P;zG9a8*Bkc`O^CJtxL-Rl-^ZF-|CH@^A_D_xWe#xd_4&azw&4hu{)8_#^G%a0Gaj(0=(ZCj@l?<-sriXHfKn z%h9w46lC~eh{8+b3+;0g|8c&+6{y^qVGh!%=$FTM>zD}K;j16@v{x5LIOXIo`p*c1 ziBsT^hcim+d~VUjPo}cov5$KXvnZU%@V% ze~~u>cR`&Cy74ic{#eWb6vKmV6IAACpEEcR`6mWrebLj-E?#fC&*BZ%_c;^=dfL;A zXSfeaY3SK>@b=A6BPsb&M1;a({oYua*6wZdeQ#ff)TrXv1e^_2_*(h7nPf&t0IFM_*1&`u`t{C38R1OkXs_n_dgVklv#O0nrvpsTDd zPdh%ryYoYAocyHPD4>R)~z8G zm5T|HA{W9Ym0jYq>;TZ#6vw&vK21Du>@XN||P+mJ> zf)wrS&(C?Cq!~xp;EF~@(;c3lK0bPRl6Q4}!wX)1rQ8bF^%ZxN%d;dxjn>iNR{6&SFWBF1p^XN=D!2P=* z@U9#qQJam($FFuRlfMv+0?@HERq-5f+7MiLIT?Pl6jFtPC0sCDu~g4#@j9i+_Su4g zw+E8%fa$wg$c7uN*y~iYKU3W*IkAtI5jq;3y5KJeZCm}8FlMNd7-dt|`xx8}%X${R z9bT)7*~UO;!wI!|bz%=1mkR?x0$ot9>h#K=T~V& z4z-o)PR(iIpjA1c*+RQw3$58@+i*p9L@Fwok_5QG7^2*+XPOo`MUg)ABFYznOclj- zk*6cfi7==a5$h<*p#oGzrSPNl)pVj4m^LH}>ghyxQ#vu&Rse5TOPrWr4eZ^zn>ZAW z^(pikJ8AU)v-fVhkz`qx*z@o!yf7GKL>1|@`z0EKPTnGdmB9!W$;hbemw;VKuW-6^ z{1TDOr*6!Heqc0>1_pvKz(BxABS?S*%m|~GDfD~yFEoVK1=2rgt$mJ}o12@tn==?y zSyfr6s-U}@9Xoc;wqyJ3z4lsm6q%?2bn?{LngSI|=TYa#<3 z1({uK7`XmU{Z)e}ZTKhIg@oW8L-#<3{Zq;n|w_Lt) z!gkh;6S@O<;vlubT!MJXVCdX&X8=kT*y&KLu|ISl$_Ob6uFb zZ3N4RT@)A?o@H!h-2PfD2T_{p6cql8^Uq+a~lm3Ul{2O@Egu7~uL#2>NH|H`}HC5kf|5ASY4$6o(82LuIcrO&d-eQvWo743 z!9xVCcgV+}4q3QUKdNWsYkn1vzgPQ?M$CfM0jkRsTn8$YR;(`1Z$$*w9_II!w*~>Y zdT{4e-tftgfI6^*kOPW=1pxZNb3w_N4*(|!hV48KV%taq2pIqZXabQy0!kc+gO*o$ zBw+ki&6>3o$o{6f47Ds(SNY70(Vy4}Qgj$^Dc?%*tBYzkIB&lD1?w^z2K&cU?qjWi z$5`$**5+;&O8sy;Bo~Wk%}a zZ&aP3z$}WsytP3xFZjvYI$B$MD8h2Ku{LKLYwKQUkoa~8_Z5Wc8I~{Pgi&8+gI@*y ztP_AN9ZWvCOXVSWi(U@c6-vDP41jb5EnuNx&Dx;swFdh%spliOd3oI0~?L~KsqN&2K(H9Fww(`JKo0N-s+~eJM>-sbqwh2 z6aO72Rwq}zdcu}o`qHWE9P~3-Vgx3hGw|h4M^$OcWj7lo%g)AVxo2&5zKe#Mc?hjF z+F|vF?!9NN5e#cMx_gub@*Nr8K^Ans5<5-2IL7-av?4e`@QA|D0XoWbatAsjD3!M1 zASHeX!Z0IY{zbYaUrM(ytf88HlY!h6(0cFsOMc*vqWcNl-Wh-D5Mf?JxL_ zs*o72E{u=QVF&}V+YQLr%7Bcm45)jU&dE?V=jSM*%zP_1QmVkjS?a_FYVQ)b)u8)G zVjCI(Uhc#Yp9A9uDlJWz8kp~rnoEs>_s}yQ(STeUP))<;Ps)I-2a#Z|n#4K|%%-Ee zCovqPFW?bpc8GtX3}QmiC^X$TgMe*@0BV%SDE@vqtOA-?)mc9n)fRYWIM1>vGh1wdB0~%9w#AlMadXQ zpvn3S)~}oU+0CuU7gU@#iuz1DdA+<5zReragbQbvSc$R{0RFNgP9^K^&;@{86n27_fwN73Abm~5uJLD|06pAVGE0wsd*Oo!#vb?gY7y%C3ohB;J9`m zN@Rme7tX2~r^~*BiQ;htT-?Wv$NANql_(T|1p*7|hQ&TYpH^2CJjM2@eq^>GY_z*jE*KyieTic=m!G}Jir3PcVLLakwFnQBajbtBgn`>a_`C;&&s$|lo)u7q6%zD~jV+0~m0HjRYgzeg$zE3ClL@HDhp$qN<0y!& z7qMs_Mhv$MjR5k8+4qP(_eCeI^x273CMEJng{N8617h!LzvojzIiBsH*5K-<8tsO} zX*L8uIBGqJ*0|yj^q$N}v3w`QXQBg*+Mf>tqO|$*6tI7&rB=em=mUyK_^BAuC)05W zH<{ut5KWKaev*Yl!s6r!8Dy*^fCoAl-aqYsE<^AagmhqI6QOcRv9iIgy8t@c&)LLx zc5q83-@*KLlULm#mRo6SyEnt*d7=aZ~Jmf}#P>SyoR2KxO z3k5W6Y^s?uMQAlOGd#Q^_P z;(HJ;3lKpJLDY#?PUdW8!`e7!=7ZC+Kw;< zpdB=Ik&TyrWZGW95E>vf;OPJmGK4RSsOxfI3>0?ZDqV;5aQU|S+vY)Ywi#}SC1pPP z4G83;ks8L2s9~HfP>0RGH;IW@)|#Lbes){mjNhszYp=!uG>Oq;yiN|WERRVmPss;G zbEPTysd11B`#f&9-}}{t1XhH3f5aey6v3==dp6$`9NEVBlXS0#7ubkz;8H2FD7j+M z5K@qds98tNy_R&ORHUicQ6wGT(;e>>xKZ%*4~I}7K@s&SfWUM_ic^K3m70^J-zr$XcbMiMETjV1eA$RPZgxDuEG@B|c=c*EfuEffHL zbs_BiQzdqxr?Jo|>7ZzoU)L<1hv`bncye>vXE6>m3 z6s5_crBisHMVX|yc~!lNC$MCMc`;J-BvJ1=*n2+MUx;PECRt^AjEwr8VxOy2%<(b@ zCzp8Uy}cJlXR7t{!*7nCTc{v4jSukVbeE=25lx;?m!|R?OxsKq?H&%sn#Na=zDd)? z#qQ%?e_K3gadt!D`bD(+4i$K?)Ku`s69k3#Fuv#u6%LeyyW%abvJLMx+Ld%~(y4#CHm8Xon=Rmh+#6tk`AmMB&q6DdMfrM6!a!}(% z-^2}uEtikb-Q6CQaUm9>ZcD=HIBJ;f!AO+-(+8DLPq1b|Mt(ez4Vkju^!{mqJ`pT- zI^k2w7wr^fpz6TO?D`$5jGw?c`WGnwhW9>!3CZU>mYTDsIW3Q}`~Ch?x$>**nu^&^ zOn$>rJw4yET?-u?a=_1{d%_ z8QUU`5vyxiEQ>c8?hE95gsfxO@Sr+NTn13g zVJZZ)2rTW6l-cPQ5a;&%^6*M(Lvu=kKj9sizVlScyR^*v@>;?ZpTVjynrv*hhb3x| z0MeziF%LL{3#S^=$h7&_1O&9Mob$4;Opj9Fw^1_EV*`gSv z)R9}Ae>1(_AR^5+K;sw->MJQMU}d8{C?Hz>wsQrKDCwM@C6U= z{Zfu^%LdutNb)yfkh+xXN;Ay>k~I+7W(V~Bx*~wKQ#$W=a;Q;TZtkTi${8~VS9_Tm zCJeY+5Q+gUa2g`JAA5k0WM=9au4O^7!6RjJ6o*c*wBJGG)N@zJe=YAbB^s>d{SE-T zz>=fJ3*cM};Br4jJ*JU6F7jE}qD2kC&!dC0lZykP`#U^+b9Qod0k#n#{{tJ*$;J5v ziobT(>;Zm6k#!oE*9I=x-niWDjjKEIAGt-qmy^H`4bWd1UXYOxi|ND&!FH7F<;X7@ zAcM~w%Z9`*lzELjcCqvJENJj1HRA&9`xh`S>rdCXHX=__5Hx*18hqcs1)NOjaF@7~ zhTm3ECrqNP6*_MT0DyYruxN8R0`P*nO}r>C6a@=ks`^M_zF4L2;TIR+ z=96L@jQ8UGDJ~TRIX=PHVc1t6giYL#n?Kp2)d^Gft|yl6hq)6Pw#JI;6w^S8$LI1hwrhiQKThmBMX0l zQX!4afBeYLf2!cybnf|9bHKe)R!Z_XI>#+^X@c*<8tBY_M zA|_Zs!H|K)xGGat{eDH1j5#uYjg$xZgc@C#7lW1T#kUB_%3D{q^9Wz4cPTE*(A{`S zF2g~#>a3tC&1Q)>a*;PrDPs!(Z&6^DDEqP_b1Sbsgh7W=yoIwRb|q((S3`&<86xzE zz|wqrqheFMh6ECHh>WTmvuvq{Di^VIIVEMWxLhH!j4M{pqZ2(^ufAdH7u+eHp5qqF#^Z}L`!ouo7L0|}XkNdU^L7ce#Ig z`BGxG(~^bsiMm}nX#7zlM9Nf6C8Q}lkWd0iZkqdguFm2~%7z$F#rHH*PFx9vP~GP# zrwSJhaEC-1>n)yTjM){pq(Z{XARw@&=UxJ|oP7K!PYFKIPtDc0+CY$h_z?9(Uxqhv zk+HH7m#egl0R*MqoiKRp$b*vt0xa}|!u1y|Jl7epN(kNJqp`qP6lo{*!w!mjAEx_i zHS8p^r;py2tFJt7EiAmancg`vJ+Efzs#pDC`t{N+w@FY>IRMj}Y;ZfkW`4g-blQYi z^d|q?a*Qq>xWA_G-dL+MR(-*tmfl#aD^@?avzuhYvjGK^4MFN4hMZfJc&KK~a)`TH zMut#O;sDv}2Nr753>&5Opn`zn0kD>SW*Bh|SPqdCRKJ^BakQJ8q)yb!y5cAKkQPLx zW#%B{Ly_Ygb-)=QJ4xVRXJ8~-lSiE$yapvyi%iMG2&KM66ww5%p-pJevYaif$#S-^ zrY(3JJb2uoLI?w>?*iKG*g)WrHZp>84j?xkh$Mlz@$Cda*ceihApZxUNRp(H2SO1e zs96(~b3(^`L~9bpe>3>WnzS{w5*}IBHr8ZqV@=i;DokLYK{X?y%?=C*1``H0FnL~> zp`rjEb2Bnx(9Oh!LZJZ@_z>6_6(x z%p%1;zXQk$zH%Y5Df^cRZmZJqOSxgSaV^fR9NHfQLWyD^0+32~f@c$ZPRLf} z@OOjh4HXl0uS%6q%^0t6F5(^&$bpR;s9hjCq3)-CE7k6t0tk=2%56LtMFoU{aHdK5 zNp47PP`D3A#04dj@pQUZSOZ5_gfd904?DwgZ$|?z^yuMKZ7+-8X^=doLWwW4KM*Q? zZCxeWO!ORyaG&ljK_4{8fj2V}?JQ12f@}wOiqibG%zLKe7QE7RR2B*FMu>mH140O! zVBHF%)Zjj}i@jGq=a-$65ifX&m*CQAO++*u(UsKYx-jJ%45VHj_OUKyqs6+^1uU_# znJxEQ9U5G>5nDW=FKHl`s)71$j2mV|$-XX+=E3^drM+35$IvHYu7xMR(xi0Iz@#O9_A=rmmnU{k_%rZf7h9b)lMQkWwi?SHx>kyvwQ0f~x6z$Fv zaE-;3WrqZv?V_5@t>xdP8o18p>wFHIuYU7ZoF2#Wb0>$QR}dJ0*25PgO+08PA}@oc zm$|tBfwR`;jGEqO%$n(a#;lp$AvxIf!2LuWwhL8sK>OnaSP4Hy<^djS>_!?3*w z+lK(pnh|&yh%_`K+hGkUN;Q2G;1wIhs@R1hRm9l?!IKr4i5G!X)d>CNS;M9`w7f^2 zHKShav-PZXaN!$X3m8*`Q-^7TAK&5VRUW@3HTYFbN!W%ddHIg*w)*qUuOz80U>$%e zc^uK7*&8v9qghXC&c0mZXY~xu&rwEu_y(J>-tocJLGSdk_vZDh?|KK{9-O|STwm|t zIksQD^P}Fy$*Yrt%M*%0y}7vf?q@&W*=gz6ld8)vZ{CW(4@Y!q2Y|v3{WjjT16aC| zX5T-e8(a1J(}nN2Unuq!d;er*yC5{*+$~(det5*H z!(4PgZypv~ffDF)QSs8z#F{Nb+bI>7ymI1mse?euYN?0dH=Nu~H}^w#`*1w|K=%hX z&G`|JxA#__7b(xFPSnb-WoHNaTG=zD6gJcyqym=!kI3-O!j)_mLCvZBYLTge^L;%R znf5Y6S9I$HU~_DG%FqA!*Z=Mx|IPpKfBpOaru>EydB6SdqQT-;PG=$tH!i<8&gQde zvS{RFdKdlne;yCZFV4}A`Stv_|9(p4D(Lc|gtzkJ5Ei`n`1k+v@BixW|LX7lx4-+3 zfA^otZ~wpl`rrIN|LcGFfB*IWQhxJ){-?kCzyJGx^?&@w|L~9h`oAi_JeYv)cxUGr zl77Po8=A?pUXv@!v73*g967FmK;dNqA##T`vXIyUWsxd}nQ=49scTq$s63^G4@a^< z-E!j^Ye2fHuR`9V*zJ_x^ncu&9a)MVW4p7|(Bo`)mYe+iEl<;BzP{#9Yd1V5qxtJM zJZ?AQdix<%;$2?k>-ohyw$RxfvrH>7t$?^<2YDwa0#l+1exuk2Ks`2m=<>QC_3%?C z3+V6ICMXi|bK(XWvJK1ZGM0@%v1vGUe=K}B!(Hj9R6#rFt$`OVz(cO*B>=Gn=(v@l zOGy~p>F0UTmi^fiXn*zucBcmlZ7Mzkz67mwxWcHM2jNKq5IJN&I9k|@^^~l zvFikG7Rp-MNjzu**YAkHGd&si0freOcje!X=czrsSKFV)pK5y@hkG{5U9;D*+T@_w zI`$IhuU2Rq`O7(|57EWbpZvlZrEeF$ohoPIRC|N1%-3ZS)4&_*K1PC)l? zTN=m4N4cHDvF-5A3u_9!wG;uHI17-7hW1(DfteBHFo@#FThs7j66ZmVFb;wkND00% ziKK8H5Qcfn0)#jX6@FV$lx*~Dth&Ia?O17)Tba)Qvr8;~W*~r>TRxOv@V0J9Wj7Af zkEsyP><8NtEw)U(y`80W_n`tv_ho=dq;3?);H<+tJc!*GUpq0<04BU$yw@BTHTQ6d zO~XJTeU?~>f$|6|@~ve{u{_UTr3rUFhvj)SlKjVMDgL&R?Ut#xwzHJ((QA}xq5{|q zpzert0pyw>J%g%W-$N};iZC%sWMD;kmIj8Mr#T8Yv5AV!5Z)cOP;p$d6jT{l&f{8& zu~G*7hi@qx>M!TS)L)PK(rqThC%X7@^@kPDk!!Hm<~hpGmecMzUa2}N- z>DlRD=z}hgUY;DkeMJD{!O_+GgI5O^XHtA$|NQ*zD}Wo1E>2Efzb7IRHTflx?dtSA z^rf&tgxa>&h)_4(3MHr?M=f+!CCr4@JTj|4Zg&S`|k#>ZaKR zqf|7ldD5;FttA3UK*d_3N*pz(1Is8&sS`4^Y2c*zJBvUY+rg!#$N>wla#OdJV+L^Q zcsLg-s|g9FKJ`A3=??APj^lwaU#MP>CsG_bP+pQmkD&k#h~;2{)bx~^B{LFg^%Qj> z{I12Bs^(Pd6au9=o^ia#3wBMqKqFzAoQQ7~NueI9unKxiRgya7y|F+~m@M<<;YD2@ zdN6}8MNbVG{UYId1Bq`8a#TP{6Pa)|4}CR2^gnTD*eUusi;kv{eFF3Bmu81h=F=$_LVh=B?v!c z3=%%1j>p0*I<1U=Fv^XDiH_(D)-R5F6ia7mqzFY;=HnAlz%j7k`CwLnYH1F)(GTJ= zgLro|09Jctke6&&s){+8I7hfRr9JiiBBo4$cl7<>7HoD>x0>8PQ}XHx23$z0s}JxQ z$)KZ1T|wuU5b01^(ny7Ola+ku1r|cFTrQL`7etMCMpRb(R@dB$VR2rj4F3w3sr;}n zEG)6mJ>ubHJCkUjV!J>#q+ocvQx|3_YlS=xq<8=-r26l4oXi=JUEs{YTuB(tx{*YS z?^e6jD|@uLLzl&!7mTZ7L~}|YEnT%F2Nm3X^%vMO3HH>)lAIRQqNsw93Ui6#&j+%! z2u*AA%2n;h<*7PyJleybL?cYArOfIJS+#hkbcv44`gr$rsd~kx0U4-hYHQ%U1e1(?RYwmr{ww*Obg&S6VmSSiI%;K z46BT0v9b!+@+R$ifc|4bFg{5+p~^^nb$tXC{Ln$RcFlZ zffXOs#Y5uF^4k|ZHD~e$;o&6)=xJrR)nLfWs^|>u`|?r`D&8+vd#D!>RXBsk6oCk=9y7(Os#srNf+I>{=QIoK zT|r(j;k$Bw9DVj2=BDRZyqa=xg2{o{cve{#c1wy={jjGL zSObHv*fn16!~hQRz4ED30=?Iju%KgXp-n}J|5<<|%iY))}rd0z;oNpEH(E4j!> zE4x`9R}!g*YhVyXDij4sMl%@iGkJz;Ljj6feNM@D$d~#P;-xP;sfga%h&am zig1e)w+-3>COT58;c+iO8n&6}F{_F9}& z`$?TQ<_5y~G3r3<9H@rG%)=-(eZXv>Y-Hwnih_d_wH_cn#8LRK69Wp4Qd!dr(gZv7 z<&AmZLHjc_w?V3R(L1Uk)dR=1?8r&dB#m7M5DkP73AtvKI#ALQ8#s@uvok7e5>cR=)A!Yy?*d#DqFEKDHD3gW)hV6iX4fGE& z*wm8LvwfHr^s^E>x0g)DGacWsgRM+vV=xC9BL8=3GFwi!**e|!L#Eq)$aHtl8-v-` z^pFUQP1c6l5khiM=d&R67%}q)SrlNmA}FFEPn<=dNCmZ?kNja28{lxYvZd*EgU}9F zky~r0+u9^tgOIcRqnPgXu`QIX_ED8Ak_n3vBTkI$+*E*T!$Gwum&=h+n3DbrUb)Wr4Z%w)nL0U8 zb`G-3NQyw7D(Rt?N2UnV@b3*as`?(#>JGg@IZ1sNXGIk~N_B7x-GTEBqFr58z3BVE z^(jpR_Ojq%()O~y82tT>-doYqe$^s6IBpH+wbQLfKcM&sBv{k&XcgoB!`IcjX#sQ@ zkCE6jwodGAcpO`kd>hZ}zWjoG8W;vhuY)NG)RP(1oq(AQfs6%!mf%cWQcc6j_%2LCjNGhtf@w?$fHGI8oJK=U@T}}rhv;RPiY^|-dfx-L- z+KT@`Tk*RiPQl}51c{db1%|oz^8iFjC{PbUxfNvO0zfgi^-S)%)o87qthhrDw3Dh)JkZx8ckY4H~Kq8Fj%EBmpXgL&?_=RV``WwNuB{B=}N5YNfEWa7a?&>)!BpQKN2qz7A)6LMU+OM6#SL4~cO#4s8 zjCFxtt(>+k@M^Vz4pFj?*t%{euk7H26%93)HHB+vzE_O#y%nyZ`EEMAyA%$<9~ls> zkpFN%`WB~Y4$0Qs2Oo+RMwuHSDh9wQc;o{7*TK8eN1-6(a0237@>*QXbwhKdV9@&< z0tdYSR2Dkd3s7YhB0A>)(hcxT>cy4~XAr%&v%qX#==dUgfA|J3vZFU==dVuiChPwC z`p62k))GT+yQ%cHo67DKD%caoj7^!u7nzxr8DLL=JB@;f#1Fxf0w|JUqEOJl>%jz4 zDKzn}z___!+cV6lHkE#`BI3G!D%TRWYf^FZM=_Pgw&lJpbK`C|m0M^CC81#ffaT=i zBuO$13~CBp2h8?P;3dF7hPIQULKNkL86G;@pyqCl5(hg%EISLAr;;TO{#H}zY?w-v z@fyMJcq-RBB4BgwUytT}L{xzA7|+HS`V{X?V3UVgkV6j#p>ovqxUPrzejYd(G(&tRbpaN)lbnci3;Q}d3Ua?Daqcm`3Fhm zzLvhOxm+snxz6RXTJw9VxjNy)WHO#q12YN;_VVp9A~#dfM@W2KE39f;(;{-CMkXsUlPb8CX5(oaE5wezU4{KF zF@-XeVCtSLap__vOsDs&^9zekAhB!Aibmp8BKSl6`&iL2D~)gQVf8IxnVeJ@Ka_7O zaB+o!)G|($>+JOoaJtf&?_H|X7v8m2x`7*BJYMwRpc+4)6>o;=zUh=t8J{eT&aX#O#7Br=p2aWUD5PbDxK=et&s#@hu1?YuBQ@waVMe#irRr1kttV5NckBg;o}iD*D35S!zKZ z(1G`7C&#A;@6RvZ{PN_8$(3r4M2R%I!*oiez)e=zR{1a~L;?0W=*hia9ya!3HK9|z zXsNdTR7PJv>kXp1P-d+w-}($zZ0ATZRIl2NbV&H^#o{yrrjRj`BflaC zyQ9U)qTa6rw|O8#_b-Z5Wl=ayvq&x5`U$BD&QsCt0s95DinO0w1HeRf+NC43&W*jI5z%_2=7O1E@m=)_ZBJzSL&eASj zRlB;K9bIK3Ndk>juf9v9$N@raT*wN%FfXmwoZ`ExKJObofYlp)t|{x)lQ|fXJ5yxq z?$ot$<78u;GmG)8Sb@J*{Vd+=c*U=5v=6%Nv@dl{S>rCd_G4YBtKNr=Tkqvj?zHJ{ zoS3dzh}y~P3ctU(836Kg72Wn;U(fHe6$nY|z_(&agoI=5#y{wSCTZvL2-a+JmK?s; zId@#R*^UzI@jtESoZA~l3Mgo}LAce$I2tvdp zu##oS9@q}S6;$8U+n)#Owd$A=m)VuvAU7W}#^{v^hI7NGR1c_%@}@NH>N{w+!)s}` z0R_8MN00#&SACM9hQU`ACdyszv zq$Gy&62Jk#4TEpU$UNAn19(7)fKtu$fGRc~+4RgkEBMLuKFjpn7Sr=Oo1WL%^t$Ky zED)i^z$bag&_M+*0_iL2^%Ee0Z9qN%J;_P5k7dpbeX#BWBkb8gHUk`N88+AlR!k36 z1&?le_D`nwID|;IV?<+6^@=hxiOM9i$xyRtHvF>64q&dKL^An;YQfha)p8^aE=N`_ z2h6poOw7?D<5T4)s%NyC`q_6@(pZ~YUX-JkghZ^#mopCT22#w4t;0vSLQ;0SRP9)G zfD3B=XB8*7W#4-^pUsNs(K+^2sBt4fJ?h&pxt-OV`U9QWYS_d*k5Z|qixfC#DwuB+ zMbD*wV6aDqYuNjzhBQ^V?|*8vZN*Aq0nzr2xxEB|eMzByFiDHT#>2tv)8JaOZufV# zFSGlSIj}Qj5@%yTbV_6EM6tPY-j*(TQw#BM9PhtG_;vs2<-zM0C$HYT=-;Mma6(20 z`f;b5bKO=+*UG)w@Ibw9W!`K&cncI{k^wMcBw$L+tsn(^xSPdbQuM6AjU_jhqGHB# zQ43>(WYLd(R@&JRSc3QMEf+zVw(ACNunl5%1D|oZf>mBNA<#*-2Q*9qtb}iSAazVL z#{@Lf_kkY|^~>f_5*G!z{`-5KzP4=kHj`&=GkM)})`^8| z7nF)D19b0Umhjo}OlX&xAv~$j3c=9i20jWQvJf&^0XWC8!;QQg`Drjuq_xQdgRbvv zQxdTO(N%XYzVPIA&|w;p-|!Y;{?YtjiSh5Mm5 zB;*gDkl#YgRI&9$3hpEPA%r%oR#j?7<9;eopMLMnM8!r?sm5S`@9pK`=lMOmXLHg6 zoeOysqODis@$m6|A%y$*1ngG~sEgoB{j5z$AOmm(b2AhOG9sqg#dT4wwV;7?EA8S9 z%@5;yedapRhV^H5Utb*)W&9}ZCB`5H6gEl#n00)pu3I4RgK&H5+6>qL{otVLECYJh zL*2LSU|(%G%M~7w=5UQw$n5%ZAbZnJTCFsd1r|lgP?P}KIrj2y>^neo1aP>3;f82~ zi43n}alJJ&6uBba+*?c5s=o_piLb8mF_Yf)P00mU980%19RY-CUZjo}XYS!!i& z;<>2_B~h?CqtYMlkqI<4Xh%~Qzh+hv{7&Y+hOzw-&3(9T>W%QBQ*VS1oqD5t$pdta zKn>4GEI`vHiXKwbod!@k@pBXcnutf)aDAb#W5glk=uJHL*a8KS3Drs~N@|5&P%Jgv zz!jU_1$SRf!`Nyp$2bRG{g8LkJBJKA@_)-L3LP?T|uD7y%eo(~ZM#JIxF6 za-v{ZeUGNz-3)$+5f|zG5`F!t0K^adI8vXB7=Bb$#teP z=;=yX+ySiqCfk#etw%~yZ*=?gFdv_OSNvJpZU^_T_3T!D0?hhNbSp69Wo`obz901| zcE3@04-oLavx!E^>=66X4K)W(T9s$oAItZ4tNzUTle(u~Qs47IwgTjNo`8w~Dp{uQ z*klNTnqFkvIs8c*(J>@kaum?HNWpkXZhN7T!^f~{0(-6#m@6z&?q`Kl)Jj=>b7Wy= zqj(*3Fu2n!#0xI=J$y^gH#3?zVwB$Od%Qc|y}9@XfKd`J>o4CSv7*0Gfl~F&+1poF zNVp*PqJAr0KD*6mT>19=<(t5+TN&eQg?sE z4FcE@bhK@t7b0km7|7qnF@h%{;;U4Ri%`~=0kU8iv5}+>NHP#k0X057ms*GwUTwIl z`L|X?+uj`JsY%9tIiuQOIFD&3PAjSf8-fSeiQ_-OP8@%m#Rp=5W}1U|3Q{vMf`Cb! zM;^j!1{ladW`VjD=+?L(8-QF5=u#m6=|FqXH(fWgYvDks$KZfRv=fi;ecSj0x08)@ z2MD80`)318Xr|w~3<;H#2hBg%9A0~4g&{KN%?|JB^Mi|%($!UMuhbg8etuM{#*l;< zPS9?_5lbC#Xv_T|e`d3!->bEG@%;1{)XLY>ek0+P_>y2E)tsO+^q}!N8ic4X)qas3 z<)12&YQ-VUASM*vN#rdlPNYr=nwx2=?mW6BrKm6?hzCi3u-3kibc5ac`d%(T^56j( zeoH3UB8N&Qa?K5|8hauLf4a&1__V3xdsou&wv@3r+V%e6jJ6E%CGK%on3)xi?oBjN zaAlmNm~%-a!SQWt;a%aoH(a&%1gXvB!`>$_g|!0nkA@`rYUy)4>Fw(?dc7y=TIu{& zu-uV!k_7@S|NHB9Higtf5ngMM5p{*kv zCuk5UvW*WT;%!;qKxX0IlSL~}`zsBc9bGA^cQC5U9l#|fNj`n}(ypeN94l9du1Sb2 zeEYB?q0p^1s_4uy^@!!)f|8s67W~)!r~0lN5dw&I07m^5?~p`RF1?($9eygz0JWVrYeyyjB_E_8V1Th`N8?&dFilKu0beP)H8FFtGsf^yAJ#DXTsu$?%VgaS2 zhIIi$vC|kms)YjvAEL>8YT5fM-iHFW+~N%i3LN@NXD7h6EP_nxt3`B)VIQECZpQPW zl7tXBw=tGJtO0x3h?bC+^;hJ@=hB}E(|-1SlHDowklssqfvvN4CA)R-80EmN>;!b^jqe)=!}V<8T6 zJzz&zdpE(d$Y=-G+1I~5?_Gi^31&w2c)TV058yXf#0P_qaB_Axiyc=FZ!48xq!BMS zNP_g!Z2vZyZRX5upNUQZsx=e29bjusL#Q>Y!?tGM&zh<$PQN+0czfB?<&D#N3@jg` zY-b+F4dJg8FQ-|yG>~`E{aqwS?I+iRJrw_NEKvva=gDpQ&)-k?zE3c%ALJc#GbT9i zeJn`A_t-{Q&dF79V0*uQb@uulY(q6)i?TT#SQ~s}bCAthL)qXP?R+=Q8=3dM(aw3( zdP?UlE?A3f0G7NI0;{MwPe8h7AOnl+i(^37A>pxL#xc zak8w)YEaj8g>pN5c!heoF{^HIgUHIF486312*4M^@vJZp6Ohvzv7oJ2wzybDcUy<+ zp+MvnNDbVqR3J5@Jn0CBqOG*W&b)AITk*oJZN=-J#!G;M0_Z7qk)$=j>JgDL$p;e> zx$8y<@dx02@j&j-N-fRw1feAs8*ylh92d~KmO0VI^8bZ&M_?f>( z>DiY^WK+Uewb?shqbq^4XegUqCGVpHXhl|$QLNW*f8CQNkiopYV>pS9SicF5*S#gx zNbedNVn|dH(}pT-*oYPZ;@Q#QRzg*YfZC)~SLJ03Ri8+RL8ZnW1oHza=)PlwCg*mY zS_n$tCmY>RFH01Xz!&ikfBA2F2WU-hMntD@g5-GV%4QO|j2L2211`yR2&0*DVyvGOA=xxe4K^cVlcswX|1}i(b|`tT224Yd9C#I7A2* zr6p*o!B5qeJ104pva|OX*&X~x?Mg7|S~ZIo=WMBu$`Fd57KSC$5G`*nAUt~U_ON$# za`f``8@RjQ^<*3L`n%pMNUh>g-a=^mOO|&uk42|_L-VPvS?%M&J+{oJAouvVaO&6l z?CRG)U-8VJ|HZp^2WQUfx6d!mkJ>zQlVqUY2G`v4y0EFKhMM1@-Wm{9!xH)4^VVRe znosCXEgFLJItT5N3o6(UNpKRRXAV%3N#x{amRknAE*Cu0fd#rK+XR*>!pjusZzoN% z$#^akD=uP-nSq{y2;3Z^fN(_X`(U(QOw+F&pkaqeJ#l&sUO^J zOh_HRdV6vO-pZGYzuqH=a{9XR`RL8-*QiRYd~hH9(k_g z8MzJALgbnNQYKNBnJW)Jo@NWX2MJ`S&!54^);swD(0i?)%beBo7wd1N<4(>p}|_l^455W zwAqtJYw-|pBuarD<>3g9rs&B;i*n;8?<7*7gyNKTeG`{?Q}?m-z(H}8!i4}oK$1bho)rC@J}pr1La?c1?f_UIz-Gz@@2l91U4`}`x75fdrsaVZ5x@H>%MwJbUJFdi1qvbe8F3nA zktfd4u@IuL6gUcCl58=Yf{iIJJ)JYg#3&P*hdxZYE0-3A4pt{BGI$0rAi|FR@LOd< z_gemjC9AANP^5{x3w7tBX|u41MvE({=B1)k^8=!GbS7!*NT3Y6yrP^`O|zFL0b2HP znNe>(8WI5^*7rRh*8z3!*-Z|c)@3gxz6YbCLZbgbruml{qJ=kLQ6&xvg4T-PROwl( z-&(>m%O|#Tf4Sm8U0oe=dyTX9&#Eg=TL5SMd9Bf#lj(;6Ub7%Y;6Q5p5J4gP8ik9B#u+uPFtvk@s*8YK3t4C5p{U|_KL@~)b~wm;CopE+u5O7 z&*>htalI_`Ax{idTN`N%RJo#n3`}IE=^_8+86h$tCb7W@oH85H9e7KKu3PI%<_j`9ZAm2N zwr1c%$J^F!aLTr3gHyI3iH|@h5kN#HjZ>uEEeC3H0C0nCEC^z7-NhLqfT(lXV z8G-FEf&w}=gU~HXP^X%q$QDWW0Ob8Xrg3ljeF6q?RqkaJ43($8UQt z@TG>~l{D^>mYhJn9i(W$rXfwIN(exgP1oxA5XqTrMYFN#4*DRMS}n^xDAA~|=}Det zBVaSnD5^c}oqT`CJR1_CBzq<|wB%Q{*H35aUJ!lt6L(cDse%zkiiu8c@ESL`TY$$| z)qq?qiTn!^W>n5*vQ-feeIgtzS=kAdkHoKV!%1wXVc@FKQgKP=bfey=+gG^mC_fgzynurfDXYXW;R|B7R91Be=$2&$BUMU*-3{&% z`EB0KHz(iqo}V0Cy}dZO1flq=13Y0z+HL;!;NlcQRlOGoxtzZ`_-^M!k4;t?Z?&W2 zXd%H`@Ss-*?B_j6fN~EkrhA6x4>aTC0bH@FfB4J4shUSw-dtF<+5%pE%7!0TkAD?S z;S(=T*IUm^>;*ySmjhLf5zsB|@~{X3qWB-Xcof}9O8Vfm_Xf|@2phR)thq)VG*jQv zLi&BIe^wo{4Vb6w-9|I{Ymne(vC}5n6rCuo05+!UXGq6Glmah@Z4}HU*YaJoNQ@@C z2-1BukGduH@w^n=hfkjj zSpou>I$|~nu5qB^6eQFahyfg ze9leBo2M7m$cX@i*yL<~A$Bu@>Ec(7st(k%aMb^fAt}5_k zVh&FC&c~kwkbXX%7VFu8UN@ih-s%7b77ADvJf%gi3RI2^5lu+1vWvpGyG4r`*-Lc< zi=<&(qiK~m4&_t%y{t16gr{+Wo_f){K=qO^c;VjfP0we6=KSG(kKzDQ8gsH^cfibQQf?hS|+|5Fuv-83e54bMIYx=5tJzw zj=^oglvYHM#zn2f(fICOE;?1!bg43RX$?^FJ9%(q6)fUVGMs#x&R@J%I4Bj|Qu9Yo zfup8~HMpkoC0iUit+cKoSj!fyinJmqajJ{J`$GhS_eHoypHdY2iSh-W znEDHHS5eYsOMZ)1{LwF^f%sg)jPMFi`P+k6c(TvACD>Uwo+Z0V0b)~cBR<5Cu)m(e z&zH5ZaF;#f7w{I;54g_?W=&q6SnXM%B!?B}FWvD8UaEuX&91y3;7;uJUSLDQe_zf= z*WzL9VzI@uKgCP;oX_#~T>d_Q!!by9@%~1?(ZJ zhjb+jTd0@ID&0eTFs7ZLSLzAdQHi9}Lk!l{cl!DI6$Xu;-aH4o?fD4?;!r}+Y~zF_ zk??1h2BCCX)C+Fu!ex9~`Mm6ztPt9k_f&BshAkHVXUN^;9^%ZbOO@A_epvO5(laEK zlTU`;&B!{T3df%7rFF4>70+!c`c)IGoO}uL~U^rS`#i&szT~A=it7LSmNy;HK{1z1mIpZxeFfd2JhFT-v?Na&4(N()Eg}#5mqqNP1FE%t(v~%F zE2pnh5<1ccYwC9yU435fxR9l*l)x?LM|@DDY{IuP*0MQiqFZX#N0k;@fiCsv&U=aO zwBuYFkMB+gnb;sMb`nUpIAIz%PU6HaSd>xU?m|)>Kvu``z|oVUC?wAu6sFi5GaDouPL=cq(%%f_@QRT@Y}^4L}gd{|sg)1ytF< zvGVQd;e~KFHA|~DhUmTwK*b6kB!BwYF?(9O(`xO$x1F{7-gegRchB)S;NS^dkO>(O zz=mK<2q-7`O#=uSIbr64UCOmUB$Redd+GlIvqc5)H@ zWbHq0Yq#62-QLpL?Jcdny9Pp-d#Q<{1P6KtP5|sOq}!1cNBx42d`}dIInq>KYP()) znMsV=KXC2`z%|FI3Enm5cVg`|-RqyM{r79_9ZDEMMU1FD;zOh*Mlx_jpSV)R*!X*s z?zQQy$+G#f4mM@kM!hR(u%2ndm7uIVlt>7omOzQ7lBQDn01~ZJLc;SRT7?YN2OaEX z)!ax2ZW_d})io9W#ZuD*+@~O<;P#Ue z+_Gy!?Bkld_TAon#!#NFKZH2JI~CeSm;;4$ay{AA5&s53S&?KFdZwAalVO%)aVgw__Ej+=rQL|f*o(vp9 z6&yMV!{J(J3r&e5FL4PKLsv2Jc%o!yKY^YKJ!582ZCufLQD)^`AFF^ybvR3a-4y$; zL^Ko@(#!p7)MOyy#075g0|;C+^s^6!Uj|FLb;mg9BGJWeGfE{aa6R}~nOHQJ@Ea%i2PIMDr*KWU&;}fiYS1$&QtI=c?$lDUkoW*7azk2r5FxiSxF3$E*D&~h7XaTu)$Mk z&`6Z85RN~4!*_K}KM^Iz$8TTpJ$rC;1s%nMi?fA;uw0YSI0Fy$4AvIV1g-3?p7j8` zvL0YpR%g3{2Wnw@Vz4xuDl1;WwqFd`2f4>4)L`>Ii3J{7kikUFi65Ehftpb zju0mWfe8q53<$1zK^i-ef%lW*+g1YcV))If)#d%fnYuJk zM(Wz-VKv5+Udw@!*!YU#3&_<>nS3hb9oU7hL?nawmG$DRl5BzpiIgmoLCC~NW(l-OSLDT!&cueZoS?>u+ zI(wt}ZA^w$>>imR(fprm`L!Z`Q4>XsOFp=sPm~3yq@?tpC^&tPbd`#-6Ul5Pl3HWd zj>qV<9aF2ygTIT29Z(1xeG%sOr)8&m6`NCor+R=pf2Ou17@+&Ii7Z1wQ+e(a?5yZn zGk=GwD(pqzglA88NeTH0smFwjn5iBy!fueZsSdqrP5=jGoBWxKcqERDRIZL0q{Wz4 z|JJ2Kf*KqlA;^p~&jA;0kBigFUg7Zac+cJgBoD!a7DC0MBN}US%};G?iffPUnj})s zE*WzQOGWQa$j&VxLjQRH=>SDXKiN)s6G9Phb0l7EoB~icQiEX1K3C#yjtoh)H?d}Qts6BCzr7|K6 zl50769zYr`_nicq#n`ty890Y9xsD4y0Lb$|U?mGc7YX4N8{vHCi%%>V{!IIKGqLNv z&v@`dAAGg&-5^CWS8-S3qJSlxa{QI|x#_4{Alsp4oz<#n;Y#4bZ1FzXXzyUAW01WA z0%AsVjV{%dmOawv0}3>tNLnJf`s^YksgUD5jlPY^;H0-IW5H^T4Oi_wIXgJGe5yj- zP^c3$g=)A8j7JRsPhm71SEmjc5RlnK1>ea%4Ht8PoYlP@N$~^kil`;=lLY4_%-J6` zB1pO-r4M8QFM5efN)k5jgfJX(iNkv+90k`Tdoi@Qtu_WPcdY@23aYmwgz${@w zB}kVrrx+o1p!kOanIh%+CX`eoJctl=qI&Oli-Pk+d{MMO=&i)giEBP$VfippH@yoC zDX>+03Jyeno!{|%0PRr7K!pSJjyS-nlKWtXM+y)ng*BK59eW0jcmPB$TNFeZh6ld& zG-1twjMq$$ja*)4Xt&5Rh|0i6ngzrAEL6x;3&6nV!2Vs&huS~K@u<=m?!}o3#gA0- zVyZ_KDWoYA8G>T&nTtGu6w6FCPb0@-M&!~(QC#qB=Wrt8#^3bc^|fnxaZkAAMgOed zdxFKleyYOqF=js(F=U|fvr1h5Sz)+xx57q`qX2If3s92_pH3jSi+ey6A5JF?5bc$9 z0W(!Re>oOai1XrHOm5)df0ng}n$CWAEu3psO@4bf zQo^9k2WW8tHoYv~w-G^aKCwGXS&k@|nmIcHD#o)QMrmft`YB*hur(w}C42 zK%SH|y#F_i*B6P18m*D`;Gy;qJk%b#Q$AZHg~!$n^pn_pg@Kv5Q1?x93#G|mj89{a zI*my7cm}Y281E1);Z|k={A74`YS-++_icA8dvG?{1N%`wQ}%{<3e0cr#A{cYdO5H`0Vzz7PAAjv%!;LhAcL3m=C2mr(o(Ob3$ ztR05=i1y&r6ybif_VD1n1>d8KH{ixi#`57SyA5(c80&W%?wZ|(xKCB*V|j~lSuKlH z6@_5uPBjX?(!*Z0i_5!=Z*?hb$J3uN#(g*jo&wGWL$-%xWxCN$6n`ixA`8a~+5R~~ z#f*O2dzUlRJE>FXE?%;Tg5}G4oAVB^41CWf22#*kxp@$1VF$vfwQZCb7F1QyrB?^* ziguL63_(i1I8#-A=n}z{&>~G86jTZ z);gTYkHq1H{sb9P)+^+!IJ^;SR12EE*W(e&*|LKvIqG-yr`HVAFCEqtDzjVJSsw5f zTwd)S@;a=y&>fl(N2WuyiwMXUNL7;uCGjDgWBH){bnq|rhodZaJ!Fg_!DB&w)=F$A zP0h%R>yQ_`U&+|{$d2Yr8ER`+0!6}M9Sytw}Ib=ed4sJK4L@kP57U#J*mCKbNG#^Pl-P$liG z-i21j?N}j!75bG4aHhoJ6G8h=NKz2=L`6N%V8y5~U+C`WC~6U3WN=e1jVP<)OckAA?pw7Ckl#cBW5Hj z&th?94gTXt=^9`aHjrOvi0ZCSGy_s33P?xu~lcsHwZn=q?Hifm~l( zj$ElU=qcj&7$Y{SSr)KtqUD9y&r%nvc6tN{9kt)1s(IoXz_%bTFe%ErM=Z*|%qDm9 zp{jj7nPeXZyhoKNQkKbb0&bytwu{8WDBZh7{Xd3Cquimh^$X4mOc=nK;ZO_wN?t+B zKl94^;e?4#2D0AC9!QOuT7U*>xQaCa_AZY8R5J*(NAlfm)V`7sgDClvql(lY||pK&T^N<5`)DPp3kB4 zUXXU-u+z~;Dormvk(HcBuXu@(-$=^SagGe;LX^GIx>9q+Av|J1i+!jvX?-`{>~ zr$YRI3yA7f<%z`5h?4hsqB)3?$>2`f(c3+ob68MYfTE=rBils=X*D1Gra16nEXkJz zBj1{u{^nz9WgyCxU`b4WFy62DM;o^92Ru*hm-Pu9cI@?&w%nwzoZbP!73}3wD2HuR z*ta*4ADc#pGLCwa%_PWXD=q1&1NUAWxaOTo%hBD*NK+s6{CO6jTEj{6#P)5=0@x=< zWpnJ4eH5Es6jZu7R3VTCLS!{c@*KE}AOQ+LUD}lZAml^val37a*P)DK84QwUU}erc z>_FVi@*zj*J3cl;ffIXq1_7RR+mhxI&gR<42M~~MTNu1#ELOX**jpKky_K6T-4-9%OEAGxnZksC)O3NkmG?;$|^TlO%UMvu-Q^`vs40 zEasntv6$_~Vr^wC)>g*S9X=QUS{D00@`xZ(HG=-5s!ek{tciGYJ##oF$5@T5(`Jgx1M`ZY#|BIz~BmFd+sPIS2kE`X^Znd6n ztMY5M2L`7XSI508)Q~Jx7?y(~M?x_so*eZ_o|W5qRRf!&>D>j9+(;nqNamn=IM2D& z0=^zG>0Mr@UyqBl++{xuRzf4+6&*l%Z+1t7)UB2R;r>Gr;ezM1Aen-EhGuy}M54G^ zmibQACK@2SC8VB=A&DvFUuHM=i`fv% zSHBDAA12|oV%UZ;{khzMm}E9ebKAFgmi{NHZx&FyF)kKAs#XXjZ=U&YcgST5oCyUjS zRcD3Sf_PO)S~2t~;9k*{Dh-mq7fZ2v5L8TcDr*eB9w=z|Cv|>yTX|W*6G4os@|sc_ zU5j_sGsTk`4R1>l5uxBHRxRR;F>6Cbsx4&I#S+YAW0le3U^8nWJ&87Ii|S8ZAqNZR zVE?RefjUGjR!`e9Zqd-dIz+)S5060jT!#`Y+KF$zzUEhKz70B1=>TB&BfR9Nm7cT% zXU)w8Z^3#X!IgflHUbCq#hT%@ZFtHSKkt3mhNEmhq4QQJfw&K1B_?X$DBEKAsR;<3 z@7aDz9z-L+b{GT~Sqzom!0Cwr8LF??z@#*vLlxin_ zy}AO~v#wBIE=*sl-r3a&;rEBor-WiC7hq(B`SqIsXF!<0i({<;+E~i|_7W$ZzBxO& zx;QR0cPZ0fVYD~lr_*kPVVL497W_btUV2&y?OL^gIDJr`RQr+aX$^485cjo?@i{n z>{OP5)dMQeUCmV8;qrq*D%@7FP@H6Gu+gvT;b;g{QaY^Pxi zWl7s~LO1av>`9Q!MS>2*9H74PlO(iaP+Um zYwcTSWr5-QB(_m+1}*_70k1@Wol$}}VD9-If{1{|`fHLe4<(m^_7SRuZ|`N0r^O1n z^kpp#V#rp?TT@PBwHNPaQ~S8SDjo}TMFmtAU^#?Y8J_<%Rth>q2J-Y3n%TCqUu7hc zBD#}V00i<7Y11{?T9ks3e?>f75u9QWN9n2Q8k2>{l?we!bLuom0WqSz{fcxx|KSI% z64}?cd0Oy}$i$|MuVU%-{a+^^nZn z-lwmpGt}86KeUjCntgw>=(f{kP4e3nx7*1>mrVV=+0GbRKd^hv1gfjR27~&CC`)bM zMcqUKj~Gw_4}^SR4>hp_p~TQLlN6$|hLZ(g4-H8$o|!(CWvl+L;W&=(ZL<(w{~~pwxc<$uCrlvosI6V1!xjrt6`C} z72KNGp!;bI@J|emA1?)^d!B`E1|Cf?CC13Y1J;2}i-+m}kT_+K`>oH0VTJA^&W2E< zn86pAjdr+1*V!<;&PI14p2PyLDNAir5G>&ZYz3KxH!#pwnTg;awum7#BtV{pd9W-8 zT&Drvi8(Y@a*$LeL2Wi{NJNK^IvYs&*8(M5&&I}*47~vVW_*Lq4x-&^7?Ib+E4{4G zSa;AWPyrne)~YevM(Z28Xmj~3eVAckAFw>Ma?onFNYYvOQ1M1qUfH6>rt@i)#w0zc zSo`^c%?2vrrr@mD1B{raxg{1H-3IcvkZsej`^Du&vI>&7*3V{Xnv*jZQ8kek z6Cpg5pkyMmCiFp~MtNUmmN6thjK{Y{3K5%3A}$~;AdlR%O5}MZZ2eSI)xFA z4`s$PR_X{`MY)N>L%FL`)YyVf7Qp1Ci?A4Wep|bYY5n%EO$&QsSZ*e@+)RCsrb3G3 z?&dL0`@il#6u-hyy@Qev{)IIAC5}1@`YLA~&r`IcRG9{GYmeWP_L)8prKnNAD zWre@2j)8b3PP2s~)v9j|q^(-r|zPG(Rt?D8-V zqw9sHpgK>UBZ3V+$Z3Mlr>dU(`s(C(Z#ugl>d}%};(c+&qkI8kC&(#(U>~(|E-Ul( z@JkCXYofws&t$cV1hOv&9%IP%l%k?Y8w=~I)+MK|1hV3q#8xdV)5^e_hVjn?s8t6P z)T|PlqmTBkrsZ7)87!952jc#}?AD5J-#vN#?k~N5J8Z3*Pu((6-qJ`1n7_HP_}%WZ z#cFfu*Pyf4IWF(l;I!AD*Im!aj}nkLI`HXzP&eg1y!a@AGE#zFRu-FX%mf5-Y*q^X zM}&+JG%|A^R6wA0F^y#pGXyx+H6O8Hy(tst`sfLEBOrRrJP0@;TW%p$;+jw$G()c` z_po8O9y22Uxe)H{Lx-De!Fr$#Z2{yC0?-^-vFk(d4xTN(>F(b=}9dp&C2XAG-~;26`T7L*@f*sQXSA^uU=F z#a57mBAS30(2d!i9Yn|`LdXCbpwM8jGk^hd%SpNMwNPv1hGyVl?FyHpK!NRph3;`} zXlcv%M`uIpH{noE>ceH4Pse?|+f;Rx!~2!3c&9^G*%QFmWv>aDhkKU2VME?~a;`VR z&-DeL*7xX7E>Di$UcPzSD~>F>w&W7ip%YhK>mn>hy1r4bUvQ}zam^w3jgs}Ae&&jp zd9kq)Pj)F>AiK%2;ubA-$2yvx4+PFh1_1b;%0m^Gx9Bg5leybd@&=i1ff!A0d9{Zg zDWECK;$;V=wGfCqKh4$7K^>430uhnA$Uw2=s!L?5Bh?NLQ5%aY3+^vI4YW49YDoma zMe88a?lEsODt%@7ea20I0(iqiz7>cz3*eIK@fVqG% z7F5ZE{L9tw-K=(R#WuR(&UF88rD{gDW`YyeTK474MCos^AsXohr;Dxv0G@IK4ZRHt z_c7%SFf+^3c^-)_w3*N+Pcj8Gp3OBEnoOu{iqR*q!hBSeXxDQk;Gw1o{ZMM}+*d8C z-FY7>Py*aIHA+SbI$j<&NLaw0DcQbzeWq~!E=?5Q9;KnzjPe;P4ZQC`SbVO}dA^uj zUp>^Uhc~`Ay{1DfXK{@6y%{zg<9Pa*(`v=M-1b0)m=v_TLL5?Jxr;GS#R=;9;qB;k zW%)**EN+fv=9)=mIKLfDpOynaertqv^fIRA{|P4XzPB&Td-GfT!93zqOwL=8$HeF; zxqJP%O{0(cVUr!IEHVhLP;pUeFuUfCG` zpg-u#v66t_lu@CXViLc@8&9TQ324gUQcxhr>17G`sQNBE2kEZLe9}54_M+nuQ}sRW zYZ6^&N(GZY4@UAh-(E+>A&X|q(2#y&iUG!G#aU4&Cih=HmAq-aGKmN%?_jY+FRgpA zyb81$WqZaJWm8n2VU%d)E%usq6vb0fsqH{5&tRz3aHfAQl~#yfo-baHJInE3t!fD> zk?Bl5y}DcKH7BvW>fzTCT}<^OjmYmVIXy4}MZi@KnDQKQ4Uu03w_)iR`$uxGPgT_D z3@g-FYHM2WjiFN1LrrfAzMR`de5~UAlrJB|cJ)hURxK?;WMKfZ6xGgBC#F)evG|N9 zhE)^_8x*nfVnQLOWFgh6L{Y))GgkFw2oc5Ps-KW0CKp;6VvOKYaLH#yoPM{{enq;p zCq-k?&fySa{^1_=lwT`XY@)eH`Z2!lPZddd5kHadaL@aA!V%re_S?_dIfDm=`k4`% zR?gJ>g6HJxIQsP4|6FhU`_Ue_fj;g1W*q6C_Uk9^@BDHcOGu25%wBn__9CM}9``@M zLqSd5KEMkMQ10W5_it#Q$gJJm{T@PLT{`KxuIk-}sM%$fyuBRnYj=6Ca7y>K4WV^t zXUlk6mlhX@uA`R+Ku5lM^Flyft92=A-g_iL0&tC#`EL&GrYTq%IVLFDDRmEpf` zV8jNHyS4}I%+xpI6iS9>8e2&Oxprt7Mo1km?WH}-4g&9y>AKZ^u1e1g+(!)Apv(u5 zI0?WnkJyoyW6z(tR%$lMw{#k}FT&YvwZ>QM&$1Ewvn-^0%-4ZjkBJD0Wn}^Wbs@-v zsILwF7CQpzC@=*k@aBlD08Rj0V3tL$hcBW4(H6rcS9ZldsB#J6-mUDzUTYtAEgbMb z`)CB5H^9qV;Nc5c_wUcpwhaY$GZgEy%&dD@7>aJF=<#h(ljf%B#CZy;Tcik(H}Fv4 zK$#}!K|_)kr4Cjo%%^eggFGI!J`jOk4(TDHhSG;%8#A-k>OZeUowhKuu5mNu`TN5; z{IlV0pQVf(M-?V(`ehYJ>QUdTi8~hAb#g#h`ZoHa=8AxNuJ-&`NEaD{G%plw` z9H)vj@kll)mYok&VlZNu)c8*E#xAVE(l6iv_!AR zKik=PbNI`XqboeyJEz3X3gTobdPisclin~Cw>c{C|IBP(E1}OKa;Fb1uh;J#vP624 z5k|xqj1&_n3s-X-;je%T-r39pe7}A5C-P~$`hytXj$iP$vSnjmE z+*J2?Jo;)@&Vxi)Dp3Pkm+tKp9e8nYc7mMywf`&K1K5H$!rFg;-U=oJjF0iEqfQ5-g)J%;lJKCMciv z|Ficl&2eN|n%Ew{BC5xxlkAEJ`1`@^>?{xfNmK$P3jmXqy}|esh(sU)6%T^Qg{;l= z*vL#;jLc*jlV(OT(?$y|^fQeEOuA0EdB|jFrIGr01VJr-;q@ zI}i^Y?H%tNusP5mfQ8_W3sQ)0ljs;r)BG)iy=Dk+CpX2A3G*g%_Yzenx*-F{{JImi zzH`t`av_g!(GbRoc4oZW(f--aK8In{dNRjIOL!!vR;IT%qQ=HHxQCAbH@-d>1Xdop z%;{Mf6UA^6o^obM6%vsVJ_TAWhrP@^j{cbo$MVy(oL!3Vn3**N6vy^nupms)$l=F8 zNKRfMHq%$o7uwy4{iT8}A^A2en6xs{S%l2<&Fk~iH~5$k08qfGxkOrv4@XgmFs}?# z2;~pt*IUko7~_^T)22$_j0SdZ|J4!f{^amA=$Q9jAK)w4J2*Uj{$}s=;H<}Q%b&e6 zj_7KHapvHKp#GWllE=UF5d+CIW3mZhpCx^YRZ?L*{LSm*qt}PMH>bTfFJ90;`FSy0 z*CY|azmUT~^z~#WmJKiP=^(h9{8_(`&)$zt{&6?Ewtnh&si2|v zV$;DNXA@1+I~@Mqp))k8z2@`3OY&!J`ST`s?n2$t;%%?HWt*%ygq1^3{i6gIQW7b| zxPZUV0%!%ezKIhY^p(v(1FGLod?SHauNH=y4_r3>+pP+<`WCbVbng*Q`niugA^Sz)F35zm^(LfkZ$jGkCZu&YA(Yfv zM(7)WfjO83)9`??@O;ez7{gAX`bVcTa4$rFhe15c$0-b`3zupTG|)5FO$dq=mi@RU zw5sj)H*G>FMOkY@w!XCu(Jfor5Z%(fgewMJ2asP@V%jJTbtBS?P7qKEbRT5pG2%!j zm|o-90=O##xJRe|V?}}P!Ga>UYD10<2|@R|EHdy8 zo-2v##q?PTrz4d%)bFJlFg6;=y9YNp>OoC#Ceh7*Dm|Bz`%mE2RwcsevY3_i$`Pgf zzNf>TQuVXyN`U|>VMnA>qL@Q}&4olr*PKtzS=;C)QLc$)Wj71rgEQ2brLyTX9DC>4 zllhSuu_xJ8s^Co1c|McHa-rJd2}l&RC@05jBPB1{=ZPirq8Q+EPN<59u=GJ}aFHzs z(>D5vmWw*qeAD`tAfURN4c#DLKL!|s)pOSBp($^pI5MqhMsGTdbW)7o61%%@{l3uG z@JF=Y)SbHslB!M|Mqv~&Gk*OV;6sGs%|y>wKUwCzHX2Hj%qZ=Z4}%Y8s}QBDv2O5ew8tf;I2gTGFB z+1!8oZujNecT~8Az@2gpgwFStjBUPPVb?pv-iogEv(05jgIRR0RD^oKUI>a=-yO;0 zW5CBKYBNCBZ_(c=g|fNm3-xBRYj#fnf4aTOC~677h`KkzfuZDX!jaV8Sr$NKx}e!~ zD#LVkHTr3Jy(Ds1iVOBmq)YU6U_z(=b-H)b=hx^<@6h*!Iy`?p< zwnvxsPJ_^i!)iZS9CMS^)5I|tf_X18RYu-Kqo#&X3SOD9gD~ z=k?wbgo;OLUceG8azP@YL}5LZ;!h-Ii!%ky$RE$%yk^@+G;%Zar?mZJcV!$HR z3`ta<4ER;{h#;3j*-V!^rOH$$>E+qPJix|dWslABKQ+!O82LSgaaVVc-ji$wCGMF| zfKvc^ZL>&ghcK+JwaX{A`FriV5D6o;xqs{L>JA0L zp(KRrDkNCJHibMZfY&}q_+pSSCYobfsS%`}<0UwGM^OlO3+|U?7=doU?=!33Akk|A zBl74h994x8z*8XY2!Ku)L;J+gV=VyTVFH*6=pMA3DzNM@b$9L7`2;^h%r?Tt94tTb zQ#pio0iJIzM81eJn)4!>+vVz&Tx?a!bk!R}7}#m^##FXY+O6ZlMYN5*4y<2Xahi9m z#~q_3jNBnr&nSs2Xu@Z`#>xiP^kjuGf!MBYiR@~xxHRW_$0Lf?1=R?4!z=hZLuSe1 z8+Y<1kouyo38s@f6Nqx%G*xa{j#}havp)4@&q2eB={&zUe?b*tl^0_&wht(U&T@bG zwR4Ui^M+dPUD3Tf7|Rb=Ri^u+1vdGy>H>p(i8w6Rd~azhrxD9S+68M^{rcsY|J`GP zy77p|I*v=Pmqn+u*@6d&GUBl`DK}K^{!T`ghl?l`R!5AJ&lxbgtr_YB-&UCml~^UG zYeEpv)j4TvFOzIn23bxs0AW3n8I!40KyIZ%e>3`+wDyv8+H=7`74ufkMCM17Z{>f> zh_@#`*A}4?T$FLc2)e(d06(1LmL0luY3GePW5So^1(}rDDB8^@ZjgP0gVJKqfmgqC zYrBKZbo(9Ht+h{Nx7OAKXu7Pt7wwyO_ZB*DLH@`_Hq-Jgp!Z0qy;i;HIJRSZkC{}>cb4)_PN7jVfV7$6>K+^o zu>C?dAV#l)BruzH@_nHAPxtyKd#@pPyef;ljaYKO>oh!c<0qZOLGc%a1wIx-^UdEG z-rq6KRsd7t{Z z_@DZ<_*>8y!nlFrgPNXg*|DGC-(ei5iQ`62Vn+b=p`zIU?;PUx2~@_QIiG^3%d&kd z@h!9Vu~=Xe5G3$AyFCWXV?qg{u>-RqOWAQ%qWAU~~7yzgndK!p5W02QHv1>xr z1vniK@pm9}EYtU?_}fFlw*ei15cyg^fi9U%>4LbL+CsL$v`xc$+>b>EH^5)zW69OL zzI-$f`e-&22;PiYstB>%5>;U7kE_`Rvt40KDVEx8=i3Hg@%P{(1F2ff*caioX9;#W z?6R-WpQH!0k?1(B*c?jMcR|+I&fHMDvO7ydq=Uyd>)ghHXMHU+nXG;uYHY-v7F zM5u2=H)0C{WtIl$W~e#X+^~H)UD-gIw*vm_$>G7#-o^Jv2ZwJ~6dqUZsIL>BXA>}u zHJd==tI!^>X=m{tZD;8WrOijua-9HB(0A(<)2i28nKE@c-VgTi&BQkQU)?WyK25Vp%6;l>kBDj|#JYs#{Q z)@FK0t&@hP*1A%wCR*yUD;9Dg7SdLb-EeL9h$%(D?zR44`G8r&NRu z4L6kXyHIz8yrUVr(A`WSu>gKY!_!d#9cb0{8tRSO%4+Scqg?aWuGaatS6Z!I!vWEn zgDu3@5r0Cy13d@LAmE?|+^VF#5PKUYJ*}146T5}qaoXt8jPi0TK_CE*g6ixttwO%07E?QMZ?9dN-I30PnSSqn{-sWGg8 z0GkShm(^rv+XnMT)ta3Nmoy%)Y0p4f%db6W)Fk5-auPJ^9zid%%s&&9kLHakXSq(q z4GH1pCD1RyCB5JcrFTdqf!O6rW6h}368smEjWY%TPv(j@yI-bz7ozqodH=JMJT>fL znN9ABO%7;QcQwJCUmv z<;~7{dj{aca(oTVA~jJR(`VWm!epbBR=a%`wXNs9JNUwKqcBZ@tVq((Npzea`A`cO z1_YiE?Tj=Ri7#*<2R7oMR*I5!ErkA4h!S_O*3{9KpW6rzPuo?ey=Y0*C!PfJx zZ$0nZP^P0U3hH!lfHkPl8*C}{E_r}((@?$$Zeef*>R8K=7`lLZCpuHve7|34`*xw#I$a zxh%2;?<&or5FUB9Xt`2^lwXiNc$KE0xr;;HSUKk(G2Lq-Mfh+~ax^VmmJcL#z_mpD z$9r~>lv=dnLxq;kWXWjIt0}8r?A@;iD8CjEgVaDQW zhe(S=&5Ct+MoNZTN|=NWnH9w01aka=$aKmRgSS{fPgiVRjiBZT68G9}Hcx|mN;{+_+wb25V>CB7vK{8w2Bi&d7GikQX z3*A_QENQ)`d!hssb-82~c^kO{#2&_@I)%SywD zP>LF|iSiS$Z+qtw@h8b}p(&4QV)2c-oQySIltjuDu4Y9Lby<~Njc2dNpE7D{P5 ziwGROrjY01Iio_dHB)_Um6Ffn(`?AXVrMnMNLt-J5sk96 zZA?L58+Foc+Bs?>!VfTo&pp`>q3mdxIdA*P?v5fOZB-jgvW{!Q!8J{2{ze`}DP!m} z;D5l60IJ6jNl`e>C_hKy1s^>KApymVV%w`vwr07HJ=rx!>90N6n?0mkFp|$`N@mkp z;TM(vS_@_|v#!y~#AvZzZ41kjG*^pbiIb9LAj*A&!#BwbIhCIhp>mTb;-orS7W`zI!b;eb>)+8k{+@WXM0xX1g*p4+sbDvmx})q>StD z!MXo6>Tgiye%0!=3O;g6zqQ`gPR?rmrY)7-R&G~(1@k81w!O)uJ-KFVtlzP^DX+DK z3amBzUwMVRu-5E))eF0agUm>ep`-?F@6ZUrPZBsWpbut@qqhwjs}#G5X=$cr2NC!Y zbPymKjtA&N=*P&}RBLe{pM|PhZ=2N2=BR*GuP%tl<+d!yse4)y!uN6z!2}f}{^nSQ z=CuQ+>*MsZa~eMyT)$m8%j{}QW>;hCF1v;jY!~sP5O{ivyr(p=5(JR_(21?oM%{`I z_e8Ti)Tp?K-+(B`C1EUGvyleGgjS8oL#j~IAJ>?SazXJIW6F$4+fs&Y#YT*-#?<}z z4c0u{g#3d>>0SfAH!`t-hLcw45W%VDMF#37LJy8Qz>HM(5dzWZ;lv)8x)u8sV*=^9 ztDD+Z#$;|*elXnMwlS^u`$yBE+K_+z3-<2GPzCzRr-1d>H1RWO)_UewXNxm|c%AYr zma|q2Gn~IwWeY;NI+_t9Pao8xr02sbSL8JwNNsHKAKT3e1=$S)&`2qlM#KZJEh0woh=

MY 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

$0iGszNZ+AZuT^5Ijq5|4yIP{u&%P)ms% z5#0n%L6@&zeqsY?6WaVN4d75;J1=I`Am(>n-gdRUjf>WbcgUUaxY`A;Wk`5-)R@f@ z&T120p@4Gc`I=r;JPWA~Xd!ELWkR)-+->pE-ftK@#P0zRDdi4Pd;rq*TR|bsrseoT zz(-Xp5$>0g4-~hRVcF-Y;J}1&w(4{o(j)h0ab`?2!s zGV?Sg=W{#|D&K|iK56az_UOME4xT8MUp+6x0?KyfcRj1(znLaL`SG!$<`LeU*+gk% zhI4e|u^UL;YQ>@^3Lx48HqbPV6l5O$Ddo3oY4R1SFOB(Iu_0+F z*m@^@g2M}C`j+-{x9I&o(rM)q&71geZfo=qnigu9gETF=m#%kl@6Xoyklr7z#6|Zx zPAz)wUF5~VZp4u!kHUcDu>}VOG$(P8`e`1!X%-Z@gE(1eQ>(~*TJjrYt)M-ED1KNK z3%wH8KU{P?6?oWLX~RbVYesCNWDr}v=ekJ}#JdOrI_bc9xGz zg;iQXjuNUJI;a~^>MLv~m9(r4fvle-Y33r`M;w?^*hMA=jM8S01s(!_cD7W@%?C`Rm3q<~>_28TC z_IaVbLG5d8a>!~)1?T})7FyK82DOXa^tTm%;(DMp17bCl)}0flddH|EVN;wKWd_CP zsR(rqO|6VUYs6*sZ<6RZNU-SY46ra_E=`)9zK&ILee)Cx?YB$aQQTaue!kJh@b{Vw zGBLMOk@l@}@U*v+0UnEAr1=~b`2jap-IF1-Fl2yiVYk!#7c?3b9jp*1D8C-Il0xuW zkF^I}G1Ka2;Ad zh?U>1^#?V9fO4{RyUS9yyO^>FdV65@catC&Daw$sdZ-b*9VG8?uA7Q<3GSD-7~fG1scBW4duB zngyY5`1HNpz&k3oH9uC*C%s!&3$)EvH>)^x6t4!SyoHqUy&^p&Rutw^%_T!TN2nqA z!$YYPYHL7iFtNS8Z#=PH%1+dc4CR#@*4K*tIJ`Y$bSjLjkK0wr-ls4U_N!$3hYLsT zTkUsAN9{khe`+nICY&PK9R>~!+&LgPC%5dZj7oHe?WnYpGAE-B0e?WM&4N4(?gJOH zF#5c$CQOQAv*!i}LC?J&O&^;PN{P&0n!>k6wl}A)?nu_R$b29}fp0r)HSN7Ep}#+S z^7Pelk4O7iLBacaA$s9kj22_jryPAS5~@%p8}DPs4y_%c{!XvM^1Ho`^ZLk}F2|Gc zLUAFf&qtLsl6;Fv$0+|`^!OO(ByAq=nQeypkkGK!q;yAiD@cDz{CmA))idY96L|2P0x}vw$Km zgJ?9t3a$m^nBOG|A+#>y?cT2yJZJ5c5VE^I_;<Zfl9wTn_c zPN_uJ*;-g&UfiMqX@FG!R4nE3E71TV&5_7bDt#ddzY?K2CBDBBW<;g=vRJbU$`&OD zo=AWSPwwPKg^Y;_#ASO;?ovQZM)_p*t575wri8CFK26=i=@~jQ!b^a=gai&{pn2E* zO^N+Q%RwkSlMfnahhJs-&b}*zGeg`P0c*(yA>GW^m?xoplmK1xQYz|8DEun_G+Xts z4UJ)PTL=c$_R3d`JzM$Vw<B5M? zOQP)6?NpQ9)-8-ntX>xirH$7`b7;^x`25xR`Cvs5t4&QVE-_)r(t6samo$uu69R;u+NB1Y6k5DwAV=<gQPweP80Gc0qv*=-g&Ef*)I!@5c`ZGtrdSR49ei0Q`NgHlb(#HpC4oD}e^r#fjMw3~f>S!0t zX#fnZ(kDr|36h|&8ibzrfR?Pgah=c5lK;$R`>@98e3WrIA7z~VvByppL^OH&39ua( zY&rZ$1--)wU8so_*`*8jDJ!u}pQ?@6E}tI~TW)SA#axc2Rn~nE>pp z&MKi%#r?MeP%5F0MKd@)t=;xY6CzRKky}GjqM(p2b#2^#ts9{f8{||AmF-^Kj<1Uf zbUM;V*TrPEtonSU@NKG~8YZh42#c|5Vj>hKte0^Iid0adGcDdY(8MPyz|`neuTsk+ z08!quG|8>*cDWMEH1V=jLF;#FeqK<(f2{zT%FDkjifbLL8Qg$KC*ChV|40it9`_?M zF?%2E?5}L@WYau)c>3*!ZoB!LUbr>#_PXX?5BfVZPP+hCWj!SgJ;OxRXNm*;XSkPx#0Pu5j{deM+ zhTJAMf%|c+zFW;F0gQe0_LjbC{`C?_`~1i$N>4hKy~z4VR^P{pOY8erFKbj?`yTlx zNScdLb-5Rwb#J4x?rl{4LpQi&#CP>FdP3YRuv3d{dg$0Rwb8j&csXhVAxv-{U|^b9 z@)GM8+cLMHXu)PR|#t= zd0TL4Jo7&)XX5Lt>Z_|#)r*akHccj%M|}9cQFyP_#};a2$(|{ZV^ zA0!%5MgmP8I#wD{mw?|?j#j>=T1|d*c&mic1RlIl?8W-Y?QfgdKBkKyB*~@M#LWIw zl1}?YaqlMTdFz3pA}SWBKo2MNzogdT1o6=d{JB=^6tjBBXUB28?a@^yFMN6zGm2{zSPM_A)-@T zO0KDZ44^pWQ%5e+hBuAKT3u4Vc=XYmSEo-NpOU>he|z@$ho2vcJamNu)1dl?Qwq>O zpFe&3_Uz?%S~IiV0{#XFMt5_PS)Bs0j%DBHEwu0R7WNMd&;}0etOyg3+O&4ijS|{% z@Sp(Hci1@3aof2S@NGZwAk#8B5CpE9kaq=mNZh z0LyIo4M3Jk$+qpfM$gu?W8WSf*-j`_DImWSVKW9`Gn-FoPu)ZFcZ?BeM* z?^QFNU8ds;K&`*KkblY89RaPBK=(0qc}@={RXt97u5(wE+PXZv^LE9kU5E5fna5F{ z+wk8IpQb3@L7Q0y(Drx`A<()5**2X-31ITf4q)1j%ODHf0H#U^b7IfVn{pPOXNM2? zRc@|^^1USso0b;T!(p5uwBf-64rMeNeO{J?z7_QSDw`jF;zL_~r_cBx|NiyM)kj&U zQ5wgYA8W_$`kWsmg0b(z{84noVknouM&Xg+JFZ?T0b5?9a5YO=ftyv+UHfj62!%tb zjCa;fz0O7>HMoFJU!y4h!i?U48=LQfE;y>zB6Bqrb zb(+OJ;}5?6i8fiKA4fj)Ii!`pelt=~-?>6CFKH^Jn|VCZG(Ojf<^)}6knIyW!#~aL zjt41Ha-|3~D5*LTY0zt`8tX(|@#@kaK?_g{)K^uF<sUc>!E@V@Sb{R-jRJpW6SplZVU$ z(mmHBIkRvdC}{EgMg;i=b5%mn^qrDi28TVo1j@S@-U>(M-fMYk(&IlaHZJI&Zd6Az zm3FEC6fwhjsrU9P(2lG6&()+|H-nYKjhcY8HjvH2sTn|@j=LnrWdgn6d<4d@~Ctdy2Qjp!} z?ASfg0-bUa`_TfszA^9Y{g{D0#~+}l7g5R0MKzCFt{;av{ZUo{iVU6_7yu*w?KmVU ziV)pkG`%u*$a{}1zLax}>N7|zGk+#@BQ3ezo(Ts>WEDM);Es_FGq!12O z_;3rzk#M`q)bpbVylt5U0r{vbc0x!rapW7Vo(_A_wfC^L2;RW`VvF|%yu=@8it)!; zVtfFB7r9SZyl5B=a)Lb%(R36>GgJ~FpYWpqvM$UtN>cP+iqIjBv~2;(L{w)&e-ruQ zfM$i{`2h`)qKVa1<@uX8#NC15@>aYGq&0pWC)Jfpuh(U9XVR3r_GkcQVnv|V#V)bl z+!^$VDBTZg+;+hRKqyJ6U2=tYS(yFl>PtS1FZ6At$^4rpxx}uUkitomUZPGLb-Jji zhc-_lQhqbVG*SGDzg6QQWy`tAPA-;rlN+w@Jh@uj;K`noj)rjJ^!&&3!M~WT#{)8- zqi@!nO{*EwvIT`9hSG#fW9$hZRk_f(x`rx>=pd_N7<0A4v30DeX0`jDsod_(a!Fq3 zP?n*4$norThM&l6{BW{0_dUGe_CxmrVjaogMiyZIy~tSG>f`Hf4tSWOvCAmC-HSa( z_2-X(ei0@fJN6x41aWX8Y$9(KJJDu9vUo7icB@(IhYAWzfO3K@;)=jT{K|_vUR2sOV|fK;t1I1o;@4)c-6uv7o`&=p6)h4pw2=!9pgvF@fFcA)SaHXb zWySWr=YC)CS=-2&we2lE05oS@tW{yIC36O-c6D=n9kdZ2OY-togk2J02cW}U$m|p< zb0PfY)lAJ5+x2XIB{b!OuOGdB@y%c+`7XKWJ#$o+5N*lK=R?V29yimNyYEJ&89CFE znNl1I%$v3lA_hqZs(zOBYkDmGCuYH4=++he6&-0daLr1nY1j>0VMQ3x z-N&vw$LW!g+ZdYN%y)6{wQ4B;rv6m!*LSi;v>qFba5a5zXS2!ic{Wzei6%ct_D(&B zp99w<5YM*dBg+v(~9n3?POLIV+*rGa|?%UzwhB`re;=Z8XX_EwmtTJ-70C-gy()!oT!@^ zR#wnM)wSigli|Kl)6nuC$Xs|3yMM3)IMQxIos5LL4(Z?807D#%4^9fAGea-6q~p@1 z1CF)JhM)xDZX!$Mot->pE0H7+jU+8IiYQU;_)eo@Vhh7ta}D!8Mqkyu!*zClSSeg| z;Q^DPMV8uVi|1uhWHudgSs50+WuSNE={`1bEH)4vNHDl#LjA*Spg+A}o(9k+IAD1p z6QrFMWUp@tXM$_Rr9)#a1vP3EXl^6_l@?a)6zHX}OM>EVDG_CQOdzdUmatE0hmY8)MTg+IcJFV4I%NY9L8Rp;;zKp{Zm* zkYboF??8$Ot)pg-xB?(aswNluNCjc1cQyf7H9JaC(UmNOy0%I%cQ&8ohV$=65jiqU zP5XI-<^UKM7%AwBMe%qh`VT50B2u+4v=?=+ zsZR>h*K#$YEjizo)NI>J)+5s4qNQTg=<{m(mD;|X-L1MY6p?B5Mm5U;x}$&JSgP}1 zMTG{UIlZ|`g=*9=0lm0>T-aku% z%uy7h4~w)o!U~Cp|5O6JaA9QPsYZ~Jk<*Xu0G3qLlISufil#q0g_6<*>5w)>7~qt$ z9&p}0fzdT0QoAR7 z7rBNZ&Yx-(L16GgpcaC18!s-!nPUv?LDqg)A1la*8yaTPLJeW`{Zu|evuSbB2(0VQ zEZO5|brK=YOpDbc5!{EsJd}Ljd^W`cUCR;_E1|_?a#S5rOMYx#KF<=~eeivPbCeT{ zP=b{57v{k&XEP055IZk&^>i0EB3&TGay^N|`XsLBP4R0UDJL3#8+CUBgjnq`(Ar&B z0%z2M3%6Qfs~B96oSN#VpdzmiJueI%!xF>Lu(csbAt5?>wkd)6tKLze#Vm63q zqHS6KEnf3vET{|3Ken0~4XObZGEx0YJixhJ`Q+}tG@nY(;(Gv1okCn~dwh(`ZQ9p| zReD3c8biIf+|vi|9`eCbCLa%+a2_NbvR=v#%7@es>-|YCA^gOpj>FV}kw5hFu!xET z!et!qNOIIpmfNDRg+`JHS7)#`hUiZtR?QI&Aw_&S$FUzaAF>6849U!-fvC!RVH#Kg zC4X@oGtbH)7F*Fo_x)Z1@6%d|6TvV{jNVZ5*Ty%|m3oTf>{b_Q7cmwK-24p6f|Rb5 zVRX*4#&9?qJWCcjmHMZD`#)7kE|R~CKmFVPbv#%r50<)-u0cyt4VXZBII9+UMTflN zw@6eHSh5%wsw}R+aDqmYEP)a&RvNWxDVjs~uv|<2@>M*QfuWiZN#NvKNv@#FGF`|h zgVPLb9_t?sql$C!>(l?Ay?0q|BuUrA_I!$H8%p5@IFEH~6`u^WN0)apT z5W!?tcXhL8P8I<~czC$~?*3oD@8W#_z(5)}{nyI>dq$b!)Jy&tNKW9h!SA&F!?S}E zZhC@!aq#K2{Dyx&Mfm*z=PwbRWZ{IadbfW-|2aADoxa-T|9*M}2A}>7U@NpYea{3M z!Ao3m_^oP(JFi)L_AVHOO>O_9tic?;u__Mk$ubCBZA+R z2httyUY)-zZa?9q()_OP4ke1F%&x=ei&!)G-AsC)kH@x>2-=KdeDxaVi*`=Qq%YnK zPBh&`aR3_MKj>N7i^ItXTEd}Zhs3u>V?Owjx}PI)fVek)ki^GS9N)Bo%Y{F`Th7BBelz%!{Ls=P379ZQwTKSJ7mh!#66|&Ygix z>5ur2PPHE=XBS7BrJFQM1F(VrxT}4f%%Wjdn%UQKmD0o!VR(E^AD}*c{0@KQumTdx zR6gRxr;~jQ9R4-=UnGmlF`~cUL2(>mZQz}MhmSAtkCb!#VdBe^L678_zQLbTf_4rh z;}xaN%FPz}(i!hz)|)f!Mc=xb(p&ByzkZERVFrD;e}8+&-woEk`as_-_|K2W^!MG6 zcbVbc@9-%Z-u*})U%&f^Pw9y7IeAYXZYT1?nN1(2Hhp;i<_$hfu5R&w$<-DA{w-g8 zbBC{PZ$fzsB-+F(z#4kdr_CDoU#N@3vnmX{per*=T754BfjH2yobCwn5Bf1nZp=o$cX;!C)9Tw zZ!c+h=!!mH(&uaX)r*vPsQFi-*he@Xzau8kzhmhS?z!!mqO9U!_yzJdGm_LU*{}C z1E~n$?*j_~K>xvNx0%&%3j{D*sGp0Dr;H%s(*b5~o@hTLmB4!yl1dE=3>jdwBfO;D1g2 z7g7fqH~3%Prr+57uSGX3P*p*5elp>{(tVY$%$l$Ix|B?M1#C66@qavXWFCH)&n@JD z4)M3&;XwFqpZ@usge_592c0gjPP7lm9+&OXDhgrK{-}Kzli5jUFCFHG+KUtTD_>%9 zy_J9S5;_^ivk58U+WEVq-O@*?ec@HqG-8_{(>E{f@Gf^2cjC@@S-wS`RCl=P&XgD6 zomO{6*(`R&kGv~>Z=BEZkh37#Q%VpXDa@=RR z2;&vd4>=Y#wRZ6l(~3Tkh>g^M@i41*x|4EP{zNSlOJD$2NO|ynJag$b6b;6dIhhMM zCQ0*q(QsZgRlVZm!@H5R?@dM4H{BEgaKobb5Kw=_)SRUy(Y!2oA@lqYP$y+)@VNOhDA^q{Y zL*7q}N8pA)o7~+1D_%{mAeg;+=n%=_s0=AsReLa|ko+MbQF=N2;Pw1=RPSgY$*nY? zK2S?}1I=EAcK5fb+-TX-5_$ae=+j`OlA}TmzBV*O7Bd^d115Mt{slwWG*N_GeQ$dy zr-cMGja8cYCRZLQ~5H#zQJ|TcztW1g&hV4#SfA+@!->ofuTPwnX@$iOI`~n2K|;5 z35OnraHeS>(3QHng9H>CF|F1Bs_#A=T>vWW?eZ_6rk$Q19bD97w6$ZyI^4h5zZ`sj zdeDL@tLBQq+0nr6>hQAE~mxO`vX&=w|*JAtgI&Obn z$L+6u)`V>*Hn1o4)I*Z4k2q-@*x+vhoqgm0lL_D?0s(Y00wSaiUulYjUL6~v0U#aJ z!Bs%qZQpQwuqixmf6v?B^Y-^wwEaEh-sg7Q``nIuU;E4<%W+cF)3~7y0-0D3Z2B4i zbqHC3yi^eQ7qT^gaTJGE0(3CiZFDd2f!>7LQfoDX#P)(f_igie?|a_+p7*}Lmc4H) zqwZsNK&Rsl=ycox+vm_hr8BVoxhd70ixh?MVGxBLD3ejnka&p`;ak|lpl5^%7-EM+ znw-G4;{d8Wh?B|=2zo8ccAs~^=N<5Q2mCAA0rPsxPJ5ufX%EypIso;K4nVzw12A;; zfbq{fD$v@S9k)NP!}jL~DF5_a9i)vqkgq(%IZ{9g01y(z zREUb05$Y!#fVPJK#kRoD7Mmh8u^U-7^QNvi08Mb$t>I!nZ-39*-}CnOH{$@@viG?i z_dd7d-sg5Oe+^LBhS2W<9o2#{NNi)EntKG13uOI4KJS2k;|@T#?=<&54GooY3xTpdrq<80SP4Vyhu;M$0|}FZu(9D6k%lMVgm|+3hlvL z7^3b5e*;XwOuUK~=o*2=Fv0Ejz-@#)6a;zR1E2T6=RNS};sD&b{dpa?Kdo&B3sY5(uR*)hd?CW;y66+Vdf2h=TQY1h}z79eg9msJN2cSby zVCQ-Jd*1$@x4*wl2jJGd&+WMPxgGbu_71=ZDKEkh;P~{xz(5=k9(ZQ~7zcVoY%d{p zI)GMqNQH*N2T;$z3>*o@4iIKQiB-*CP-^MVs^9b8_q_N0O*;U$?to6m9nk5x1GaYn z#&!^!AVc}}t0)*vGX68Kwk5Hjg9 zVEzc*kDj4$u+)Qr!Xwaa{zY9x;E!)FF3yO$5&V*2%=DoNQB@xVg?%r zPbH!L4Ce%KIA07IPHWl1Tq1aBk~wh)n1uuqo})Js0{DUu6ep7#D)(0O%Ad{9D2G`) zkncKbL;?5={>7Dv*;|?N7HENucW>vaB5b^mi0cYDxz>sJ)ulcMq9n0_uk8|SHi91pvR_ieMS-vnmO{&QfVz$!!F>S~W6+if0|eME(x_1> z5*nfHdq2el8|Gim1e;A0Y(8#+&Bsk}d*Dv+NQHJ3g0egUqZD8-h_u`aQ$gfXW=iat zQ5>39gls8;3WAUj0QPXAL=7C<_(>+Xf}Q`9P4H&jW;p4q(*r~xIMU)#|L%6G$fFv5 ztk7mMLcG~Xn_2u^qtBe2eapz2`#Ew=6%2Hk<>=Dt5{+;=37PYwB)!*8jt`FkwiS*i z+PC5%nh>|q3+iQGo|b6-O7w@5m+N?-%?*_Lq_DB+BJiaU;`Egm6N(cq0Tl^@IWw6F zUlJFJ0pl2}O96|VG4y2tr%a6MOuISO1ej82;Iu3U=H-^MhcPZB5u9l+2EdfQ0|wX8 z{@^8n7G{9d%vL%@>?LSdqLYrroKR7reC0eVz)G7FrnGi^#&^M(m`||qcD@K{b!Vk0 zS7Zt?Q(`xq?9o&fctTs1r@Voc+gd43wRbk6DWB2R+Pi=L4^#1bplyRB3<_+RE%B7a zE%vkjdWr_WfeH+xtt^e0pMSL{uXqF-is2Y-^sr{W6Kai-jqo08AJN+xehFNS#;&DB2XL<1~#3f2Vc?k`x=TMq!|*W@Yzw!HZli z;v_o2$8rT$_cz*?BRG8y7JNAc^cXfj+JgESL(27G!zWoBPGNg2xpmqeEqB<9u;pOs z5WHv~TJ6!F z^*kYO281!@<7$DS&sHA@%ID#D$c$NpjlX|cel}l5 z=c^!7p1uLFTi)#E3yFa`e}3(i_p>1%#ssNHZ$-3a+7#kTV8L)iyY8@A#Anv4xyq^-VmZO>ictQR1jrW6H(~yB0kL(62jv2QffXw&zniB<>m=1w#n(EHdkK;j^Gkm|Ag z{zrZ4*fY!hW=^<|xTv3q;zX_>6Hdd2X*lZNg8aL(1=V8_>V3a&Zeveq;Ym^L)DtK) zxl%f8p_gWpW2IXo?JM;H&v(EC4E_WZs0i>VfJDd+9auXd;Al{L2b&C55%6ntGofnb zfYQ5-z_%?Or7o+Kz#dFAyPBs%ECkI)nz8YiH){ya0IDNAgpg9y(pZsA%$U9)7m(+G z1454;LpNDF(M_tFX+On8+fOmkcKeBjUyP_P1=V2&C_w_Khk@KIRAMHWWiX3c7Emk6 z>P6N<z|)Tj{T>wbTQ4oNRc=R7^;tyuSuh73+6! zU<~DB>C%h{Rgy&ZW!XSVoMWV4#X@46%ZI@>8E5iZ#n8eK^irQg)M!E!R5FS+8p7E) zlbp4=3axGh^%kqqQtxD@EPCj!c7s>X3aXdf&6DBvK)NFP0fH;A&rBBcoY?C=1WDD- zFGurVMZdh7|LP=ET59nw8U41$Kkn@O=@0*&!%D+3!2ZFm7SEwc-V{JXE7NQ^l67v))ti1G^cyBR8g1K7w zI&PhRJD>IyaEh@XkrK*Z5Me-X zCctw6WO`=U9ZdiWjo~N8FnQCfT)33aj2O`7P++Cy6N5g5ScZwCOg;lv7&y>~zimiE zA!&X{=aT~~-+EUvry;PI$T=hSF*G|PNOp+IoQxg1{@qZK3|)bs02t@uK2sBPN5T)= zA)Vp*h3gLJ`d740r0A(d!&U$(mvGln@3@7xpmWotbxl-5*t&LQ2$T9k+{L_c$lm21 zNE)xD4N?fyD_%6KOSLRfa$zt%k{rat^OGKV*UPJk*9Y^*fzR*6ex)d5PHd0^fPRDi z+22*R%ZEW9E>egdKa(Yx2$toF%yOA$aV5j^S88dJI9!dfsq-bbk}r9770R#Vl2uEx z^meD<;{p2*rlh(`@C&Ri_)|&JAaOIbEa>|r%m0Kp5|axS=prF!YAi($HeK$?AT~TL zyo1iqR4+6{w0fr@hI}!-nqf6%`6yy915@@`llF=RUoKNefg9rVg9eu=7z~u5hIe?4 zb;2u#cge+-vh={JQe)nng^SUr3D|-%n0z;T{5WORe70})>4VdCy=`U1ZFtR%K=a$0 z0BxWk;=>he`P@m18?@yeRV%Pq1>a!XpNq)`8}Me*ONY`;j5aUt12 z7ytx7s@D;1C&p?oPH|V71V-S3_*VGQsy@g_fbNPx4@YqG9=|!oO_#6sFYv?nmv9#ypB(># zN`YIZo2|{%V|jER?K$3H=NCvz^)v| zmIENQ)G^XXL^kpTJRkqhaRgE}J!HsxQ3R$!Q(&sQBp*AViACO%hkm3x)QC@22k@)BW!-vp6BHToqqJ3E}uL~&zIcP44#G#zwO z(#23O!1B0_NA?UfY$ECT}fKrYlbY(b+F@j&&kOCOZiN1-B5h zkE%EL1-d0CazqXR4oWeF|l_-#c*ehfF%y@}&irmt2bMT%4@5 zlMnQAp%ZUh{?@DSveTV2?n~O>Z`7{1x3;6Vu)~{fZVaR%^&j&2YP6VD?dN=V{^p84 zQtORLP1aU!wl*0}o%4>H&X4Al;|+JTkBS6_g{Wo{Sb&(aqZCB!2!13e!G*?Sf}l-z zeH^JeGMXGaHc)dGx)ewOYnwv}sjGm@bagfQwK18=+Y^YJOZNrbh_K*^rnD z-g>uLr#V%PuBtPG>Es5dd)Zx^RRbXV<1ze`w0q`%=c>&jZ z?_$y$FblNy;^Kmc(Q(#H?kO#X_xy==GoIWdCV)M9)m^M*blm4#8tvnpE~B<`naV}G z=>faVk-$X$PCO#-1+l(fjgezSKK_`Rm8aljwbL1I)GFzQbB;*gLC>k_Ye}e`a=TK+ z$RuI+iwrL3H@GO@?~{+iBVjyBA}Nf}Z0Eir|2V1lS|X`82bM-MTGJSdTm4EjL*%U^;+6XgMb zYZs3|S<>~Gij*k72sEoW?mwQAdst@r<|ourSJ`Kca~OeJb^gF5SwmF1ODXStg$-Uwt?)5~8Aqo&4oUr20Hmlq5xw zp32LhbCiv>D%vDpG^ftXAszCRnI-+>m-!yHEpJ-hy|=QNw#k}azi(ZWs3ugEd%A}pp!gA{ z_H++=<@oXp+fkEe-RRqWdy6Jv*=;llZl_k4u1#7pX(W=mC~ejTUOVVVs%|9 zT`K$xJM!W<6eb8z0w;_OfWboQpnn7Knqo;NVp$>rIIM?!L)83%DkXqhAKrY+SoPyu zzGvu`@#HH-e_knn)hnebgkYInSBlwnrL>P91O#!1>`RIcfCMd!ZA1n{;G*c+x5LB$ zb+{S%DR?MN1K?3sg5UxmLCqLJT!^f-Rw`+&+ozwoRE#QR+VfKR884OXX1>xVZ^>6T zyPaIZY~xnsfJ{e=Y-#mxzqS(CKtRgwnVa({DZJO<1UN&B+MoXL$F2F4@6(iXz1C74 zmoEQ*gjbX79QI45qDVwq%bz@+vx9ch&}3ixw6)DVr|WCV^rPBL%Cf_tQIZ<|BW-wc zPSA%~eJeesux9d{G&qpwR2?M!2ARrD%Wf%lP)!FVBfhWg?7Uhak&di#=|n-~qLPQB zy|_ZLDhxC>P}AWogL`^Nj`-;Wy|^Y*k<#~Kk|x-A@WQdzK_oETmXm_{qy*Dc+*95n zDr!_L2A&0(iv~CpWOvRvt!(Gh+x?5L1{#+5>!b6d(+lnO`TG;?<9n?9ot+*h{K>a} zH@W&+rG92YE+U!FNHK_sgeJ)*g-e@!8|<5)hm%1}sf;QGj+#VnTDkQ>G2(J!=Bn2` z{X@aDte-<#l@V%|wc_#NFVVwmvWR7#ABR@Txq5}W2|g1sSLwUAvR{;0 zs-0qapxS7N*Z8`qFT0=7@TlZdZM=}Vx4c9sKX*(%B8YuPX_4qKx=F35>s2B$J#;>S zco*r9?9#`hKoCG$~grEEBlU+@nT>`Sy>q{jdzJjxqKJe+0U*oyR%Ad;u*MKW37 z$KI4n=eU6x)x^m!UZRb1Vs0ptT**mATOHMl$%a%m^=U631x{t8fDu}F4aWu*$qmn*8(D^+Ct-4crjuMDxjczdM1+8-Pr473-+F|vt=oa#!G z$twvNv50vMSyX75zU0NQ%wlC-Li$FvdK-)0Ld! z%E1WNsBR(U_j)8>n&4*sm)S~EIyu4R^$+!|DFQ2kcO)c4MM=uTIR3#x|Vl#wiu5~@n)I|L_Uz!+AnGxc{4_-qgLbf$xq*2e){ee+wxvCnnZiFW$&pW z>fa_9ulHw1r`q8ChjZF*5kJ%JEFeY6Y+Afgw}-SiWNQ0emqGf|2KTzX=T^7f#(P`1 z?>0?jx8&SSEp5utJyG{9gNs$4pJ{H-r3GqYD@+7)Lzb6pqtQ;=u6SOmMmE@P?Hg%t z&p9z5jMCT^Hj3wBfX9FkF+dKa1%_*z7#|RAL;hhWAVx+2@G| z4rJY{h&$Mh4g0CnAI;!d-2WWnZUmdey^*n}i=;Nv_;hQeJ;Ng)2P5IeiHAzs5Lk5Z z+kwH+1&XKuTA?ohR0z5YKlOb`Iuh6u5JCbD-f}}bjht1#w&&Y{q&egdz;0WcHjA1aoPJ@?BHCctE4M#oFgEfAv zCY8TgZLu&npd2mi`%)PsFyP1(C3}?(6;k_quv%`1F%lUmZ*P~-Y$%I>>U=38zr%hp zDnumvNM^#~2R1kJ1O+aN4QDFDcPNo!xUX!qQC%PG znQ&rfio=JzIC&kRa#6__A`cO#4&*7Muu>rzFxce#98$iCNZ7eZ?jEfu9ei=P|5|1j zzosHbxN9DIgX>{BFI~MXG<@+ISNDJNazT}Pu#~E$q){#17ULzqcCRdzEsU0$`?lsB z+~42#6_5)x5nl~^5lmF{5VrJh;@jlcms#&eB=g2M43l$B6@kg6^Z;FAiJc$*!~W}U zKYst+Z@+)N-;|(WQKrXdCpuai=r^0oP-@nAbDCr0_3cajLLfXisRQJe$TpH#Afk%g zK~zPd#4o`a=LiQOBNPJy<^`c9#{o`13IHR+a%|)=tOi^SoQb9#R8A&}Fh4$wq;{;* z$s{5mYzjbB*p!*+<3)lP@n3L*+Mvd>jhd6GbnCtU#TODOYsT&R9te)40d z85fSXxg8TtG7BR&lu!t0b1mHA&k3!>p=Lj}X!l55)QV z{X+#9czp2f_q$Dbx=Y#HC^IA}D-v^y;L2_nIwiB2O2p_m`$osv?*KrF1S?Ez;3Gd9 zpfHK&TQ=cBL)nJg8>+Qsd9LHRsGze=9QY`322HRT`>sGmXzHxZzUj1^eWU6c`FYR& zW+fF)@cs>scW6>HPt)7%GP_lw-TL1uN~+61oW_QHZj*7H>Xg*uv;J+S4NeAY)oGI{ z0WcPk1ep@e`O_%WA=kRP87_J40RDC8L1X?-%NaSU8f}4L@&1m|OT~Q&!Givr$^xmP zON^8=-rfV{{1@UTlew~Td}Y#V2z-=l1ma=}A<|Pq?HZB3U;pwS7nAv~irh=+oBb=! z#b!l_f{cQuE!$K1*T2D3u{9(w%1;1J-Yt428>QEfdEKlt{R=DjRVHiKCvKW$cy%gkfg_;h2mPt#hbLnxL$%RW)nHt%N&6 z=#Yrr@cZvAMyq3S9?EEtsFRm{6)mMVQpib6z@bDVWUwWq#S<=PNJutvi89FLg0x&@ zS<5o0P}yUU1qpN{ahGl<5k-SlC0cc|_^%IRTs~ir>N`ScdQK_hWa8XX0U2RZ^Ajkl zT9h{+C;pn^cS8~j7!teGQ<{-R5vApiH`;@6-e!~-ycD+bn#t$qfZ;)xQbvo3K&NJ- z2Wc+Kp~0#}g#$(2F$Bo%ruSxU5~{t+ZU`(XnoaIAjxtkaUMOqii0g!GNg77NMv}Oz zv7n(XUXp^utK(NYe0le|{!winxUq30=>7B2SPp`EjuP!jlq9-Tj| zicM5FKpJIbB@_owix{cnbWa|qJqS}z7ONKVxTQ>;t8XCV@w`T=u(uB9Zx<1dUdGOc z+F3uG#Hi;S&o@px9SY0y0AoO$zalB$vN~icI{hT?tisad+S_}3^bYGVS-!sdf>R#0 zoLBFp2c^Y{u6!*?2`sAiO_X;c6Z)>~$~YwWs>@VrHOq zU3qQ;-9VGus$S~{cR-Whs`-Z2wjPW*7yc$k2+-t$Qotl4$N+H^Fi0ZZ8R3=yBrHfQ zHbMGi8Njb~Kme71QQUxAK&(bo3_b9`+t{tB#@UwtY*x`}eM6Yw@kLTL5VTOu5LhVy zOHrtc*na3oqHRd!fRmQlPgUdCDq<&1^o!v5yVyUt*ht2BcK-hL@w*NQ(DhULh4P%wX8BC}01i9sk0GPEdl!vMttAm0e=Fiit^LV`G? zI&#lTfvfB4R_NL>O4oH40-8Wf#oEF#5R82Cg=0J~oWFsEqi@y^B_o zz^jK^&V(w^Vk7W~WR6l-kg(+h33zN!frB4YxRNc`Fa_vsk`&m~0sPB}^Aju_qYBye zyl{T*3un787M`0sAVu`)pJ*~)I$|q7tQXX*Wb~~U)Od{rH8(?KZ_n4D$d%(W=YJ*3 znpwnF(ZFiD*7tP$Zw(9V1pcUO71Bz%`10U;k<$+q`<6(KUUHr_LxEA)U#3YT`5fjv zu2Q+**-PX{ACs+57OP0|oNyrfBA@*9H5hNhD{eGLOd>#PG=j#sJ^;qRJOSeY@&)trjK@?z0fz_YpO-t4zXEq+pEAtJ+fklz@=w5Q zL-zjH@&Y926GHVsq95R|bLrBWQC|Fec406a!LoLO+nJKLi!10=?s?_%SsD{9Tz>kmGhmq_d@oc48_@}Hr0wRp`hsdhGHQzIL*lZf;6 z9Ymb3Z=cG5v;`3n$bpD$j6tfHBGWGtpix97P73B(9sl3;fO8D~*&t3F z0|*Hf5!W3cwt7!1;%*fS;m;-Fe@oJ&0lGRVp`Klk7}XMCD5;_DDoKVA`VaB1Iar zp^QqR&~q5e@>*%8sGu$BG6RBgPQ$wil}4}3jtb-Q zMf?V;%lKTAib9Q!fklwN*Na3^MgB>0s5Y2i`8~}B*ovGcoLjV=wtID^ z_U`Vcm%Maam}2#}cC^SE8rf91THOwEE378*e0@+BG>q6_@Oiw~NF>v@w(`iRO<;38 z??O;ggGky0^wdM^ISTWKHKlE??^2ifF4dU2*?doXqb@OtZ3d8cz7aW59N0*pbU-|V zFHnXASygC*HUJoKCNsSnUur7?!1a1~w>G^52>R@pKNqI ztI>MI$!I;|Wb6QoiLzUe+UjuvkXRe6%WfQEF%qVG>QERGM`L7ANpj*iNdT8# zGQC*XX)N5I=48~LSL5@^*!g7KycQpEGFp!~89M-P`hcj2(-@Hl&kO<05`YieuwCFb zI|1tB6V!}>9~-G=z-O^RjHrXN%Rp9*4Iaf5n(a?>GCr@x=aaGf$!IpBL}Qh}ncMkf zq&8}! z_>KoS>c|Vs#1P;mLQ<)Ll+zFkG!SWEx_;^)24qBlfEJaL(FDTv^ICjfi$DFzxE<>H z4mD;7EluwGs;+~E6-i@)c=?tn*BdXZM6?X9if+uuD}JtxXi=NmA#nm8gd@~vhA8c- zhPLuw2**d>AbYVIq}tl;zEPX2*U$+tJdLKAf?3X=Pa%+U9~fem$c$!;%rh+29Oh`y zp#&-(QcW%ezGUoVGz3IIRuDXjAy@rvco4^vh+hc=LIlYT_DqbC#3ZKr-v!bD8FiW3 z0OIj@PH4Xf9bJWsD;dTC``%VsWq3@Ht_Pv}_XVDNGuZ@Qwgg(Cys-^Niu3Hl9NI3Uc& zlj^R${Dr7t?}>sC^u~Y~%Xgcc^;RbOE_n*;Huk3X0O?8XeqMg=3aRj){_wwLOR%+S zpCn@TkWdD0m-WD-dxCJg8_EiF+8f85EnU39l46lISl&F#o!ph2%@XXM4iu{Umv!EZ zYC(|Kln+iv2$!9skneqkc6W?>=+Q2P|lRZ2p(VC;PY^TyF}#i%6Zqapj#AN~ow&LlXUL^zOb za~-+_@|I5+X6hk$fqv29{|V-4yos;#rU&xZ?nPSOPoE1&m4F$~X! z=s|W%iQc1vzS`=0+DGkA|J}dQK!c-wluX5pGC*^ejwE5=4~tuz>BZYg3<)8hPrqYO z810>_r*Ej79xRiBc_50n_&0yh_U}lZ7Gv=v*eWRlKD%BpfPV1~C(G%}dyo2jsZJ1M z1LObwNEtBG332+}Gv*(CE7`26!Hh|q<;f2k&U|e9+FNKQih2OW>eQ7CZ~-(dvfJEF z!OFbgj1SosM#0kmr5gL`p{wpt;U&2c( zf7_fal(dogY$23fN)&uhd@}GvP1_~Nc}e1kF6@O1a4K!6Vsb5?Jj-RPPL88D9lEcBw=?aM?{Bd}@xXoW?SJ6LC z(m7MF%bJWxq!Tq~vH>i72Bn~t#2XKew13&xUOki-bJ1i$Nj(Oz=T(W7I;Tw~X#QI) zXel6^-2|0L1kedVf|TEWx!C>}?dUEXEw~heh(x7gurk*9jd3VRDe__aj%YeC zrAH z3L%nCzHOm0kh1BJ>S`#RYnn?*zD*Fv3T@t{?f;c(ve58{8NmRp}_BFsbX*b0~C1VdjQE=ijSp(sXjxlnOJGnVBEX)|?7+3O`qS2A$Dj_qPlDweE z2I5nUE0L_K9_*_+rNYwd`+QYF#uaJ&HRds%vO_7g$`mDUUje=vbO zq)1khwM3a>t|iF<^~OAZEZ9+G`q&X?2NI;{*IJ&2R?ye-%j0!f_|NxGF#EhE|{%H(Z_NznH-Xt8vCq}fuu(m3+Ph5F)rr1!hh^SkRuG2dYtLtqN4_Ik&oOJT1srfy8?pXsM=*FnBm|tE(Zk|KJ9=eX z1tkaU11pDH9!?&?z@fI5&7`^NqRtQ{N9f&NL}v9vZXwB?t5rT?|x?9K@TrZ<|bj%T4^Z%{R3V)cB~9_KnnV5hM-m7u11A#1N zNY_Q6qdE}u$q^hD0b+njisVw=4ICpyH6UW{h)4)9Eqfr!gg9b?4bMRtmHUOojNyzc z;1oFXkS!HS+fM}H}@@2EDPQU>fF>D3WMY&>Hew|#WEUVCYc|e^fu=$mM z1GeT2TpC}{I{%K;a54l{cXFyrsbhXk;g`{_Hd9oFrP>&n%I&eI@I_9&H+py}?;6|^)#m=$w;C-c`Q5u3>cI8|HgawnwA0FuZIx3s{(JJ} zF4fpys-R{o4Rv!99V=(HG*(+dQ!R6Gw+2@%bGuq=Zm4nH%)s3!(p!kQH{I7h`2hqz zv14H?b&x^?n(Ndv0P>UA$osS1&^01AMUtS6QzH@}urh7LRj61ybP}8sZUr3(Am&E3 zTHA0ooe282P>NqAv@+A!bv=<<0vU^h`DG?L>Td!Q#6w8nNH^p@l=k?`v-iNa{JuRE zchixu3P?aRlZW_oaFueb|M(P%7pS5gyv1Q~@ZqApra?)kO$<|J@Hhc8gU1P&8MMc3 z({*h|-sp9j{4tyhqB8_ek0C+FM1$ivfu-0O4oD(y^jA z@UORZrjp z1H269Z#Y}1c_n>I>YNQfu9v_&n@ix`My@Z`sE)bKRZUa77{V)AAbC+iIFl7za|lDJ zV)aqms+!sG7JX;E)SaIB;=jSEck}Ay;~p+a!W0%e6MoI_MR=3t!1xv289vVa(u_d5 zi4J=O#EV;`&=NSm1iSMkYJ33-mN3!uK$U`X&~%@p2FeqQXIxCwt zGSJ^K`^5eYM>ho*C<}{51&-`cCN_a?4c_Zy!topV&1Bk}u~H4Dx3QR&Tb7-gsfH-5 zt2)%Dh{-j%#F_1aJ@NeJXUC@$Hbxf_%0>isj93R~rDYH$M>qQ#yby~0ZL(q1`J453 zl%@BJnUWhk8rdEgf53~ z3;@W912ANxf}927JKuZSsa-j?|6EhM-8FVI5<<OUgV z`6bSa9|&#)?ihOUy_Jz`!@k*YbK}YJTXlo>E$*nIRr1*9O@0GC3{iFZp&d8E599eY zI?@E7V)XsKxh-usXmc$2jj=3ali^>LtV?5)xnFa2dm|rI%M^41fV2rhbX&v`P}yU| zsce{Q35wkiM(~9P`zr+KI~e6m@-G3B+KyrF$E!TiW&rX#y%WTAo7&RVxPpnKUz5BL zZj8b`@Q0?U6`-_7$8l@Q;Nd{B5&|oS)&j|jQMU|>KLslQ=FTSW3_{)UYyk{BBdE$0 z2wv@{l_??we3q$_OnHsLP{ZvcQ*I}ja@#wU3`*v(fy!if5lHJJ5C?~V+66v7@b3Xs zVHxm93OyjVcLaZ>fly;)k*XCLaU4}-3NVYd@w75!^$hoKSEjZrcMqT#yjnb{$AVW7 zj)tXzSNH3>`^hRt6xNkIn~N=*l)Qmzv=Zs$Zz(lS=5k9`NLId-2S=vtW$X0w%SL5d z>WKUk_kz6?xld^^mrerqD=kyW)m>xGS!{kLd30tElAVvp1fdqnTXPyZYV4LDNa>E0 z152vqmGoQu0tE^={~nV4@S`LhOc{^87d`Z*oSvVzNI;42C7+>M5qc9s1 zwBUp@gv%Mvb9yqnkTm)TO)MeILvn_}8zV6jRfStd&ysH7L!z$H@ImFR4Hl z`#bwL%^m@HjV98K9ftcozeLy^;!?DUe zm-E?M&e}b(CoVmFXhKG$!2nA0q$C5Jf(r(W9F94ARRU@$E>WF8>DuVpz;@a!b1Pg8=L`_0AM4Ch9oeH@DVH!@i=}Q5;}H`%8^&{Hx1YqQ8)ER<=)lKr`c5i+Gkh* z)XeZh7|3P{`!^w4Kd703g)CtJI03>b3_!z_Mm`{!JOH2>!0m>@7yAbC^a9JO3V>lq zc!#GHfC}Zq&qn|nZP=~?V00A#qeBLSMiWrVZ1hoN; zhcw3k)OBVI^h)dq!u0@+XI3!fk(Y^Fbn|H?z^dlh{6$K@cI)8x4_CwN<`ShQ>e0Vj z+6L>t)nT(6{$}^D(`*_1u3h>gMPFAHs;W(}FRQ-<6&bNc1JyHTMWoVHp{bFAlG_UF z?XRGi|DF0#YCT`)shd(ESLH(LF$Mm5!T(%Q1W{{=I({^nYhJHqKl5UqqKpWD@Pu2@ zZ_vn?^IT(1L#1Cr&nAMYISO8PR34->iN2PRehRXuC@$lut26?D3NtC!aaAg@{!T@i zSsnU1=P%Cbkrk^k*opg{nTx-KE|-nNscG*Og4#lhTfL=Ge>(uY2h_5v{uaxx`&b^| zT&=mls~ViK6&bvh2G^WCze$5zU&tHvx4O&QAKnoHazJ83<%JOo@GaTCmnM3QU>tIe z05Kdnh6{`d9Z=ukyfI<`LfOKJT_2JNw#7;Yl8cP_Zb3P_g=2d)do4~y8izWv`GISJ ztU6&Nae&hH7^O;-S68!j*ALT|sg`I4&g?ejX16JCudl(m97aHEcP$GUXBHyr#6jdH z0L#?D{Q?Bq0P|}Snr0MPQDk_Kh5+!j@X$GNm7y6^?sN>X+bz(*?4Qw;KTb=lnM0%7 zl(*N=bkK~7z;a?g3C%FHJ$Ql*V2DHiA`tONcU;rJq@!2{sBjj>7I<*bi;@5n4^Wxa zvKSWl3{85IC-f&f!74p0h*cNkE9L);X&jc+?~8{Qvx~AVmSOlat*T&P$Eu{ z*YtkE3Qzs(aqHxSB<`3UYM8IX6>$1Zi#FhR1Uy&{2|zA9%)(F>wB!x0D4o#0j!5kz zg*Fx|BZ=PP*ManUl8%|fZ;ThW5$=GWd8LbSIGfQO$8&|VQS^{q;&XD0=C2{!em`4Y z1MlrDq$|Hl7~yQg3v^GGC}Y^wDuXo_R0wR!U}7vldn-#|8}?bvEp3p`)^2Om9xou> zNIE^CJIb^(Rc=q~^VLJWF=cB5obTopc)0=8ciWxqYb-rK_QDvO5b7692Z|$#9k5LU z3ma z1yG0`F9xV-NPY)DGEK1BndGxv11GDQEVH9pw|&UXM^9FV@+TjJJ;*Eo*#PQWlf;d| zIZh`(b|;je7``46gGh*z7C>{zw1ZO?*=_<#U2H{Zg&Z9&T)29kda^3|edo#A<~E`F zRR*)k$M7y(jOMxHr2f~6>qPGvW|>s8;yN+mU~CMk%G3CxY_-D7t?|PoNY7}L$2Qez zK*!V7FeMDlS(5eFbL;B6-Z$&=>q@;SVSh@DOxgqK99rwbZt3~dot@F-0q8#usz=Kc z5KfPHv`7S>gbHnkuudQpIB-ZL6+#7khXJBEow$4iU_LkEegHJpFsptA!`;wQ{R8 zw9 zQDNg9?K5}+Y{_8ChIa)cBpB!DBA0P0i*T|wiIzKxHGWQarpklDKp+@aO>dR8o=*;muOO4-I zYW(&(Sr7-Fi&zF~A`DRY#=v5YVd*=Tp4w2#LeKL7r;gJfiC(Fbc!o$q+#12Drz0>| zl^VnGTpR7h4ojJla2r&{Q%||0;y04UYW-uADLGMj5t#`E6~{X|OrA z*8p+Mjjoq<@Yf9Pr!t--fTLz<)B#PZCo}0_7`(nX+tv7=!O1I0OZ-Vhhx;Fq*~Z#n zJ)ZsYeX!NmXsxmj%<9ZEzC8u8$UV4a9Fo~eaFBZwErz3c4_f7hZpk#LU3sm%ZT&^* z`rnE0mC9_xOY*xLSTry zTFkI3FS*6zTZWjuhJR=b1xnV+j`%S{{z+o7(4_ukP!t+3=&8Lq zm5AmYllNde@ci4!Toj|g9iBpc#mxZXMD|xSfok33SZ2rw!75mboA4{6@&i_nK`l93 z3`vr}4h@%uq?CR~k6U}1?8M2dy@TV|1GvfN++j&#z0y%NQ|mV$U?6#JxHOhW11>uR zPzlhV$0<|e!gSMl3sl)FW@LLH{}Jz7WDCkx4*?-e2(J0%vR!Hc#lDGxuUuje^F1fn za);B>YRH7#BT92dEvP2x3kA88<6X+@Rpl#Kw7FICXbK5{I~$i?N2MFUXG*?4_^Aem zOXE!YFnG21{^HH9Mv014s7zU%85Lg3ko2-4eu}9t7YZZqWF@N%NY>NoB94cE_daEr z7)$^ujjbJjzL_oVqREfD+W92==l@$fL*#o`yO`WOOte#qVatW~mVk&k`i?zE&YJ`k z;)mt?0{^go^8Ve?p%#t8I7b2Er9}ysYz5Ca?hpF!TQVQ{$5%*UB@-cALx){i84Rz= zSfO-3|D=#o<@~A~(3Q!|&@Yui3RksUsC26BvMIXD$LP!DEj^Xe%2P^CyLH=WrZ=fr3yHM7E4W1XuU-?35 z3Tb(57mC+*p|p2Oq_L-aj*~i}?OKK~($poFR)F9lsu`^aM+=h8Jpe<1UewV65DXYK zI}{;unG!EHD+>h|8lL^+3+4Gl`DY59MI)S!gMhi{(irl^{*wJBT2;_JW-NIDj)i_H1Zbq-(U4% z*vL>trD0-(shB!Qly?`H zc55j&?bcFmwueZIke&rGcaicOI)J$YGz=vH;Ms{7e=XT zK<-mqX$8@ml>4Utg_*jarTi~c%A2gapb=ARU341(wdyWypX1^HL0ES^zz7?0kOJ}% zZ1k|@kc|i?TV#+D@{8~jWQKxO*3lyX(YPV`4^e93ht~RDZag_3z40vNe_>L-*)oen z|MuZ(lJxJU<7*(|f*JNQ1$KtC(3*Z-Cki$;qdBuSPV1Em zbUN#w$jI*?f7q8)|HBktGeQM7=U00&(>N zhKHbjB8In-g&2T4rsq&EF;FGkNNV{wp|ExOz~#9mN=P#EA_9M+4=)yD!VbbfYw8Ft~6&m^!bEB zma|`>eS%Zs7DfvSs}hJbi5|nn6b-?EC!_q9cw$#brHd%?>;3OCWiY;7L|#Y^otH*=R8~?ZuaL zPlLL#@tzJgB$Cv82gMHwShhfR=y_n=f;$;`ZUFQ2eWbUURQ*g;3_60Ep$WuAK$Msk zs-u(DAdIUAC{0|`uKmaB9mVld(mMdG;{(6XG7=qJW;XO9AomF!kk<$&q1w8w-f{4D zA8BosFS~=~73#}lD8-s4@rksU9>%?ATAlpUYOD_4@1GC2m;5d@+RSG(j)cNTHEnNb zdhq?=;^^dZ|J~8~#pS2t)5G_lzEq$H%iM3Yn6doKM=W#m5zD*-2_OoaklG3)As?$J zf;|mXOw_gmWficyurKt~M^Q6SsRW>D!o-YRtn}DJ(kqZC%~ebTRJ*|}aXKw?XWKGg zQ8J&G`SUXW$(Q+-6yz8sNeIh~79cl5$ziB+kgH!;%qP17LtV#wqt^oroga`rk!QhC z85ihQ#%s2ex=zx|tNbvI9F24k#)zV9bJaM@;I`7YF)`bplC`p>?oBS##!Xt=z+ z+uO%s=c9f^#DSTD=gbn&$`dz$-_H|{?GrMQXM3P(bOh1nA!RC!Jc29&cu`P+*9VET z?XT6md7le~UO^!Ojj(|C5}52rSwRw41cxR12uu}#RvV)2iT#rt&gVzpeK^9!$8S#2 z_T{Vn3pDZl<@>XXD`1{?q~`6`^)5lz2X^P&-we zglD1tGYPc`x(cVMYEakN0BI@I^77U~EiZ2`)Nq@Gz=n!UWOb(o2-TA?p*`CMdwOI; zNRY5+`3TbkHVh5m?7DUa5`hb)jrUPA5^*>DYp?Y zJ(Ie^#hSkMkr1%PpGg|M;yjqorRx3zRSE&V0+8}ZvO}0a#xe>b077y?zo;|s-krb* zm@L31z*GRFesh=5mh4-o z@c;^%h*^ef?|G6`Lo@>qTv>*Stm~m*JTsdT*!mY)Kga0i`dQU0{6G=g(xr($Yc29&#h| zs?1qA4#&lco-!2%Aj)RNWNSGJ9MINi7Qy8Z6^W;0YbGm$N3GC%Qm*Hy;K8LE z)yQKS5Ap>vT~N*{H5LuW@)dJDw$-sy??(N+rTwC2VeBe^JVkxWlfY5U*`26g#ydN5 z9TU|2J|lerYGcCU{^e-?t55^6Z2D_S@KIwSpXb}(x7hyn_#yJzUtZ0B zHDQkhI)O;@B%=W11GTvq(vQ?*WI}@Om7|8z8Nrd#0@s+ruXDwL zQ@pm==9!pxwvg-gF${qF#~QgXb~y={H;)D+c7;*wI1punCE-k%TZGn7Wl z3Y3HQ=ZBKsbTvz9RIWRzQ;`0G%q2IoDmGjTfh--Ax^_hgsrBGHi@@JuZN^|LM z@q5wFqe2ovqJ#yr{$m9}nB$Di2*MsiGSx%Eplv3er#4rOCK+x?EB_=h-LY>KmE7=zJdQ&22pV3fBJu^qU8om_v+Dro|uK>Oij+te5GGpUBe1# z4t2p#Q`Jm!B&sd0u2(Y2lsyBOz&r$U@e!(2feyk^k7bXw(#%0!VSP(wU#Sbu0E|K^ z-goE7Uqccf1zC2A*QvaW_5%GC3?&TJ3?U@0270-h=lDrD41|n8H>c>-=Do#~p^OnL zLK@zPFd!s?a}}a^AWckGhUF+_6%LG2R-8(kg7B0piAr5XgGw6mA%|jdMW&Wa>_c{- z9J0N|lt&J_oJbg@F=1OOzl|YQhHM@Wp>)lMB3mLRFHu-1fA1d`0)WstGzX{%C9pBu z(A*d$2{M!p3`hju@?cq}M2Uy+Hplr;s4NgwDC*eU=&6dBM5y5~&Y5;Lv6-lBKAxj!^O9fTR-nqc*D!ZR-p5;R`X7=EXf$(QkQzb6M zcp<-1ur$<1S;DuBH9`)k`i#0YEjqAKnb*k|yo+ zeorlrODX1;Z>W`}8Bks#<$x?dY-temL8&s^DS=b7r(??1QtStj_DNHpQmPeOc1nKy zA)AK3;XjWLe+_`)9qP-nXZ%a42cYninB$6r5&ig$b|!m3(~mK39FsfR(pDdz?U|+r zl(L@M_PP0|ggq6Rqy#K=s?Lrvw?dTWo7ApqUUA=y8Uw5O4Nc{CZRUKd0Yvxn zKWZq5Xfhw2dP``1X1OS^ryk#vkMAAudTI{N-rwIZTj1X?w|fzM8q@3PGEPe{%c!FT zkns=R-M3f2jfOvd_xc~){kx;r_vWkX`M&+_8}G=lfBO&rYA?TbEv?esnpNqWn*+NS z3fE_ASXVu)R(tg~v5eH5$Cv&lo{{<+T5GQeeT627COoiQ1Kq+-V&q?;OvwiDvlRsa z0=~cycaZml@Ro1JjzOr;0w9*C^tVYW%ZXTodEr(gV4GqE=Egj%r2qpy_}i1C!{hzZS6ec# z>Z>T)Q<<23rx_E%?M3JV8hk2cQoidTc*VL8ue!tCeY{ zV_Tp^cuK)qr?UKu7OakPv)Lq0hy5FrERFkjVS$*P6ip z{C~;zR?Xi}IF%Vwp6U5!+TdQGaHwkgRytJ+$S>PRv??S+Mm0;2rDzfE2^j;Km!MB` zAddS|7buvp!#{FZg-y7@$=hRz;c!Uboj~26KnH}mLXoC@LJ~wgNgJ*510EdoUVRT$ zm{ib+G|<9uWTlk9A`|kRLa?N$;+wNU<(pIFBa)VNtG$46K|}YF3^Hk;5Cb~7uan{x z5QP|1F&VBo2es2o!kaStjiGB4z|612SaUtuqw9dw#C5Fy$rVX8k4mQ+l=zReqsAON7Ds?o`J%CL1|#V z3EDj{n}Hff5F#Qf790oHcur`96nD_QH~~K!;4C5w(uD;Qo zTkMCW^Ci)PBnjZ^g?Bl&O|Y>D#6W;smzs%#oVMokWhEhgcUv`dod(iH!DvnjY(!mO zn@F$ijJMBh1rBD2D3S?M;edihK)wvz$WP562x2<2DSg#6p?6pW!j5bx2kZ`R5(7HS zOYQi}&$v-7b^m$HIDw)z;_N$ZXWVH!-0)4GpIS?!0I&Pw83qhaow-R5&t4*e0zK({Gopz z_Q?f{)Q>*B&ukeVsrhle*xxGW3a%IXTf8*ZmBUZ_@UbTAnECipBQ&y%aLv@Cv!ZfB zF$xwJD7fa_xt?GDiz+U_hcPD|O>(Udkkq1&Y#Z_zUm=AD!GWSpf>iKHuJM%je11r^%9+%OOL2s&M4yC zAWHnpn^e;zP^46vjw1bSluf8G2&fIz#(YV2Ddiz2r!1mFxd^Ff3#KzvwNL!8Jme*B zj*z?ei;c6BTNicH@n8PkAOH8IL=SQb_&@#OkJ|B=Yp-%V^02hpXe4A%goIimvWr3_ zA>ZZqDvijW{_Xz>v!$26NYS8r6sP>OdA1t8D@F<&queiVvHaX$ti0uaj|410+8^W_ z1wG|(n1K-OK}wvwwb)3A>{OG zz)xdDK#KiI%#G59r9dfqloF+>)KsN$ueAQh|Mfrr$A3+`A;npb5XD)2o%dsb7}I3h zLynxZn9D%@Ys-Kd+T~_;xlQ~oe#8qgD#t`dRPV$u9>H8HAJ9ZwTKTnYW>-`Yk z-y_6HIfxmyj!X3_m+Hi6gEqLWm1Ye3-&UpijNJW_$6~?x*=CTj&QwBFpQ3Z)PZsxG;FZ-bOa<2 z{L({ks|Nw{1_KlR**Hxsc#ny}3jC}gz6Wx2;`qJ~z-xGJfg)WMB-jD#`GlV*N zzivU=Zct>mK*U1Ee*b<*fUQ|}C?ezp;Pe?VRPNHc*mmVQ0<^gS0oo?u7h+1QGc^uh z4XSpAd`6y90o~@4>V;NxBR{M)mEex7YezI5+&)!o<=9!{_ zlB!2mfF=*6$&pOhP$<=gka9dM)V(}yC|6d_4wRz#?cq_5NJjS4XgFokET*z5_x435 zRx((=s!;+4EKt3py#r`so;1Gs ziVF09{ndB^&MJ&9G*Qx4#q2AZ0d@B>&EzHPa(g`ck5Z}_jqMKVJyG(wZ&k)NjbJN1Y~vI(sbKOZ?Wn4V8ZDW_4^6j_Z+@6UNAkB7S$3S)V5~_%{k^o%sjxM}3^<5XtrZzBo;7=1exFNg< zAy^lb4<7vIMpR{@1YUq?S6Rx*5!!rKZe%H6?$2ePnXo)NL?#QkRs#pXLc#}BFOWg? z)P)x}d3@ZprdSj;p(p~~fA{Y4Z2#a}gissnM+XOQj}AY)qo6B}VBE5Qe!{et<F&M3i%+3BWH~VT<`Eo_@6{J<%|K}Rw>xCVL{xA5z$rTG1*)8Q(;Mvf?4}%+ zK`imYWIe`FvQWh)TxBqlqc49uEWKT=?%mMTBVccf>Fgz9Pri8%;?Ucg=+dQ?My>k{ zROf^;O|D_g&w$XCXV)+Xo2WRg7F3*;sXrm|Rx@QMKc{x{uURISxVmAtZ@9Yk_HIJ| zaFCsCfV9l_ZO{!OB^!<^fo&6@_TZlg?bNf~0BH!8fy!8DF-S>q5GqaypKCRV7Ene^ zEYN{y&EDpqa@H6L!SbG_IC!Ff0@bFKgf^v2q~sm*4ZkxAf6W*^);qNkvdU}1nP0GtY3pA?6`8HbKT z#IIjo8mua4>d#ghnqFldm6yqdh?BeUM=M!smZh<=zqLn+v%INkM=AArY#=cTjp zf#q0tr55mJ(nC@o`FV@Wn~+*zwOf66D@THq@iwkm*<1sFdfrUK&R<RRB^~m$X1e>ewB>+iD;b)EU8+=$c(e~P*?%4>S z`>RQ-7V?>0U4LpU6x!Cx$AE)s7ZZeMI!t~mp#EDw{gZ_+XYcmE ze+8p|3-n)6DD&}n@IitFo};CA0DwS$zefkFbyqce>l4OKyKqHsm0+W@1RI?txI^?k ziHy(>TonA-0RFcFkcYXk9S~Fse6Tt=#Uf zOYoQ6g}0|%O$d00%{HoRK=+0-U=vR6m#J1wS5z!vS-a*nS;7W`Jvj9NgyHOBa*AXb zaIW7CW&ci5DsiQpPUeY?Z9Nt z2t=x{A?3Zp8{vtwNvN!CzI|sPdxiegq}$xWB!!u`R}BBojx3wGV_FqtIA!vKbP0SI zP@esaN+p*3f|*bW?f7<1VoC@?bgQh<9V9Y$)>UVT} zUnYR@%O~Wn*rD;1kisF?Ru91XA?7C`igC!g2SFtvw*VdHM&UV+kur~7|5unXRJzhg)_T5r_u=RQ2|I6>e_evC_4srf#dRx8kZoMj=9eI-HlLb?-D!;UIGcEC zHp{2}Si$bXT_ZxvHvpLABedtaPUOL~OaU#Urv_@(;S@w_kQE5e@sJ2a;ttLw(>94V z6_xIO6?aw#r=k9|f^AfpQlAC;rxR?uZ)`|7HVoZ*jBN9bkCAP@u|0*2mjFZoaci)^ zpbB3A-w9+j$i=jD7t~{}3$$;~0L2)-3{b@5M3Di1cmxa%7c#Yq@#Ok2iH)a}?W!U3 zEZbjHwzr$8kTaCjQU=)4C|!QHhB!s6TE>Qetko2dr6y&Is|M%0VKgI#j}a-T#0SIZ zz-+|`Ch1W6-+D%yYA~muY1hn3ZBC(X!=$+pz={n?rAIOw7sFF%bCO8=L4*>>mzXF* z3C~*)(#Qw&VYMj!4|M1T3^6&)3OxbVn8F>VRustv5Nu&Z2re#0F}(a6IxATy4E{R? zqA(~on4Uxik^g`8-lR8@Bu&%2^H*4+Fd6PyTy$SNt0#j_r+cu{w{+4oI%+WR)|Iqy z+rk#7GY*41%xMnYgRa5=)zgi_^kL9IUj}okQy=E1#2@JT3G;j+s%C0xre>yedPHUf z5erE(RZ&q<5tVOwzxORXR#LE$vNsha^B}7ylMC_##gp|^ZsMt`KbGFbLj212qghWW zwcxM-l$%y1?ho4+%RK3luKkLVYP@bun>^k>*FKd-V-0QVD7qO%y~BQXq?<^aWi`$1Rm=dP40F39Qnu9u)G{`e zmo8oj@6@3iHnRGmyKh|)c`NFUfz`l1XuIfJ3k1iLoB(snO13n~xLP-qe4N#)cGIy4? z)*=B${rdT(uAi{E_RRDT z&CR-_#(JObefD;AUNl;x5*FB~7T46tj!VsvD{e`D*jK_R=aAYAAXpEf;#Ai=51s&MGi&(i_fFNka15On|Zacf?2_ zEbi8IYD8|bWZzPQ^uwS2LAV*_1Er)Xk)a&zba_RJCUa2hL~3BL{2}xY71FYa$Pg9| zsR-R1>YluYeGN$O3vnf-BLcnc(E~jsDeTIMGE1D%N@LxEt!t#u#(j;WOHJ~|jopZt zXMWf@kAzz;Ry|SWyny00U)QD; z0xROjmNH5TDy;^Bdlc!&;(`u$p8&;J?pcTB1lu~Xp7U^YR7QH&r#rltCnbU5zDPGs3t;|W zsFu0SNir6y84)Kw>@2;D#`2hn?i6g{<;++}gQ}%33xUNy9MndnSX}aTF-eOhsTLuF ze7ukuDmRqQ0~zD#@RFeuBE5&j3+RN01DU$I>2x+$SI`S%$mE;~Pao&Uok+H+fw0ub z0FNS76e?#S%2m8$p1gb_Pa+#g`>AT=yeF|Vk&x9ej6`3JN0rGMnjuA{-aWFfPGn&& z-)X+|KA61k@`!rF!Q}ztCxq%tM^Ch7aUaf_M2*p4p4ZlQ%4RE-#42`GJ~L@o{jCPz zTJuuQwb8hnq&TZst(DFZ(J5}~N&;^^6*ucVC?`sS?$ty;%nw8))j1DUDrZVN7Hnu_K~$d(lgZJ^SH~}(oj!l{tbv$bT3MmoncF~H`RI9MTMImt>9kAC z&}f9l7fm!1J*>Cd-f63C1INRr^LEeiBF1T&c5ErH&O#-}g|Wm7;7x$PfdaZDMr71B zh=-&}3@B1XL;zumMoN&?iK@-OU&(_>vg{LLiC(?pGXpD%Q%RDHuEDLBgbq1q(LPOJ z5Au^W&fHquBn%CFdiLxk_{(>Xk1sBSu;JC~i?bJJe^(r|08ehRuRjx$-OiX`nSD;< z>~n58`hnh<=dzxf!RPlXw;~JYHl5W(x6B5JfhX z$JA=rNm?TpLAcp5EbFdkUrpKe7PD_R%fwG%fAOU4i1>TP^UzcJO z_w)5O^L2YjDIVO6hxD=68%3k4-aze(6#`IEU9dKXkj(WVB>aG-RQ# zkj^v7d7+NAp2@zZleHL{$76sLN_GKn zq|{p#&k3R48D1arE2$iQqHNtU-#(2#h#E5fK0y#c1-raUQFNh{6Wk9OU*gK**;LntQM@DxW#`1D^s}`1XcT7ZPv!ti9X}7JtAKp2ew%FB!ci> zI8;s!HRq~~tU_1;_?V23ScuN}6Sz4q>cZrD+-lSc^7BA4WYfOhl1a3FEV zOoTuyz$QfKD@~-V5j}x4@{s_GLhz@d1F9hUF=lVgi@Be@Kj!RhM8gDaW^d4D_U^8B zYvG%~_z#fZK&Bgfm}}=?V314}BpFaMidjqumIEgb2>zfh8~Opb;{=LZ4Hl=dB8(uDqzu@j;Mj89R(BgHnUdQ z>%1WuC;+;bD~bqGi>QFZoHOa>QqPE8-IFY%aOG>uU(qX(m%?;fl|eWb(Wo3BU?L!6 zIq9G~)r?G9Yq!)P>qn{1OccTeJVtRQ$nMRKsuUic6mJX zNR>{I)?2$7BoqC#3W#=j`C@b=qyG`Gy695L1(bA^h&Jdp_Y_N*K6E^^8WiOpCzyWh56arE5#@#dS;jRx7>fN_$;-?^jJ}h6^$)Q zq~*$cWuMfO(DZ4AbJ=-we%>kWKrPye1oY$K05>>a@8B+cR~ac%zJu=fg$^`%v#dxM z7BjjKw^LW(+=>_68Fdq#h?Nyp+`j6Nc9AclO9j@iP9I6;ll$Y_^HG+uDCq&Mi>ft8 z_Eg4nuGUe`wOcsFkCD$3FKj`#_D1)s!8P;V=pCmu>Ah|_ZTGZBf+>|p@L@`UWD*C7 z1;`MFN}!O~_m-K25M}~8_Cv~oky=9h*+$y}k`b3Qo3#v)3|yb=M?0I!-+_2)*`~s% z3w$b>(J+JI*GWmr5FUnlD0NKN%hOM=sn!IKpB!HtzdQft<;h*JpR7S$uC?77kd~Vs z`)9&k{y9~_eGZ?;I&>Q|JeFe5P&x#?Bg%yq{QzR)$a@}#_ zAmrhBmSKdwmpD4BA$h&ulY<5(7$GBg%_sjM(g({??yK%b> zZ?$a0TdmsNgKrrG1TfKuU{g_pvPpJQ7EQ&rO|Z?dDS`8#uSReORVgDNaBOmn7qCIY zIp!t#a`4S|9E!->v2Sb)UzkMRi~}q9ENk}jiK@fTJN&jg)rRV5v`BE)zF5V`J5^nw zRhQmZgbnKxoX67MvPXY{rCI7)^a~8`a*lH@Ne$LGN()-8=td4&%d1tp0jX761;(qs zlm*iZBybfHFOEuFCiUiBnrL6Fw50Jz%IgS{adIdWY~nI!m232G1Y9^#fYimk;S_*H z$B9c-QZ!RC$oQv;jL3eW3a+u_Dr>2(%K$8+=ns~V%<5o`gqa$(Q? zv2FRv^1vpKY~54ZURJH=(S#twI8r%8kt{jhCVaAM3ynP3PxM(-4ajMA^y5;SPOudM z$qRpL8q&POG~{8kted$h4_RBa@61hk$kl^)&qIRQ@liHNJrh(9(zk%ws1pr^+sTQ1 z*e6pDI1eZSIL&>$V9190M>Dfkjx0Tg?o$&@qF6mR^K?XwgcMdzO z8~H@-MVG_3vs-K?yA`r62e~&?vDII>e?g^eR&dv(IkE(rl9^$P6%e*q0J|Gg78Emt zEOL)bP~c)(Q7-BD8F?T$prA5?t5q~$Ttql?+wuZostFU}enR^^HtVI)EFu4nnFX8! z%e|lff7$cD$-@X*%>SUp{NFtn>IHDaXCC}!ejEgOY*8%i8eU{1B5dd-2CM-QtlCH) zIAX)Y6z_tZ&+P!>KR;Y%+ctghmYSC7F6VzuxAT7f|2fb99mnOU5^fowca7ER}9Z zTMj8wEXyg^x?Rh6bLiljul2hmpIH!2iKY!5FPlZUWlUC6cP^jj*{EvlPQ^=#aaW4Y;B~0Hl)6}7 z0Hi{n+*P%l6)LEro_A4=TNLras7yuP#t z?OeRNIDX!F{pReC$0y%(9=v+`^f^3koyXsFo}8Y8KX|xTbaL)U!6W zMW6d3VIlSCZDv@*ks*+0G<4lEr0_nCA@Zbp6i;LX#MPG}n5!(w3jEHegG4$~8166Kk$ zU9F5TI2a zXS68C-d9zmBC0j7$W}LSDL9D!a6BSM-|u7^7>q}*>500JzyE(xK_&Lok!|7hqKMnP{{3riqjUcDwY=f? zPBn3xt^_T41aOPx1wwBNwrd^<4kwHDywZ!q(R(>W%2TsR6<;8!;bDohh=&ZaN-C@7 z_N9`3G`}@fFj_XR^XQQTZW)H*$V*!jboPNL>O8M z(Q>*s63`+HS{w#bdNk08n66@eNV1Y)P|OCon}7e`%Da&i!`s*Adncd-cpWV+rU!+R z?!)W&{X=O$BQz9kfPPk0WJEbhRNXjwZG6U!=r>R?zDsWvI@$1AU=i|a?9V?aqji)KrT0xzt_5xW!LZPS**q=vK=gb@>fJDy-Iz<0} z&#f=wj0bwhI8F~F67)!-ix57@W>CCTma3{1<5htg{fmj`<)bUqGSHgg+UMZXT6HBn%oN0_D!6!77t1>Wdb4DG#mYd2!#2(`!9( z^zF_K5;fT*f-4xc3JpT#?Jkd()QDaJ{jP$}DC)|7R;V^iWryXh!gF3cm}XtUMu5v} zd8L+S;o1P|x+8X_pDYZ&(D9E-OLm9Y=*n4*@z8U9WUIoAMeEJte^k_N1F(n@yNmFj zZfbq;g}sp^dD&`yr>y1;C622{?;iL8t!||;h!mleG~pJ8XV?o|H#Tz0FuWup7-Rtn zCR61lrU<6NWgk(e8)R{qE|YqpJ{5*a;QDT*(rT%1hajvM0{;ztc*B zPAb8u70R9&Y6qgkGR-95&?F3&5j>XRhk@n#_mgY~xvx8%W|f+*+BCCT?Uq)n-O_5YTgE23 z=#Vd`p@lpg)^$-ebmy0ZPFAjOjP91Oiy{prUUt8eYb=K^R7i1 zsaJIFcgtJMFss!Jvs%rt7P6lXgx;thMh*`w@FQDxw4!bAdrCeBvp+qr#YG!Sq ziE&DH+A*R2&H^j7YrCa^__6{F-_NigZMWPs$;?)h%xpEucHb?1%r7!T3cH${aO^mg ztis=98Yo{0{rYZ6x;MO^WIy(988j;JwAwAr zR=Z^zsU6Qr4U_H9Ol{PRLz1UCT0H=(1GKqg5!oTniq>nKYjIb^r6@xcHDe<%(wfwc z<$#$4z%(~w#Z!yEHRx`|MHN#r$mIA2t9UHWXmB4>zTTzl{MgX_B@1{9|Wf0pz zfMh2==30zPL2VB3uH1|Klu$=~wxD38{JZIIe|Rc6V2ND zCKK}FdeM682I^RdRR%7Vp(wev6th-AyRK8&MFLXSKvyy6QWShXKX3ijIpl?)JHQN( zFo6|ZvP~*SsGC!V>OA0@D;HJA6{vhNC(BX<0Ayr9a;O7eNPfC9CI~H;CC_~OT;)!_ zUVzS>9;z&b2TUQ zDn4L987N`i2AZNJrO6b}B>6w;ii%rLI`?X%bHY+8e+@`uUNEnR=W^Uli&2)U>l-E? zX*J39Wt|7}Coha^)o0gNPpD-0&jug5fIDvwEAP6+orwW|= zm^|FO9k72Jo`3cGU6WC2Vawl9|EOGzM>{2?)}ET z2;WltMKBk@C2&nM$ekc#(<2t*Q|ASXjb}hr)|;Z&`a*tF>pfT6?>PIhd`1$vR7O2SSn9 z&B=4(&JpZLMR`5JtD_8!d^)HNL-GTbLGU)pD0BAHSmM!3yPoN>L*B2w`?Yt6Yws33 zAsd#R*=pIDt(IMjz)u?cu`qfX$inBubP&jZA~7;u2ic4qQA5_5o0AOA-Ow{Bpo??g za}z%Z9drkmql>oBAx3b&?CzJ{-7dQpYtCur<}q41ct$G+&uHP`2{jf%AU0fCo(ZKV z>YaWXMr?YS3%6FpB0~uTr_QhwsCO|gO)qmfi+`-h2T5=L2a@H_*etoxOBzw&-^2XEurvs$e^tJT`u-NCag=;Q#a23CaBS^z<$lOz_^ zWO z-NCaFZ2-56uf|Si!U5@GX&Ph{Y?;p#!8oxSrXax)Yw%$K5|BBQ9CBAmU;vocmYpve z5ckXOe%bwm4qg+*5W00O96ULC_cbR+@192>U}7RQ>Kh=IEfRNv0z^dx1hX6FjP3)6(ZKat}GcyAW&$|w)*axO}zvk{8yp|5$hLvZxT6uP>mAAWt zm$R*9l-0#v=#i+iJXpR+oujqIH(@NcCLHAk@@OkJ?vQaDg|@F=FcUv~G)?niR)+5*`R zN4?pk3C*s)T!FO1QEz+-q`mW6wfc#;b`3@!#@`BK&VuTv0IUAxquw>kQ3bd@9*#zW z>(*3t!@+^R&gu}1(4-z~_Nj|ZNNnU$f>xhGAlK{yj?n9;N@YN9n8mpdD0yaY~C4V5~3K4^v8^u~QR3qe%?V2_SNzK$F~cwjAxFSeSB8_Z~L<*)1@6&_9J4 z_U8_;pRWMB{i4}DP?sfC&Xe(lXg@Y6n-3x4cg@6u0VHzaB5mvCVS+$d zN(h^Pc4Wb;6ACGlDEB!~QiNVag*4UBtf$yF>U0x6(E|5=+5DoH%})hupHF|Y{fgN= zJQkW3DwnAxlzE;FIRJ{ZLU!OIg+~%La~IKqGK(4CP~Z`Ot8fQeEd8RQk0N5Kn`h` zlf^{Qe8`Q^0gBLL1{Of9IbU&RC>RRwm(9I1^Ao_8=hNS8zhYWcrjo%7;ns^FS8%8_ zO?(j*CmfIxN3)|eL?Seg;ByvHBQ_<=r9u@&5QlPA(9Y?KGh@4!cfVrpotfJrjZ3iI zhwrhCTE*XXBB`ax1k}RA5D(x2q7GN^TWt*lPRp_5cRI9m!G0 z`U!PRyP~l=1nQ|IVLA2RteuztrlQ=fTf~*>xwbO08<>c5isOa}#lI7$1Gb zKpvfZb&PDl^H@ ztrZY%Fab8a*xrR3?0}8u?QRUXb`p~`GaKq3{#lvu_1&Uu_U z<{DdI%Xr<6bEgTHZc#ahMDHya;EvWoN4~fGtEq)x5O7mt3bGKQYD4hiNGp#<; znmiltmNU(}DUV;&f6G4^_AV9+F#)GiJ0|wC>TNt5OQGWXXz*=U*`^;TQHy6i`t`fclc73SRq@Hu zldP93ds^&r)r^#eQJOZtXFlM-AO7@@sj6Z8U8PqSrE7ED<>F;6StRXs^YT&4C`H|Y zYI|kKbk*f>pgg5&?;lVuIEp5dEPYt%zb->mE<{%u=mT{(qp|WKu-YeLNXtM@vaHip zO?IUIu``z9zQfKN`O%^6YpUHY_m$bcdIJMpoVauw>d{9Xu94E?dH;D9>aOzc1_1s`WLDo{66ZHtAWIE&7-ISN-=Es!o^@|z7r>faFn$qq~XGk$% zzx%!Jlq#y!w}6ZD!=L_h=jBlDU#5s0CZ(bInrq4f&pt*{C_2luSE^g5*ilYmgeP)r zckVP*@8)%nuW`+~V<|NiU+X)p{-Sl}RnA0LZmtw8UP*Vf)2Cgk`OK&Dv;HI1Zq(tB z{9pb0yV-F1+tO2D`S)j38s4Z?&vfo8uVH#ryo?UYmqFiOI%QS;ZP2nc4mW=XaDTw+YTP$2fI^v zg^xOoL&Xl!i?mZU;q6Ow<~knU9F_fDz13|e%)8p`v<`YV4GwG4RZHX7QGfK?&MD=r zYUVZhty_-X=E!ckjg5}%rW1E`WH&akgCkoMj-JcuofCEbqM9`BPLp95>+E&f&$4G9 zA6wn`(+~aW`)A)>zkS*N<~#p+I30Za_WJa1j>=EPaCI=H(#mk3U|SscW`FbI;t z>_g$KVM69=_z{^i;Z?TCG(-8y^9M+aJatM*t?Ro!?~(lTPY1K^T>e>4hwYvqmiqu3 zG`Ttyc~&9B$QEAUnlEb}peOFO;Wc@H^RHJuz)FkDF5vu&f_>#|05gm5cR)Kb;I4!$ z1Jl2cpQN$?qfy=eTeKj4(xzsn!(mU%<$m;DJc#lL>}-6V^HG*S;lbpAhlI~LsS|e# zQywB4{f%Tl;2r5Rtcmg`0ELLlv3FfMj8}?1MLU2|?>f4fblwl(Y^kQACR1^sN@t9# zCGYVpN=I+;)F#U9soZ;!!l5~FN~GglZ;nRlQT#L^(XUg z9(K;Ai(HTDxbzarp59(zf?!xr#W~ekqRIQ|a8&K$KAxo9j&kg>URPc)l2l3~mEw^f zINKE_o=1BJopWKhi6dnQ=}!LPO-KL1Ip58?$$JJ?7(FC~r*1{g(qu0B&fwX3@%klR z-FPU8D6TG_tJlj!IiC2DLJ`xM$WzUQsfDj-<&WL$S|1B}4+#wMzLMTdIpsKkQ(^=& zVO`nBQRpUJ@m}>P3ipnpp-~`kXQcESq#4MUlB+rgO~FL<<>EpOSuumq?=}y?G`#J3v7({%=on*@y|?Dw z{*E&?NwKd#W9QACEJOqZIWkOOW3df_EQm!0FDE-;c(xOg2n$ibBUh19S7rwA%t%?Y z8-iU%USWB2_dx&KE9o%N?)n5d_NBc{_H7sL!6=bFiVi!9eRMn`&|;HJ3GKk%b$2f` z+UGlZ!+73}CcB36?ymz{eGXxEVxdUOk24}SH_7fy%!v8d z>jKGTgUeRLbKbqO0mc&JowB_buhpUh{8_ge^md|xe!Z>(*kUA^VTc@`i@jEA)5{n;>3w|zCXV&%jb8$cV!HAe}i-TMkBXZ@?% z8_ZO=IJfByW+}qXu;C3>wz&KX`-9lMoe494niNcqlX=pKQNidXACSuXM|lmTWAbb) zq?1t}8&SQ%COu5V!}_ake#tWUEpEkkcXm0lAq7<>)~yg1M&xkt-sPj1;}1GfuI%KF zuxLt>Wj37(XQt$n_I2x5lDZJL;7VtlI-{=gaw45T)0rcQmv<#xE>A@T&(&YjcRoo~ zB87ZRkwC-`>h|f5dO?q8iro=MM9z!lw6DY`Q@KFZl=^IM@9Q$#B_?BMF>+-NDDRFZ zD(jP}p34Bvdg(kjm2DZUf=N_b>JG&4RDQsCDBPcb?<7$*E(b_C5qKwKl?RfMAlI({ zP}wNmGOmQ#p%df%j%mnE&*r3=X7Q)a=ZR@Q&yI>$r!t_t=rZMc%C*JcR3O6g5%EsX zdYqwC8|UH*%G}U@(zl5ai9)WSx&F1}ArBa~qAhu7YRpR5 zr~XurI?e7c##Z(GT6}jId0Zepuk?uXev%znIq8c*SJQ#lV3Fl45RU2!s$!beFn#^8o4qIBQFzP&N5~68%FLx zQ{bx5$p`r*(hj_Wv(on2U{&(^jTNBol4@{FRGcXT9G1G1k_=_IE)MS1V?% z#&JYy92alk>LV;B`TVhb=*D9`*u`eYno>(rX3NTo?B;sy=!LG=ki0=BCBP(=A(SIk zFN$8NiOILA04}Cqmf~SdWu#e=mbC_mNN`ZmHg_acrlx6Bh7OBPF5XlM*q~4j*e6$q zw}@9AtC2oY@1}E2#;m=kud=8&z1rUx&#IiV@zeuf&)jHmdBRq!=WDfkzE+F! zx;Tj7qslDbmb@|eQ6C5aYT|)SwKj-mICw4ibWJmb%MGk5{xWBn=zmyQ9DnxbYk(l| zKlSzv}1lk9JxkkX*k4^y*5wP3b%klmVNu5dev?YWK5*q3ux&LFDB|Tm}JpdOFoeK9p(!QRXV29pW-?x zr$pshr(FpMtN4K&id}Ho6)^?^CnRZULu`_w#%`X0RghTxeYt?XIl{=-;uMrUma}x{ z3i*wtjObF*55Fi8+v`SJtMrMQJuPt}Zh3XkIQwgUs4u?mKJ7~S6YqL58cz2y4|V*5 zW9JD#juRU$OF8qR?7HAfWtA@Oq0GZd-?`0h??xp(lT7S5EKuBz0-UO>C;4(V(1I6Y z2$qAQqB7(}8MLe?Y23;j(6D5M08b@@FW4ySzcq%@Gn88=hLW)|+2zH5E`*+4=P2`5gE|zuJn8yu` zhHZN73;ms((6CcCpRn_0ZHT%y)Hw!>?6yU1qXDZZ8=sHGjrEP@0oW%}x-r#eaUe9i zwi6ksl4in!SW^ZGLZ5tZ*eVO*ZYgRkHmJ&GokJ`acrch4Nh}KB!XIzal?2-Xd(qf6 zi(7p7H9Q4nURPm^^(QahKEHs!04jr@m=0kp0m9l@@Ry|CW`%dgc(q@l@sX7IeoKB5 z^Dt<{+rp6Jwp_V^+j8XwyQ3>}q=Nz4#+K~$ag^E-g?WjAz^Uyzfg4eA9N`1|=qFPD z3H*XZo7!UMB0Zd&d2Qu7aQXOmyK;@H<$1qy?^o{UUb#;5%5~bVT&L~I-JSChemXcF z$@V)&?#Iw^$N0#W5eY$EOp@QC;1-bw#{@VpSb9QUpa23N%10!LYYuV%c);=8yI#5G z{mQ*xxu0d_ZjN{0r^tv-`podd(G@us9qd^BycX$j72eO@KxJe%Rxj&j>*0*@^SPT} z(7G(eFgnVCIXgS+WO&so3et~o5w#cmyc<7z@~=CW z63oa3L@Bbvx*vGF1F9Fdi9c>S&uFI!e75$O3gk>feD%ol7>>64gxvQ;0t95$j>@}! zF-%X@eeC8GZfv5hSGc*cwQgaNb*Y}Zf)B2I_vNJ~&S_MrEGl<4x|Z&4=(e^6K&wlq zvF>Nxp}Xtl<&kN5h<*|C8U=~DmnSC``UcxX`s4l z*nS#zm(|B`r%>X%d|#(vjc=&3?@Es+m)TXpL$~_*D%%AAh8y13fy%wCdtXmF(qQL! zH2PirrTwIcuS!rc$w&+DtBQ@AiJ~cprelCz)P1lIHT_-bv- z1<7)??j?{aAd{)igvS~}kWhRf9}{H+<~Y6LjHg7O6-+}NeK40ho+`!=g;M6I6J9}M ziN=oA`$WKU<*S-+UkoRL2vBirUDu+(6hx@|`bk8wl!7pmuk>a1D`hWoaRz~y@sL3f zeBe;ypD8MNKPl-2z-dy2^gZ`*R0J#~m@9ySQY(6ntC{N9I8{fz6s$s7kE6q;d_9&( z^7HZ*Pt|P;fSb zh&N6QIWzDjx-YmV=0gu$-Sr@Ov4lN;g`kw6+BC-fr{6e1|3Z`)1Tr&iG;Iprf1`X z-}O)Zd?zZT)x0$KnIGyabVk1x@5j&XyZaq#NYLA25jd5wiFiyR| zhD}Pe7~E_*5gmxC;@!r!i!H8VkEEFqq&YYj3pQ@c73TWLA{vm}Nfw4$K6t%7a?JLB zF1En4*XO01ee&w~&AD=9l@nZk{PgVQ>W7un>R&aOI;#)o(^38FW>_QI!E7VlNu%f{ajlv+c8cZC5vZAC$$TjicSEt+^6kjyVPl26y{(1=qP{=p8CFiFHWQ*bv({oh7%k?*?%6a#@kHwgk0I9LIslfEgebg? z?l&QMKjc3vjl2}T10<0hmwcHMAo9Y@q2ebFmnlL`$2M%=YBL`!ZyV#mlFj`WI3IQ> z`|0ucro)(W0Sx^}9^(WcMOvx;X*gnq99sxg!;R$FLLaf#Ls1QO1iK-q0U#)y*X4&A zJ)p2U%f8Er#&FLDF~7W5wTq+TBxPI*;20C6?w_3If}#a*F~LW;)Qx8fThhxuR$36t zP5v5PjV0Vs^6=@e_r;CL1%)9-nBZXqOH$T6U~fqUI!Jnf5d#)J0T>Mx!S@=Djg5Ud zRpBmy`eK|+1sF1$Xe4m~O)op7>*>3&omKiap$xYGqfnoKwb8o)L-YIWrc=Su{AOMX zpM%`jC7D>dkF>`%RV)bC8Y`oZ`)vU?my&u#Pc4q2@*cQf#U-a~^yp)^KkEx4z=tT| zy1ywMpG9{b%?FC{NhG$aD9Y*ba8Qo9CT1w^XkQuMLNrm7X!Q5h$clCS5&NwpBy|c> zq%I(QX?uIZJ6E|6h#ug zV;%2&(P}@!dV0KnUOcY`Z({X~X5IvS5jHzyEY*S42SigQd zkBj$jJ+3-`FBk9WHL+D{FBsB!zedr`2(HL}w%jeAvOccnC)tPAp|QnLvUVsnF4|cS zu-?RD-n0R|vo^7qZ#b(3Xuu6o_5sBM6$2t#u5ZL>lA@LE;Qo+zbVHmXONf6lR}zkK z`NWM0hsH2W*9hB?47Pp8YULB{Kw@6wWQv?L_ro*^vncmX6j^*9309E9u*xMAmNr4I z8;0w1hEm%ktnCei(Smro#q0@gHG6{HXOEeI);7a9hYlKJ7K|9;oH299%~Kf2ZTx66 z_hJ5n^DhvN4cm|G9ETdUDa)wO9+Kv!bGMmm0Q#i0za5kih^>gM2y32kT%3?Uyk&D``dU=gUq+F9go9)yPg6BFUz5y8e%~eWK>?x!+)E+^#e<;2 z)qR13UuGpI^+byAr^>37n$fZDDo2068611 zwJ3#tgwK)-1pZq14d(rj0&OMpGms8us@$^kuT=?V?S!}IkM#gostB;~6(HgIN)Y1n zM!Tx2gVt(IsKw31N7aQq>1bjChYa|I-t7?Em~= zjTQRQw4)>a{lkZg+Hvg!;xS9*1c{oVDi)zgMDy9HCtj(;J?y-lsIq^R@lcr;sLm(v zZ=^BhH}C1$Doqj?k}~?iyb4`CSVf62DlVi3Qe8wm_M|6F93@>FN0&AGlt1+5+DsIT z@N8n8h|vy4N4Sbq0Xa;0(Ug%mSJ}1ZkcN%Mnp0MxWt>>t$D#pQDVpC=XZ<*FJ&25xg|(tP(XWTjx5KzBdCp4O zKV9@w7E6sYscfMrMb|A!5{B{@rdmO7eqNNM9PT|Y$;?$k>^5n#%}(sf*SnU3sA*Wf z+IR3tE62BVK&xkN@;24ntGk$T@|UCjp#YxNyOeT7sTtB87LMf6!lNw7!sEpJ&i|MV zK7RLs3wiIOvHRV({r3@eP0vg6bo^mD9ensO8;2VR#$A=ewV$bD5?pUs?C zs|W60WkP|a<)?092X0K2HbjF4G{1mvl01pPyBehFA`6@=1){J$Epne%7`^Uu~vTEI%Wm0ETi@Y)pi{U^j=c+HwSc>jYM6yK5}JHE`7i zeCbf=-9qKpNyGU%3^;Carr$|% |y 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 z%0DVDb3hOA8}Sg6h)wBAXT&SJob3Q|PcKce`9X+tc~|GEj(m%#yrhJZ z&^1wYVEHYCNKOOs*RX#eGozJDbLNv4Ej5vgvy^RsqTiL5^clZU8GKYqor(k?O{I(- z^kvk5Hp(XHP}o6&JK>!qIV)}&_^%j&xbpZOPcNse$ST()HvN~Q@eM*(;pqN;+26}> zd+IVYh=e`JhNI~&UI3K1-UT0(rVmuFRK}8_X0$a>(iCTWxwil*FxECbhjbUqi4dUq zDsMfKr$pim^5Ip41R#L>U7WT10H{@^!t+QHxeQ*(jYBFu%^zO-xmg3YHY42>U~4C} z%a8{X7(w4ttYi`<6!xfwMLy)eyr(071(ORn0{V%r()vos^SWE$iCxw^-MXzs0(Cx+6~@LS&{^0FvP(v>nF+Q!n;q-GFvU zfEA582Q31334U`aq?)2$+qPpLH0U05PW) z#!lbTc0FxZceGv8(E2AYKlHzRqoAg!nu!5fPGIS$3;NK{gy7~^Xv-K{{TDAk4Ei?x z!^7tAC((@%Uu0d|s@y8G=H$Ad(>Z?z%^{9E%tWXd@yHRyRH8iQS|4Lc(IYEr7*w69 z01kepaOR*C-V|<=hhr$A zi~RP@;mMh7?x1LHmkI!iMml(wT^ialuixHeaf8j}JT$s21do|TRG!Zp^|JWYpp&n9{X*q_rVvLV6|d!7&EB#L@q20>;awG${c zBD<%-euq_Im>Q_V4IOSQEP9Xt)(B_-#03fEihF8(lr=Bjd?l*~LXnY&q9rgPneeIu z6MzLP2?H>gYi<~(KYQFas9H|#Fj+_DgHm;WwjZd3{`con14Vj z@2 z(*7RV1`%;;n&Ih|7FnT_x{;+L9v(Q3uKAySf8S2{t?eD}Y`qMet(QS}_^O*2rX9qA z4-K`*1Q9g=$Y6<15rhp;-IV~rszYJTiXCv_n6Yjopg)7=8s7H47JI>`UIr`WV1I1O zpe@B@Z@moct(QS}LOef70Y&=m0??!; z05?ZERR(H$82$vy!2Dxb2AerC?ncP+i~q_(=Yk7k^|zW)W-vZ@JzBO++xx}wp@hRA zZaE)uN&45X&(2oZFhIj2MU2X!#=2d0Ao+F#D2FUELaEfnk~0bI1P#JMs0E=$OVr~_ zLI@l+j(cn}vH8SsyXB10J2Dd5SRTaX=QLcnQc(z0e3jSwFwlu3kzXOwt*#DB? zIaK0BXif?!1I`7@>8r|_-<%vboQWSsO!X>YXD=9u0@~eKwtI*|wJQ#2b1@pt(>Z85 z2vs2}Y1U3axy2x%dr;-HpyGtu$U4Rae15DiT{ z$6zf`ga=03(=!#x&oRz7(@20wRZz2HeLp`t*fDoNCV|NMCn+9N>*?<9ZkR9e-OT`} zwQH^$+-d!?-Of7i{0yAGK6u?!XvVu3-FqEOB^nH3sNeo7AE1^(GlXCWAhDjCs&d4~ z30lhQHFH#H>&OCD-v<{sVNJbw^9h|T0Vom~HVpq1N;HT(`ySHWpz=hdCxRxR;lM6L z1kpe~Jk>3e)Qt?B61jTl1>nf9%8P(y9U&QAf82hXsGkkaN|BdetUS19Q(`S)5YGw$G{#g z(f6{kv+vH%4^J-kjt}3Qqra~XUcals3k0xnC>P`q@Kj<~Lao7NmX>)Rxmz#fotzDt z*K>E4$(Hj=7d+WJJyY#$iDl>v%%O+hZGHINZ6AJj+lRkK3Mk8dVFL2qE?Y=-Z+sPETQR19BNcJ0bo_9)8*`pC10FhyRb|;rH4? z0EVY+_w3_@?Vf#{&^-hYKsF2kKNOz>D~;q1kkqCIvTvvr2D%S=AS8c`B*C+V>U9A1 zQeffy$OSWThyYTR8OVo%wd=TBJ^CAi0De8?^T+e(?;T*Jo*$hZe#-6OF&_D!`f2X= zG;V_qM4c8YW(4#kK-KpMe3j(m6(GU3BUS|3=w;)tm#*=e11;C1{?}C1O0UFfXyu){ z8hfy!Bnz%0D$uS-L}fIOmN&PAw3|+vu!C)VAKIGjuZq*01`xny1)BO*pY{5Ec)UyG zuXYfIL6x6C-@QCPhwsb!x7ULBkA55S(Fc%w#j#wkOSNY5;4{G}^NOfrsA5u12smFx zGXygD4@#$n_r&o?F{gvmBhez!MMEJtKsja~HJ^d1`Rv0Fgfqz{Py4U-u^4yv_fe^a za8pF3c4w$*R?g+@DhTIv(dnGWgFDRyO6L$=H#~Xqp!KJ?(5lSu}a-b*aP7{{Qj^Qmn15^=l zjhF*4#SEgyvdVBoZBf-WN5t|3A=mOMZX)3rGggmkfk{&vJ)#Sd8_u)CnO2m!W<*f* zvxTfyq*G$Sxzl9?Q8wdf!`??p_vM9Mi1)-quz41V%0N-OdWbsuKrHx2%0bfGl&Y%u z9;%NFZ6MiB!ET*D`)z@c?b}+7h#)*4A%Y4XwBuyyeQhm{m;1pt>2KfPXWvf0d-vhp zo9WGOfBQ|m#M7&e#ZUv!N}tAyNdLhbXa4==Vs6Z?Z!#|pZar^&Z+-Zn&42JOo#@)T zG(UI|O@=(H7V((x@(!^x-zf{stPOzh3F|{3rByvp3vXjEq`522SrXBOR!mIWc6cGS z!*+Dc`V%`FO&ZiBpvi`~2jQ)th5~^(7#^VT(NZveK)k_3YE4J!017cf+W^=P@e`z0 zJ(M^kK~-GB*L}oZ^pzqV^P?Ol@scW8O|A!yng~Qh3_5h7S?K#(07DOX6mac@KaJJ2 z_xAkd;j42pP!2EfGc1|&vnH$N#o>AJdv4;K?7aecU-3hm$+N-&*wqmF_UQQ=`nfQE zn)w3X z4RK2&hATvzzLDnZ6uW_2XF-0MbeBV$q~>)Od0nJZQ@}%MJHQr{Ca8{ejM~oRf*Eur zr~8pR(~6wa)6Vp?Gd=B0?K_j_ly;_8>S;dkfjd*Htn%g)yYEbZnImXyI#8PfH;tR< zASVX&Cf_k-&_UnldBny*)2_X2k1)M z$*qh%bV9#4qmD75HH^YL?>ax?SC=bvJ#}Qcwz105cr>7iyjpXWTx(OyIgHUG|*@4e6m}HF#!8S1AwhW=(ea7_e zGuFMjF!XJBheiZXG6+2zvM^ETCR!9gyiSh{GYUM%0tc-df^A7lHB6uHBoG4*Q)u5= zPJPBep6fmCj8$G5pK8W77hBb`OqH){_+5+eyOariP+Cd0Skr(Y+SfE4MUV!~_2lxS z2L;CV_rLq+;K`%`#hXNIpEaJ@VLjw1W-0m z)U>3LMjy}-BsI{t0dwAAMGX7=mR*!@uae0cnI|~ zE8cIWh97{Ezg%K+GZu}&y%X=O03}PwE~IyU@r(YuD`p}58Qzy;f}OH{9i@wCn1At$ zUQZ`!u%FcuHoc;kq@;F58WPbx(nsPuwgF%gkgx`Io{0Z2AgYft`6llYJ)*XOh|XF3 z6zwMUff)&L5fp6v=jCM4kaKHS7#6_=fp3}Vu+p=DSSW5l+z1K>{5DIbv#}71;F3BJ zT+Wq};1WV)(43&F=X$QxFKDnO;}Pef=rWUJiC5~lnrZ79T-z4JS+90^&gW56$Oz9T z1I}!zZ>8KBA&V(#NH-+M9sk@w@=l3g6q(Ae3EL}epa6|cmj+oW*{5+Il?;(ksA%^Khl4XV*J}{%_sP zR<{+f4oy%_TFcW!y8QRQ`?NsT#9yBGwO6yAYvvF0`E7gji1Hwab&9F@{n+LraNBW<*75hI&ULCCGteJE4Ua@5cAfl{NVc5YJh z%s73{thdb8DtE6NvApmg;XxiVWpf+|HJBMq`CJhA7F&EI=zRr&JJvJnCc+%fFJ5}* zNVaUThd-Z_KxWPnJ0E@Eyt{a_pvAl*7{~I4UQh({DT;I&5Tm#!iVNgdveF`-#k84{Pictz4< zBo7xdjByFB?>EZ*YcTudhsKC9RGIngkaF`~X_JcsPb zvwrVLta?oCazL9Uo_{g_ii=u_lHw@5f8^FFzTBfIfLtw^V5!1!sCiK>OoZ&Zy0%mh zntgAKnYr%9eg7?0Tof67bVx5Guboqu_XM>zJO&Wu`$nbNC>}A z$?jv=1AW-dU$YmezRz~gL0P>kFS(0vF0MrSU%brd0SP9#I8@27W&-;{xr)cO`&H!y zFTURU&7Z3Z_6rQa#cAmZ{Kh#&MM)5m^e`Tp`_eTEh)bHA3&r*1X&2(OK5xh8Xq#!x z@fPdc5TA50O)u!x*Xudha6_f;+GMdj<1M}E7ip=%HXaEAfw_-(6dH)#+9$kMf_tWuG%^F1@-|TEHqSR^td0 ze^z_lM*sDvhe)dU%4;D=4M%nk%0!9p0;}!X03K;X7-o{Nl!Lkq2RLmHxqE!)I&th< zv4)t9mO$y%j9dr@MIJbN{t%cWNEUfv;+sGtga{f%NbrHv)zf?+5ui%?lt0@pe^w33 zJSEKj2th_xd9$(CY&KTs&Bp4y*>o2{g9fmk29|C_Kn4L9lp_0WgIOy883@3Q&@Z$s zq~PPwGAt*Fzzi1ou}0-$F%fwD3A~@C{DI<>XM5dx$$6a|&!3UElvwUYvS4ubfh1TjJbZQ2V_@AEbO^CH?tK zi8_kf%GGRVIaglvy2I5t1F5FIqM5051M_vEqUIvW$IrV;NXm8E;d-wp$qoW)^blb8 zuV`t?q*jWn3OQGyVv%c2ikNXY#h*7KD_#-jPkS&-cI($Y4dGGyf5}ATWY)%2FNIWgV9#i^aCxpi;^UnqL6B zC3#Yjwa(LzG!Uf`;Kr~EVCEOewmnIw6OoH;=nOA!tb1BlSYV6l2_3G)Dx?`6sd;4} z^DMu@jnDq%E1= zR!(oEVa{>4a00R3PVn!%J-<+DKATOMHbGpcEy-XE?V-IXt6+9cDRdxuqY$fr;Skm5kCMvQnY_M6fsU>C^?#Cgm;~eh-Pg3E>JEq&T^sBn7J!eIxFt1mxtF*v?$u z4ErTHIBHHphYD=gK7N=BhgV@{gwo#>cpG3<>!GA8b0wA}zA6Gu)cX{u-02;I1#>MH zTt)&S30%0GM4~M8S=WG6Fxmq4l5!X`jl(C=+UyZ7W3WM(VctsP2GaD{l z`b|X^%Fm#Tx+MXjP|>;0*E|8Z@iLLOFdhMeJ-=VMI6Y_sf?Z=Bi3p0|LYHfcwqtDk z*eMpWAM(kcl*dOl-nnPF(=V`>vr>4Y)U|!~zzQFKAb_F>ePWTundq&o^av2c7SH1r z-5@PeZsa%*a<5KKwT>WGr%0H(h=`;@D>V;GsJeo#9brOxH5_t_Tj`04!STk5d<^*L zMiezK$^+Vh0HuxZ@JHGsF=fZJ-0It3hpp?K1WeX@*D6(j7xj*KQ5Pbdclq+(5%2CD z+|;TpLHAR034+eg1_~A=vNR8Oce$GzoL2)p=4eIHtqW<0Hr~;8r*C~%rv)mr&w-G> z41;vP!Og(VaEb)nu4kC$?hngke!q(flik%@eP~0CH(grI+X8V9X~U9M#sk5)UlX8I**0{6H zCv@jVM-&9$6UPOwRswM(*M_H~gBFIcA1HS=qZrC*ww>5MXlx?LVEU*#c1)Y^kJjD;R zT#??6cF;&ix5b@)H@&X=(8p9vi>x}{h@537?H%?Bsr;17H)qKgtAr-h6us0Y8C7-2 z0@Wh>+)!r6QbAOmHMtBfIbV;AGrYSU;moMqFjX}T`<;5#>wS~l_mvCT+vz{gYZQ-^ z7mK7Y5l9c%s6>+@8z^cMg$qSp#oU7n^}a{D>jI^^ls7!%-m&Tx;RX5-u!=aT+<@C) zhd#fvz9@7-Yx`9BxpRvABz*;z-MrLuS~47G^7pJ%@h2k)C=GjkMs7iNP1SCkSWQA| zxo|m&?&BHNOlQjwLpl;xJlCoRWSvtnWl?jCb6H39j9*93s3B3*y+g7(WLcLS{kvq0 zHHsUo6oK`6hm-eHA`*tDi@gt}%UpY^1}MMQ7yUB=l-$di&OPb8mX!Q=9;-!_ce;$S zz!IW=$_1H}%1^{1po}+)JMlPGU9Spj)78=&ELH_qz1leQp63|M~9VPDv+*&h0A;F)M9`u*J?LX zw4u4dNU{Qu2;lxL2lG+r>bV_wkm^2zde1$?(s|%Uk`{bXMR5;3qp}& zu=ztaK%IzY*d&uty^Wz^8paB1QI07pI!7HT0g6V_21e`zKJ^Y8DAVyW7qrN`n<*`I z*+aZYU|L1g$(-dw_O{nMpD#0+WRa9EnP6+V8^uhg6HPBs(@i%>vDdttr~45ls6-SrXhA zA_~FHKys<@M6f|bb?iI~bU*JBbY`9u9YxcuZy}O7i2ak^1hM4oNF9sP5L3(fi2>~7 zg#^^{7sLK*^msDeL2fLQmHbKogH2%E86F&Je?+V*$YW3-nHUrZXUVZ#+yR2<$tC_9 z5&8(!Bv<_Qr{NGi7t;sRPv-N4^exVP1sCNzByIZpJh_E{$z?L#VTlsXD#$qbCA2H) zDuU``K&cclfOH00v%V4eH;IU2^B|`N(0xGR*{TJa(FeTw?rAek|C8g&)mWd!d$^u1N43Jo4$P4Ze@)K-X{Y(CS z$QLYtoB4o>K`&hey@ol54APGSa_lC_{m%#ey_*2e1xOvh5%evduj!!waybTp$IIuh z0jyfbpVCSg98Rw!vI+sR#}UJkg7B zK0HNxdxvK`cq6L~6th`smVFrzWu){Re=Rvdz@mXIhrK;{Nip~IGJ$4Ot$jxwM0?5< zfYj0qJi|JiCoh(0_22*70lTEMpM3fD;B@zV|MaUt|LB{;TLZ(oz%#kH5<~Y6?+PsH zal!Qh-+i_aV?Yawwjt@v%V&zuuJ`N=FFA}1w>4In?0jXwZDX|bICuU8V^2;hRQPr1 zbG>a+M?%QqTJ5COVJ$tW^MV3PGU|w@4V(zICcnl|-4TD(HQoV&P^;P`Z!gR+1s{vp z@A@N|0;WMNUW02p5QSK^jdrY7Skh;rBG5-lMpE)@G5=T4{e zFi@(T9)X*H=&hzp=KNVsh)tL1e(?T4|7bdRKj#_&+Ir}*jVNkFADGYxTk5>lX~O6e zDG!qz^`-1k?Xi@Q_|i(q<;<}sznO;gTB7hx|CC_PzyIA|L6PYmVJt3up(Xx&c`rBb z^1e-5Emfb0m+{Iztthq`!b|2ZY+77X=@%m$Lv8jkkLDiM_v2$;NvK1d1=FVB^6%>Fw-t z9w?AMz9CyqyhE=Fz(u*kMX0g<-g!HzK+5UPLi`6N)Z(ga5tT}dnv5FtLdthmaBj(^ zT-+c%gO2}mvZp9wh7~1Nb$*u!0*-NYqT1Yp4?@uo`naURuklEIqz})N@g<-)_%O}* z_~-NB!)T0um*dNT55Wr@8zBC@!p@I>fcd`(`G+~xhtP-n8B9q0`++3P-z36n!D}g* z+%SXMVYCe5DHi(S7#RHVt^5NI8ebEnURW-8?!TVXbPoD262#-_mlp|{0@!9>U{Uce zOKe?u)bQI&q&NEr?_AOw6j4zk3puDPybt z`UXeSAF!z~8fSNKi22{q;)A%WLH|4$-O*5;Ur~UWzgZ%ph2Fjezt`LAIZnkD-%j{3 z*qaAo|L|tSd{2Y&gTg;H7ohrOaMha9sV>8L3uveZiM-iQz;vjvWW(T&`hUKg&E zFk~uD6g+}?yfeeQ8eJfVs&0vkA>=#{jv`ZAH2Y3ecVSKtrq8hJm(5usZG-;+QG|_#)PiirhtoNpEi7L)gOe-l z0Bmm*7ZQ_ktn7fJ*PuWwYqBla{i8ocsRz@}QFyJ>s{gpS! zDc01wq^__fn-#pEZ0*kT=)gd-F)k@X^84TYb;)%Ql%hnKR`9wGw=X?v7GzWudIRdC zc&p)Vf4qNy5@SK~mWN)L6n{f9qL?zpK(@mP(hfO^=2N$k}-1q0$Ttuf|mB<+GDY3(uPhNHWa&Fij@q=R7_DMG?& z;y`T&Qq-~I1U?iNEzN{50B{#EBE|tqP9Rxk#6UP%&IE~h^O0tg80zOHUiD@)4&KL6NLG$D~ep20Tu+2kobu@*~d1-ASd9`*qSZmQ5EOU zP7n8wz~J%rY`p?Cq~xI;zju6maf(|9g+)83NQ)T#>o*4oqMz@beRI}PntuP~;lbNu zq8ZuS2Ls3P-kXyKDiT__iU0#Pgw{do;PHc!Pe?~bZt5rk-xfySZ2OLLl@qpv# z?g%P7bPUe}mzZyaP(*cn$^*GEGDVSrCmppnt`!;3mk)@l5VSs;37IWBMXtz#R9H1h zgeDzV)16056A6AF^XUnHdcr@$C)^zBTI(~eoAwsZIF8=p8OPC`pYg9s8rgn$~VMS*>^!72#tQF_oFM~git(p(*+fnn_#H!U<_`H%aITPrF* zPtW+-cW38^Cs^5sC#Uo*K0W83PpKw#^>#X5l(mJ#H?{n{P7vUBX(64{_jXf$UNxv9 zfb}drMKa;+F)hY2lhsP|1hGyc7$;~YeY(7FD!Jq;19buR#z!Lb&kv~x{)#q5`Wj(< zQV?luUwZu`mA0=nnOz~oE4k9Ss*eK5oQ4;ti-<(FoJcgG6gNPDQy^GCq;!MSt=S_S z;^ml=qlM5=LWvk6X1e3}B5M|LCa%}}i$WU+F-&&&WncKhbiw!VYf0~t%$XJ+e$FY- zd9xqCU3JB#qq~amoh(0`EfF-^EmmF!2YuzNEq(LV^VhlM$maI>@WLyvae?3w7?K0G z-tERwm4g}`ZvnMM;1^NyE!^Zo!^V?D+jU)QTt4?q>-y;^7B)Yld{ zXlviz`4(1sd!wQO90Rst$Elu}5m?Xt0HSrVse?-Z1sRW`(Z>_#teH2b>$EvtXRW)U zvhP6AUDv?S;Xqu`f>tuL6UlM{=X>DCrtJgY2K;9b+Oe6y|8iq5^0m;1sG=D~PHob3 z&-J{=oivJ_{DCHI-NV-TS?m0$b$`+{%$Y~()~=K47Hnnk_=8O)bs`I)EBMM@oTgSB zKx5jD5DW`Fek7y7ige4gs!y8f8Jgo7k2`5AD%cO0v_@O@BLx>((h@{nNX7v+f;7`I z8%D?*0Z>&^8*V6kB?Tg1A+E z#X|TlAS;e+V(tN-PiA95@ZYNGs zPLXBQM_I^Uu0yYsp}&Lp&(AV0szS$a^`Y7SWe@o-7_;+O-Kb|*k8c;z=f+dpC7;#deLlX;dek3KbJBqHe@G=s%TBSj zex&uB&MzoLZ~{bf5)FJ!sg6LeA0%LK3RB4F#*VE=aq1=(Vzy!6#G!2(!0fv*sYQ7F z&Z-u?n!f3HD=#R2%?paynKoJ#RZM?kq);R_>_87t?i!$vFmPZK#k%2{5T$5%LGACI z9KP9W31Bpnly9T#+?I7SA28kK1E#w>EnorzJ_^$`1=&7Ig3Q>oV50aiIAG`_1{a$_ z68Mgmg5uuCaay9;NXeO?=#NnjT{R*NOGCU*f5hq5NH$q_KlAB+ko0=X)EN($?(W%E zkefMyn>x1XyL#l~VYhrwOMDv>9Q&yg<6mT95@@qSfj!h53layU zG{!v8;XP}*3ZV5A<>X&dy)rAT;@^siS!zOXK=m)@BNB7ICtxeX=?TFNqZs(c{+A2F z#}RxIkk^Du>Aw=lC4ud3X_b6c_XEMj zHRunA!>?#6GRel=a=o)c*Ik0YbLuw6UadWOFOKs=7(NcQSb{qx85`^^XB`S#x!b|e zenpuP=NdAJ503$likHFGy%QqbSYoTudpfol`F}fgQkZlUzPB>>R zjeJ8@3xbl2G+O176Oq-{UN5n-h#)&ZUTiFwe>kk^=yEw{GK3LzMCjsvwW%q3prm$v z&dts7@+1_?rImc^@Bik1`tS3)5umS_nvtQ66hbew%NNh`#}%{%cR;qXr=Zx9SVcJv zuI7ZJr&j;le}y6dVE4E$$U_8@o1Ss%0^CSf5UeD8V0G8K z>2e%%kM9y<;*d2Zd^6%<#&1ZX^qApNY?@*iXH4M03e~811@9E<8(8mkDXG(5j217K zVU-%I*WjHU$oJig((7*bkS5@3IRVmBvXd*@d9YU9+N*#6e_mmQ-Ddq^#QBvnJ$5RlM|9Oj zF*OSS*`A6==l%3*LjM;}laJwP&`G;*v)7D zz=PqTil5MR-vO=iRuX6(;1f083G<#tEq}&C*w*;CxM4tbQ})dJlO;#HvH8 z5MV-8S@Os>Jkqmseo65gOu7IBUe7sJx$@wSO0Dvu!FvJB&y~&d3QjI>6=iT)>1=*o ztDBZZc2_}F9bM^+OGP!zwM+%u7$h!%$&@G&6{uqgaq~x$Te5!1 zIGJ$KkkHOiRY2sqjYsqsPf~Fk<$7O?6^f4_B1E+s>}1KN<=RnYnChHlM#QwJ?8~Q5 z_>hvvctp<&X9w^}wgl6GFUt$4n3JNirSzQ3o};RS>ZrJZYB|!;!9L|6;Al`);XM^5 zvvHc2h)NiZs2!bQ*%Q05>V1hQjA%AuAt4r*3dRSj{EJ%T(r|82P$P(nB673E!x)s= z&nR&rhk;HZ{w$$rmEEHf<_2pvo0c9NGCC`3OV%67+LtL6J@Yz{q}ZZ)9 z17>N5$X*QNg+{Q<)B(db8)en4LN%4!-dqZPK_cm-3y@#QG@`9ZcUUHObIwe0|=2*^7&IaG|QzQOn zrUT-5-j-Z!qg_k)K}4(c?~a7pb+Tm3CvFJ?YPx1K5@@Bw#vg8i`{eJNPNg<`cpomZ zF4Z=EEAXIn#WjH4Iqb&#Rk#kTTRyO}nHlMU9-5HLN z6h;(9v8EZHLTdN<@!P|5*qkp1GL(Gq`rWJJ*9a%SJ34>)`fb%teHc+{(-BAeZ(g&m zZqu(qaCaXF+|F=)sTzyU^Lb~3lh+4FUjxPa_TcC>PCY+AdiCNH2>;@1fPKYf2I$en z^P|@%hv#pO_W3W&z>C8-r*F_t@%LMxiqH1n93H;9puXel&eFlT3HgasgS|DPylYoQ z2gP4=o#Jof40MqZUf1L1LF}p~yj^9I;jP~c@gR5G4C%m5n;{+8c{B6^a--c88bqc6 za^w&NNT@pSLR1$zz|_Sin4Mz_8soZS8Xope4b_R128+On<|p;d5c*iA`^cLiP)hdG zX85!jK5d47^qZl(b~Dr*!!o*ThJ0X`&5#f5ZW*E^6Tv%22U%yD#u|1xpnL#24Z|4m z$4JuyH!#3;@91fyYpF|^P#{=Sf`Nu^JpgGrzUP|$V{eAK^|Tp2ZH7;q;UCpzxIVOv zzcEQfNhALeG47SGSG5z&az)5$u-jqE+NNN)Xs#OB24og-oY~G|x)bLC&me+4#SOw= zvU)4aIbNhpIl=%Es32+Xzo-YeSMFoj7~-a1a^Be-43IZZBx%5Xab*3k34JWWd^;i* zHRgDq3|)~5p5V!B>U&0oVTfT+NKbYdk*l|v%5t;H0Thh*V2T;;jgucqfG{JbK<*`_ z+XabYCdP~hmx6WN`5ZryUl=HLR{kq`!dd1hq58hY!3?SHFj^FnXiWJ}auR(16DA%a zR<=G7NZ|>m_~4FGw?j4u9A2aaUJ)NqSI2=3{VCrIjv$bRh$$mKaL?Lk+z5#(5fhxk zZBU=+aKVGbWVcE^O;(d|$(f(x%Nf5#oFVIXA)MDoN+ z+&d!fzop{xmBdFlnsMkE-~1#lE~@(XJrGZzF_L0(IE+z7h)U0{gx0wlYW#Gb<0py< z$nhyy6UhH3aW0KU8XZjRM9WU~SrA4dJ1^1$>SxO64XV^Gz+V2FgwqDYX$7k!}<@iP*kyG|q& zL!5#>%mn1&7Qtj7@^M`WdLYb2Myz1>&-x7k%rgc_p-kawSKMol;odVM5x1+>tErNE;vnDg)X^d9RL<@GjM^>Uzxu4{afz zl{e)@YXrRM(C(_{9-`ThcJ@;bSPB51O$ zq4R01Zqr<2!#4HxQ@HcI!rMxE$nEX8!cD*P3OD`kx-W@A0DdO{P(BP$*bTxY*K(2N ziGoCj^b6=!QUm3}t_f}ys`W8L6S)5bv`coqE*PFQia#y;(HFS!w7@^}1>RQGVmjRy zxYK=scQ1TL#Tg2XyojRlo(^%N1e>@8#9Qp@5I3@nIEH3pS&p{t4IeCk+k633_)6x_m|MBSHf!U$4s5_&cVyCNyC0 zm@PQCZjQ*u8?bkh{NKTkYg+ktW<5_`(&i^0;G?SM1BX?EVy1I;;&L$Owg5{h#+~#sMNDfs3yQb~NcPIr8ximU~JiLi4=znuik?gf2a!cTS#_6*|HG;Dkb0F&6R=Y%#G`B2&EM&Rj#!D#t7$}}9u z!oi^a(SZ?49o<7VNCOeC8M>9XrtKTB3Rhm5>qpn6)JkG?wF`O2NntJCScI=g!uRGkBSkP8TqgGooF3nD== zyo%U2T`M#JmW&f3%D~dJbX2A%rW3{?R%sf72({{Q*tX-jD`oTRSEsdhby`$5UvoJA zL9fp4F2?N;A?i!E7w?sSQT}eV^xj>ccLaXbY;-QBIcyp>Si0oTeIPT!^*g@cV=YM*0nc zL5nQ-pzMb6O27agl=R(c`XZ)GWp8A{&PZ7;MQMJNnFxd&nVO%>__PkuK_4k-Vj{o$+*u>40DmN^2Hk9HiNw@kpy=nXH$_Fp@M3 z)f$N+9$bLPt%m)>ypBcstcAZxDoH{fap%SU2{%g>D+@eY38%`@Vfbr;FqRidQ4&Kv zhSRH-5^fmB6l$KN3p%@+Qzu=6o6ocvu^P$g;OW`@lWleED-QNu*BuUT? zt5ytoA2psBE-K3b!3Z3;38Dg0w-7~Dl$@rF_APSZVrE&Pkapj@Imw;LoP2->`er6; z>N6k)L?ITDs!f!J9tIIxrc$kvbZz+IZUlA>(T#xjkjWCvz`Oubc&y|+R_dsR{XHQ7 zyTc3$<8()8TXF9p5Hyi$c{~K7Zg5AG846{k(WNYeL=v8)_2#I~T;!mPd`m7g59gNw zm6nxvgMCtKOhjd%7_49%E^o5hlP_3F5{{`*UFQ`u;FY#iT7;N~_alrx-ch8JoU?h% zRD?)eWuPu0nLe06gm&DC_yLnm3!k4j=EsA&@=_Jk1u>~*@hdQ$m(*k>8K8i}@F#gq zgqpC|fU=|5l=E%B^rl(e^}*x-zzWG4b59mm#U5N{d%&KFM|1P*p2=AVhH|);q>XT#ok?5!@qQ$thRF zX3Nn+&F^SdS#UJI<&i0^cWn8vxP_Gg)#5~e8ql>VP40kd^hWb+v4h6|gYkj(Q?Y$y zOv93-WD1RiG?#*$NZQOYyh4q?LRiSkRW8@7z$v651B6KB1uFP}=wVWC-SYj^jVUfG zl=YAWoQ1DQ??BQoR4>BvYi~$U6jHTTFe@s!odgAPMM75ShH@AfjSgmRL^5P({G0Fg z(eV@PQuFDvo@GqZ-#8ym&i(h{@rm)<7k96YkAJJ1ZsxtY{_f5E>gv~LKg_@W`up4c zyXakVxmZLezxnm|o_%@#!kN$t{O(=)=G6K6yXoQL^qcPv4&MF7G51frt8@Fu58w29 z-$yikU*b)cK%*`G>RE58U){mam{@b~!w1i|J%GwIBfI;-%x-_Ynf>tN5BBoI)x^wZ z=7;O|lfd-vZ)R7E>&QwjBjZlXtl5vZcuH>WjOfnuFF&|He7OFAuO?UX`@6fV8&DMf z;A+m57I-uFJ*act8{3~i;d#7t-FwUU(O5bY&34}3-QPyz*;TMK7EAwqaKC^%lzsSc zJ%4{?-~PCm$F}Fre{^if6-w@Sp2t&x92%BI_j22OP&Q(Q4~Wj@UO;0ow*G5Z91L}Q zM|K=~S-W@ici|g!5LR2$z_#*g>zZ0CvQ`@XHI$sh$-SYzvlA<4E4OzmM|9Pzx@e_{ zUOBEi6kqd+fiLyI4y`9({%|7Q^eu2~pjI>0PI>LX}o zRbvgvkfMaC9&7MEiV8%yxTRQw;TWMEB6y6nD*__WXR;B0i_9oNm4F#G0rA(M$ORhy z=%kPs5#M}!ba423KzbhU5R*9FgZDqEIk}%GCI28Wc2k6Gi%^)^3Ze45Elt1M%IuCS zfi6$#fT-MyVqG_NEK!3Pz`-?-wTQG1$iOXxnZPygg5*3T&7S~8aS8s;(ywD&qRWG~E^TijQ8HwcNqe!u-gExtTBt))SGy+>eKMlwF^f zASA6nSEIeUPf(K9Xm9azQ?!@bJmApEZ|mXS=S0sw>WgDCh$+Ke2E`u2FHQVNjU64@ z4P_+dYXaF3NLl65_XEL<`tEE$za)c2$fHp15d7?(N^nb1ywFoBd#sWimGK=MExMUb zrV+?yZgTRV>AZI-C_y8b)^JWCACQ5t7+>TJD~R8OQ}X8+Za*E9`;S9*8DEjHc==xp zdgR^X-0RUSr&*dYFN=r(+!WOY;otr5fA`P+G5-3uzxxmW_g{&>N?soxhr_dHWk>HX zASlmD=rVS+BnbiOrIXv34=j$XOo@n$E(F63)5+!>vm$uT6s$^0ola-8kE~))X9qZY zJmRpXp!y^k0`@UDI`w?-)d3;tr^7-2co9=8D#XWA5Br^>;B)5wd@-F~!Z$uXCV%lS z`e(JFmpK8N{rQMSnS|WM%HYHRV!((gBNVh?K1^B{m_r&2M8O-ys*SaN(P91+G&)8r zKX+%Z8RH4Mx{G#8PteYf9SLk2&)u5Krn!}MuF!V~CovG}81*>yf&n5K`_;WX;{Rvw z-FhTRvi!dH{1nkxV7I$BGt#f&?!6*sva_nYYo~5oS=BW|K`8n)E2bhNav~zDD*I^! zh=3rGBB@4_O<;+xuyPKPv zA2UC8&j0-XL^vi}G^fLz;Qy$_Zpm-NgNEB!95$|AZ^CVS4o#P|=d|~#JmH%f1f_>J zQ1v1Z|B#)BX%?fFRK}3qJMf$L(khLD(x-md@l!v?QBBcLS_VK1H$*R9BRSs&4;5#j z*&kT@s;Cxa4qzLf*$2vyC4h4r2TdTaN8c*k>tt_u=w@$W1_f=yfxi4Xmc;&2g8!mV z{Pv^lEpPgTexF`nQR%`g;hoBY+Lhdbg!abs@AQ_F)@njsZ;Psr!p;R3y6J1pC+LZ3 za`{tz(y1iqD910azWYwItwLul_&^qG;q$B0U>fT)5u#OC=*?O=ICv((?>KZI79-}k z6t7hF!E*oDlRoO#=|vOu?fUVytE|4LBlD(?pI{8*;SoXcNvmbH>EcG7+i&Fp-kDr5 zUmRDeq!w-<5K9zwCQEl}s+fB3fs^X(Rt)SxvJmcGI_u_|1j%eOjce$ZDe$Jge9L;; z=JM8SE1P=P>$#cdd7xN8yf~Q;rky|)z^t?iz_nK?!ZB_y0y`LIh)eVm{u?!ku#Bo@ zoR*8?S+D1d;KD9lUvOoj+eC(E%Ul&T#VOWeCQBhS078EyPVZ-7zlL`MB+?J?MDkM8Z3HTQPQ zn%jO^E7A-L%!N*om}TZsKUb1yh2{p)nTTj7&Z6&AIhs>JFgtIlwFl%NWd6?z3 zEpzSc+zmIHUsVIvI z{fp6>eDVC~(esnfIR9_{v&AHP-3c8h0b&?RZH3K|`5t_-FdlT|UsJ4P|DVW4{PTbI zyWjp3{(ont7XEUM1z4k%s1-1(ehn?ugB`J0iN{DIY3D7}7k|&xCyFUJv+6C~c|Z5x zWb7b-ID9=;u8M{&h`oAs12Pj{`tYG%0c0ygU}aI%WwQDvj&~DCG`z!hJQ58T?@K4q z)XcUB(U}A(!@h^XvwejHy9dg)dFRj*O^!`n@| z9NihmgxAcb7j1Dl8onHF+-QrV(R@*B_kwBw5>QF%R#{Bi2~N>6iE@xLC04loZ%Nc8Xd!G#I%Q zFiZ9fZEEzXll|pAG_+^4L8_d!RM`HouDste;ZIXMZ$|I>VG|1sFLH4D>GYup`?;+m_JtNsrwos*TKqN(P86DzNeeQ+M zo$7}#J6+$4CC!ZG7E0ps3S?=<>E-Z(u*%Z!>(w9rkr!_9A zer2Cp2obJHD9)R)s^BXzMJw$+Yj85X9tc1)tr^TxL56}T56a{$VnZ#Wr8QaHDouyjsW z!W`QponAY!F0ZM0&)Z6EyENn8cw5<+fRh{y@k&^s5JDg~

qXv~KnyWRAV)&M8Bd_xBFjpahL=#2F|&q^1xW z9xp+@AqaW|$byFb{qh)nFW*dFwbK~)+2d6^d%S9Ak5Xbk$lPAYw}uMg#18?gLGzVB zz(Q3!**uZ14k7gN5|V<@eWe@@sFg%zxGEEW%l$M?ab?>wo!2>~TtNkxsRz&ThvHY*AdI@~J* z&XGG!n!`Z4Q>H3xTwlu%HgwkuZ7AiDy|)z5F#=3ic%)M5rc79Hb}K8$XRDuP z&^%k@rS?;wV_TmR9Ll7|`Q=(0%pa-Doru)%j9*HNzKtrRKFAeB*Rqk(Aa&n}D`%=L ztfB0S(fhJ4h1S+7!Za1|Qe};GpP!ow-qe;DnvO^aRioX?1(m9fw<=w;wNOSUZA*mz zWM0(uY^I3qX5~EWyS|}bi}LVY`-O#b_uerq8=|r9df{!cb!fhP&sgP_hPFj3zm!+< zm0r}m8*z6l2m5U{Bd*`573?>7ElpoFgna!5=VjX^t$mmjsv9JQ*qXRm<(7_z>QI3a zo1gTeUI0yJUKQ}qaxg`24Q9_2>Ji*knC=h)j*=xGCJsY)ePiO@3cPN<(o5iVijbV7 z@PjIW8W�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®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 SAs+yfclN0(J#0Ivr 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 literal 0 HcmV?d00001 diff --git a/crates/getter-operations/tests/onboarding.rs b/crates/getter-operations/tests/onboarding.rs new file mode 100644 index 0000000..1259f9c --- /dev/null +++ b/crates/getter-operations/tests/onboarding.rs @@ -0,0 +1,290 @@ +use getter_core::autogen::{InstalledInventory, InstalledInventoryItem}; +use getter_operations::onboarding::{ + apply_setup_preview, preview_setup_with_transport, FdroidCatalogTransport, SetupAcceptance, + SetupCandidateCategory, SetupReadiness, +}; +use getter_storage::{CacheDb, MainDb}; +use std::cell::Cell; + +const INDEX_XML: &str = r#" + + + + F-Droid App + 1.2.312fdroid.apkaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa123 + +"#; + +struct MockTransport { + calls: Cell, + result: Result, +} + +impl FdroidCatalogTransport for MockTransport { + fn fetch_official_index_xml(&self) -> Result { + self.calls.set(self.calls.get() + 1); + self.result.clone() + } +} + +fn inventory() -> InstalledInventory { + InstalledInventory::new(vec![ + InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.app".into(), + version_name: Some("1.0".into()), + version_code: Some(10), + label: Some("F-Droid App".into()), + }, + InstalledInventoryItem::AndroidPackage { + package_name: "com.example.fallback".into(), + version_name: Some("2.0".into()), + version_code: Some(20), + label: Some("Fallback".into()), + }, + ]) +} + +#[test] +fn preview_refreshes_once_classifies_fdroid_first_and_deduplicates_fallback() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + + assert_eq!(transport.calls.get(), 1); + assert_eq!(preview.candidates.len(), 2); + assert_eq!( + preview.candidates[0].package_id, + "android/app/com.example.fallback" + ); + assert_eq!( + preview.candidates[0].category, + SetupCandidateCategory::InstalledFallback + ); + assert_eq!( + preview.candidates[1].package_id, + "android/f-droid/app/org.fdroid.app" + ); + assert_eq!( + preview.candidates[1].category, + SetupCandidateCategory::Fdroid + ); + assert!(!preview + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "setup.fdroid_unavailable")); +} + +#[test] +fn refresh_failure_without_cache_keeps_fallback_and_stable_diagnostic() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Err("offline".into()), + }; + + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + + assert_eq!(preview.candidates.len(), 2); + assert!(preview + .candidates + .iter() + .all(|candidate| candidate.category == SetupCandidateCategory::InstalledFallback)); + assert!(preview + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "setup.fdroid_unavailable")); +} + +#[test] +fn refresh_failure_with_cache_uses_stale_fdroid_match_and_excludes_fallback_duplicate() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let warm = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &warm).unwrap(); + + let unavailable = MockTransport { + calls: Cell::new(0), + result: Err("offline".into()), + }; + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &unavailable) + .unwrap(); + + assert_eq!(unavailable.calls.get(), 1); + assert_eq!( + preview + .candidates + .iter() + .filter(|candidate| candidate.package_id == "android/f-droid/app/org.fdroid.app") + .count(), + 1 + ); + assert!(!preview.candidates.iter().any(|candidate| { + candidate.package_id == "android/app/org.fdroid.app" + && candidate.category == SetupCandidateCategory::InstalledFallback + })); + assert!(preview + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "cache.refresh_failed")); + assert!(preview + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "used_stale_cache")); +} + +#[test] +fn covered_fdroid_app_is_not_reintroduced_as_installed_fallback() { + let temp = tempfile::tempdir().unwrap(); + let official = temp.path().join("repo/official"); + let covered = official.join("android/f-droid/app/org.fdroid.app"); + std::fs::create_dir_all(&covered).unwrap(); + std::fs::write( + covered.join("metadata.jsonc"), + r#"{"type":"android:app","android":{"package_name":"org.fdroid.app"}}"#, + ) + .unwrap(); + std::fs::write(covered.join("1.lua"), "return { version = '1' }").unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + assert!( + getter_core::repository::RepositoryPackageDirectoryLayout::load(&official) + .unwrap() + .package(&"android/f-droid/app/org.fdroid.app".parse().unwrap()) + .is_some() + ); + main.upsert_repository( + &getter_core::repository::RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".into(), + priority: getter_core::RepositoryPriority::new(1), + api_version: "1".into(), + }, + Some(&official), + None, + ) + .unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + assert!( + !preview + .candidates + .iter() + .any(|candidate| candidate.package_id.ends_with("org.fdroid.app")), + "{:?}", + preview.candidates + ); +} + +#[test] +fn apply_rejects_tampered_or_unknown_selection_before_writes() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + + let error = apply_setup_preview( + temp.path(), + &main, + &preview, + &SetupAcceptance::Accept(vec!["android/unknown".parse().unwrap()]), + ) + .unwrap_err(); + assert!(error.to_string().contains("unknown setup candidate")); + assert!(!temp.path().join("repo/autogen/android").exists()); + + let mut tampered = preview.clone(); + tampered.candidates.clear(); + apply_setup_preview(temp.path(), &main, &tampered, &SetupAcceptance::AcceptAll).unwrap(); + assert!(temp.path().join("repo/autogen/android").exists()); + + let error = + apply_setup_preview(temp.path(), &main, &preview, &SetupAcceptance::AcceptAll).unwrap_err(); + assert!(error + .to_string() + .contains("unknown, consumed, or already applying")); +} + +#[test] +fn apply_rejects_preview_after_tracked_state_changes_without_writes() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + main.upsert_tracked_package(&getter_storage::TrackedPackageUpsert { + package_id: "android/app/other".parse().unwrap(), + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: getter_storage::StoredPackageResolution::MissingPackageDefinition, + }) + .unwrap(); + + let error = + apply_setup_preview(temp.path(), &main, &preview, &SetupAcceptance::AcceptAll).unwrap_err(); + assert!(error.to_string().contains("setup preview is stale")); + assert!(!temp.path().join("repo/autogen/android").exists()); +} + +#[test] +fn apply_delegates_both_categories_and_returns_ready() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + + let result = + apply_setup_preview(temp.path(), &main, &preview, &SetupAcceptance::AcceptAll).unwrap(); + + assert_eq!(result.readiness, SetupReadiness::Ready); + assert_eq!( + result.applied_package_ids, + vec![ + "android/app/com.example.fallback", + "android/f-droid/app/org.fdroid.app" + ] + ); + assert!(temp + .path() + .join("repo/autogen/android/app/com.example.fallback/.autogen.jsonc") + .is_file()); + assert!(temp + .path() + .join("repo/autogen/android/f-droid/app/org.fdroid.app/.autogen.jsonc") + .is_file()); +} diff --git a/crates/getter-operations/tests/startup.rs b/crates/getter-operations/tests/startup.rs index 4c19f93..c5f5843 100644 --- a/crates/getter-operations/tests/startup.rs +++ b/crates/getter-operations/tests/startup.rs @@ -3,6 +3,7 @@ use getter_core::autogen::{InstalledInventory, InstalledInventoryItem}; use getter_core::repository::RepositoryMetadata; use getter_core::{RepositoryId, RepositoryPriority}; +use getter_operations::onboarding::SetupReadiness; use getter_operations::startup::{startup, UpdateStatus}; use getter_storage::{ CacheDb, MainDb, ProviderResponseUpsert, StoredPackageResolution, TrackedPackageUpsert, @@ -10,6 +11,37 @@ use getter_storage::{ use serde_json::json; use std::fs; +#[test] +fn startup_derives_setup_readiness_from_current_inventory_and_tracked_state() { + let temp = tempfile::tempdir().unwrap(); + let actionable = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "com.example.app".into(), + version_name: Some("1.0".into()), + version_code: Some(1), + label: Some("Example".into()), + }]); + + let fresh = startup(temp.path(), actionable.clone()).unwrap(); + assert_eq!(fresh.setup.state, SetupReadiness::NeedsPackageSetup); + + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/tracked".parse().unwrap(), + repository_id: None, + enabled: true, + favorite: false, + pin_version: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + let tracked = startup(temp.path(), actionable).unwrap(); + assert_eq!(tracked.setup.state, SetupReadiness::Ready); + + let empty = tempfile::tempdir().unwrap(); + let no_candidates = startup(empty.path(), InstalledInventory::new(vec![])).unwrap(); + assert_eq!(no_candidates.setup.state, SetupReadiness::Ready); +} + #[test] fn startup_bootstraps_idempotently_and_joins_static_updates() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index ebe139e..cbb180d 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -14,9 +14,10 @@ use serde_json::Value; use std::path::Path; use std::str::FromStr; -pub const MAIN_STORAGE_CONTRACT_VERSION: u32 = 1; +pub const MAIN_STORAGE_CONTRACT_VERSION: u32 = 2; pub const CACHE_STORAGE_CONTRACT_VERSION: u32 = 1; -pub const MAIN_APPLIED_MIGRATION_IDS: &[&str] = &["main-v1", "main-task-v1"]; +pub const MAIN_APPLIED_MIGRATION_IDS: &[&str] = + &["main-v1", "main-task-v1", "main-autogen-transaction-v1"]; pub const CACHE_APPLIED_MIGRATION_IDS: &[&str] = &["cache-v1", "cache-provider-provenance-v1"]; #[derive(Debug, thiserror::Error)] @@ -112,6 +113,11 @@ CREATE TABLE IF NOT EXISTS migration_records ( report_json TEXT NOT NULL DEFAULT '{}' ); +CREATE TABLE IF NOT EXISTS autogen_transaction_commits ( + transaction_id TEXT PRIMARY KEY, + committed_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + CREATE TABLE IF NOT EXISTS download_tasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT UNIQUE, @@ -237,6 +243,91 @@ ON CONFLICT(id) DO UPDATE SET Ok(()) } + pub fn apply_generated_repository_batch_with_marker( + &self, + metadata: &RepositoryMetadata, + path: &Path, + package_ids: &[PackageId], + transaction_id: Option<&str>, + ) -> Result<(), StorageError> { + let transaction = self.conn.unchecked_transaction()?; + transaction.execute( + r#" +INSERT INTO repositories(id, name, priority, api_version, path, revision) +VALUES (?1, ?2, ?3, ?4, ?5, NULL) +ON CONFLICT(id) DO UPDATE SET + name = excluded.name, + priority = excluded.priority, + api_version = excluded.api_version, + path = excluded.path, + revision = excluded.revision +"#, + params![ + metadata.id.as_str(), + metadata.name, + metadata.priority.value(), + metadata.api_version, + path.to_string_lossy().to_string(), + ], + )?; + for package_id in package_ids { + transaction.execute( + r#" +INSERT INTO tracked_packages( + package_id, enabled, favorite, pin_version, repository_id, package_resolution +) +VALUES (?1, 1, 0, NULL, ?2, ?3) +ON CONFLICT(package_id) DO UPDATE SET + repository_id = CASE + WHEN tracked_packages.package_resolution = 'missing_package_definition' + THEN excluded.repository_id + ELSE tracked_packages.repository_id + END, + package_resolution = CASE + WHEN tracked_packages.package_resolution = 'missing_package_definition' + THEN excluded.package_resolution + ELSE tracked_packages.package_resolution + END +"#, + params![ + package_id.to_string(), + metadata.id.as_str(), + StoredPackageResolution::GenerateLocalPackage.as_str(), + ], + )?; + } + if let Some(transaction_id) = transaction_id { + transaction.execute( + "INSERT OR REPLACE INTO autogen_transaction_commits (transaction_id) VALUES (?1)", + [transaction_id], + )?; + } + transaction.commit()?; + Ok(()) + } + + pub fn has_autogen_transaction_commit( + &self, + transaction_id: &str, + ) -> Result { + Ok(self.conn.query_row( + "SELECT EXISTS(SELECT 1 FROM autogen_transaction_commits WHERE transaction_id = ?1)", + [transaction_id], + |row| row.get(0), + )?) + } + + pub fn delete_autogen_transaction_commit( + &self, + transaction_id: &str, + ) -> Result<(), StorageError> { + self.conn.execute( + "DELETE FROM autogen_transaction_commits WHERE transaction_id = ?1", + [transaction_id], + )?; + Ok(()) + } + pub fn repositories(&self) -> Result, StorageError> { let mut stmt = self.conn.prepare( "SELECT id, name, priority, api_version, path, revision FROM repositories ORDER BY priority DESC, id ASC", From d1b2c148fb1ca96c74ef2d8ada3491a70771c067 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Fri, 17 Jul 2026 20:54:09 +0800 Subject: [PATCH 71/72] feat(install): prepare Android APK handoffs --- crates/getter-cli/tests/app_cli.rs | 38 ++++ crates/getter-core/src/lib.rs | 81 ++++++++- crates/getter-core/src/lua.rs | 28 +++ crates/getter-core/src/luaclass/installer.lua | 5 + crates/getter-operations/src/app.rs | 162 +++++++++++++++++- crates/getter-operations/tests/installer.rs | 162 +++++++++++++++++- 6 files changed, 463 insertions(+), 13 deletions(-) diff --git a/crates/getter-cli/tests/app_cli.rs b/crates/getter-cli/tests/app_cli.rs index 9991497..f4d0a1a 100644 --- a/crates/getter-cli/tests/app_cli.rs +++ b/crates/getter-cli/tests/app_cli.rs @@ -170,6 +170,44 @@ fn app_install_rejects_product_command_controls() { assert_eq!(output.exit_code, ExitCode::Usage); } +#[test] +fn app_install_rejects_android_apk_platform_declaration_without_execution() { + let bytes = b"apk"; + let digest = "dd37c2d7274f7ea982cb83390c36918fee9ce8889073c44b68cdc00bdb8c3e04"; + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let server = std::thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut request = [0; 1024]; + let _ = stream.read(&mut request).unwrap(); + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + bytes.len() + ) + .unwrap(); + stream.write_all(bytes).unwrap(); + }); + let temp = fixture("2"); + let package = temp.path().join("repo/official/android/com.example"); + fs::write(package.join("Manifest"), format!("{digest} app.apk\n")).unwrap(); + fs::write(package.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{{{ version="2", artifacts={{{{name="app",file_name="app.apk",url="http://{address}/app.apk"}}}}, install={{kind="android_apk",artifact={{artifact="app"}}}} }}}} }}"#)).unwrap(); + + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "install", + "android/com.example", + ]); + server.join().unwrap(); + assert_eq!(output.exit_code, ExitCode::GenericFailure); + let value: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(value["error"]["code"], "installer.target_unsupported"); +} + #[test] fn app_install_executes_declared_command_and_reports_resolved_argv() { let bytes = b"install artifact"; diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index ee7d976..7f4b3f0 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -277,6 +277,12 @@ pub struct PackagePermissions { pub free_network: bool, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Installer { + AndroidApk(AndroidApkInstaller), + Command(InstallerCommand), +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct InstallerCommand { @@ -285,6 +291,11 @@ pub struct InstallerCommand { pub args: Vec, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AndroidApkInstaller { + pub artifact: InstallerArtifactReference, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum InstallerArg { @@ -313,7 +324,7 @@ impl InstallerDeclaration { &self.0 } - pub fn parse(&self) -> Result { + pub fn parse(&self) -> Result { let mut value = self.0.clone(); if let Some(args) = value .as_object_mut() @@ -323,7 +334,27 @@ impl InstallerDeclaration { *args = serde_json::Value::Array(Vec::new()); } } - serde_json::from_value(value) + if value.get("kind").is_some() { + #[derive(Deserialize)] + #[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] + enum TaggedInstaller { + AndroidApk { + artifact: InstallerArtifactReference, + }, + } + + return serde_json::from_value(value).and_then(|installer| match installer { + TaggedInstaller::AndroidApk { artifact } if artifact.artifact.trim().is_empty() => { + Err(::custom( + "android_apk artifact reference must not be empty", + )) + } + TaggedInstaller::AndroidApk { artifact } => { + Ok(Installer::AndroidApk(AndroidApkInstaller { artifact })) + } + }); + } + serde_json::from_value(value).map(Installer::Command) } } @@ -470,6 +501,52 @@ mod tests { assert_eq!(RepositoryPriority::GENERATED_FALLBACK.value(), -1); } + #[test] + fn parses_typed_installers_without_changing_legacy_command_shape() { + let android = InstallerDeclaration::new(serde_json::json!({ + "kind": "android_apk", + "artifact": { "artifact": "app.apk" } + })); + assert_eq!( + android.parse().unwrap(), + Installer::AndroidApk(AndroidApkInstaller { + artifact: InstallerArtifactReference { + artifact: "app.apk".into(), + }, + }) + ); + + let command = InstallerDeclaration::new(serde_json::json!({ + "executable": "installer", + "args": {} + })); + assert_eq!( + command.parse().unwrap(), + Installer::Command(InstallerCommand { + executable: "installer".into(), + args: Vec::new(), + }) + ); + } + + #[test] + fn rejects_incomplete_or_extended_android_apk_installers() { + for value in [ + serde_json::json!({"kind": "android_apk"}), + serde_json::json!({ + "kind": "android_apk", + "artifact": {"artifact": "app.apk"}, + "surprise": true + }), + serde_json::json!({ + "kind": "android_apk", + "artifact": {"artifact": "app.apk", "surprise": true} + }), + ] { + assert!(InstallerDeclaration::new(value).parse().is_err()); + } + } + #[test] fn repository_id_accepts_named_repositories() { assert_eq!(RepositoryId::new("autogen").unwrap().as_str(), "autogen"); diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index bb54f43..4d54955 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -1016,6 +1016,34 @@ mod tests { fs::write(package_dir.join("9999.lua"), script_source).unwrap(); } + #[test] + fn android_apk_helper_emits_exact_tagged_artifact_boundary() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local installer = require("luaclass.installer") +return package_version { + updates = {{ + version = "2", + artifacts = {{ name = "app.apk", url = "https://example.invalid/app.apk" }}, + install = installer.android_apk { + artifact = installer.artifact("app.apk"), + }, + }}, +}"#, + ); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + assert_eq!( + package.updates[0].install.as_ref().unwrap().as_json(), + &serde_json::json!({ + "kind": "android_apk", + "artifact": {"artifact": "app.apk"} + }) + ); + } + #[test] fn preserves_chosen_installer_table_declaration_without_eager_validation() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/luaclass/installer.lua b/crates/getter-core/src/luaclass/installer.lua index 7f3aff9..0dba5f1 100644 --- a/crates/getter-core/src/luaclass/installer.lua +++ b/crates/getter-core/src/luaclass/installer.lua @@ -8,4 +8,9 @@ function installer.command(spec) return spec end +function installer.android_apk(spec) + spec.kind = "android_apk" + return spec +end + return installer diff --git a/crates/getter-operations/src/app.rs b/crates/getter-operations/src/app.rs index 0a18e60..c8000fb 100644 --- a/crates/getter-operations/src/app.rs +++ b/crates/getter-operations/src/app.rs @@ -15,7 +15,7 @@ use getter_core::runtime::IssuedAction; use getter_core::update::compare_versions; use getter_core::PackageId; #[cfg(feature = "lua")] -use getter_core::{InstallerArg, InstallerCommand, UpdateArtifact}; +use getter_core::{InstalledTarget, Installer, InstallerArg, InstallerCommand, UpdateArtifact}; use getter_storage::{MainDb, StorageError, StoredTrackedPackage}; use serde::{Deserialize, Serialize}; #[cfg(feature = "lua")] @@ -88,6 +88,12 @@ pub enum AppOperationError { #[error("installer artifact name '{0}' is duplicated")] InstallerArtifactDuplicate(String), #[cfg(feature = "lua")] + #[error("installer target is unsupported")] + InstallerTargetUnsupported, + #[cfg(feature = "lua")] + #[error("installer artifact '{0}' is unsupported")] + InstallerArtifactUnsupported(String), + #[cfg(feature = "lua")] #[error("installer executable '{0}' was not found")] InstallerCommandNotFound(String), #[cfg(feature = "lua")] @@ -144,6 +150,10 @@ impl AppOperationError { #[cfg(feature = "lua")] Self::InstallerArtifactDuplicate(_) => "installer.artifact_duplicate", #[cfg(feature = "lua")] + Self::InstallerTargetUnsupported => "installer.target_unsupported", + #[cfg(feature = "lua")] + Self::InstallerArtifactUnsupported(_) => "installer.artifact_unsupported", + #[cfg(feature = "lua")] Self::InstallerCommandNotFound(_) => "installer.command_not_found", #[cfg(feature = "lua")] Self::InstallerCommandSpawnFailed(_) => "installer.command_spawn_failed", @@ -190,6 +200,10 @@ impl AppOperationError { #[cfg(feature = "lua")] Self::InstallerArtifactDuplicate(_) => "Getter installer artifact name is duplicated", #[cfg(feature = "lua")] + Self::InstallerTargetUnsupported => "Getter installer target is unsupported", + #[cfg(feature = "lua")] + Self::InstallerArtifactUnsupported(_) => "Getter installer artifact is unsupported", + #[cfg(feature = "lua")] Self::InstallerCommandNotFound(_) => "Getter installer command was not found", #[cfg(feature = "lua")] Self::InstallerCommandSpawnFailed(_) => "Getter installer command could not be started", @@ -222,6 +236,8 @@ impl AppOperationError { | Self::InstallerSchema(_) | Self::InstallerArtifactUnknown(_) | Self::InstallerArtifactDuplicate(_) + | Self::InstallerTargetUnsupported + | Self::InstallerArtifactUnsupported(_) | Self::InstallerCommandNotFound(_) | Self::InstallerCommandSpawnFailed(_) | Self::InstallerCommandFailed(_) => self.to_string(), @@ -245,6 +261,39 @@ pub struct AppDownloadResult { pub artifacts: Vec, } +#[cfg(feature = "lua")] +pub const PLATFORM_INSTALL_HANDOFF_FORMAT: &str = "getter-platform-install-handoff"; +#[cfg(feature = "lua")] +pub const PLATFORM_INSTALL_HANDOFF_VERSION: u32 = 1; + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PlatformInstallHandoff { + pub format: String, + pub version: u32, + pub package_id: PackageId, + pub repository_id: String, + pub package_version: String, + pub request: PlatformInstallRequest, +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum PlatformInstallRequest { + AndroidApk { + target: AndroidInstallTarget, + artifact: StagedArtifact, + }, +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AndroidInstallTarget { + pub kind: String, + pub package_name: String, +} + #[cfg(feature = "lua")] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ResolvedInstallerCommand { @@ -442,7 +491,7 @@ pub fn download_app_with_transports( github_release_transport, download_transport, ) - .map(|(result, _)| result) + .map(|(result, _, _)| result) } #[cfg(feature = "lua")] @@ -451,7 +500,14 @@ fn prepare_app_with_transports( package_id: &PackageId, github_release_transport: Option>, download_transport: Rc, -) -> Result<(AppDownloadResult, getter_core::UpdateCandidate), AppOperationError> { +) -> Result< + ( + AppDownloadResult, + getter_core::UpdateCandidate, + Vec, + ), + AppOperationError, +> { startup::bootstrap_data_dir(data_dir)?; let db = MainDb::open(data_dir.join("main.db"))?; let tracked = tracked_package(&db, package_id)?; @@ -514,6 +570,7 @@ fn prepare_app_with_transports( fs::create_dir_all(&downloads)?; } + let installed_targets = evaluation.package.installed.clone(); let artifacts = prepared .into_iter() .map(|artifact| stage_artifact(artifact, download_transport.as_ref())) @@ -526,9 +583,86 @@ fn prepare_app_with_transports( artifacts, }, candidate, + installed_targets, )) } +#[cfg(feature = "lua")] +pub fn prepare_platform_install( + data_dir: &Path, + package_id: &PackageId, +) -> Result { + prepare_platform_install_with_transports( + data_dir, + package_id, + Some(Rc::new( + crate::github_releases::UreqGithubReleaseTransport::new(), + )), + Rc::new(crate::download::UreqRuntimeDownloadTransport::new()), + ) +} + +#[cfg(feature = "lua")] +pub fn prepare_platform_install_with_transports( + data_dir: &Path, + package_id: &PackageId, + github_release_transport: Option>, + download_transport: Rc, +) -> Result { + let (download, candidate, installed_targets) = prepare_app_with_transports( + data_dir, + package_id, + github_release_transport, + download_transport, + )?; + let installer = candidate + .install + .ok_or(AppOperationError::InstallerMissing)? + .parse() + .map_err(|error| AppOperationError::InstallerSchema(error.to_string()))?; + let Installer::AndroidApk(installer) = installer else { + return Err(AppOperationError::InstallerTargetUnsupported); + }; + let [InstalledTarget::AndroidPackage { package_name }] = installed_targets.as_slice() else { + return Err(AppOperationError::InstallerTargetUnsupported); + }; + if package_name.trim().is_empty() { + return Err(AppOperationError::InstallerTargetUnsupported); + } + let declared_artifact = candidate + .artifacts + .iter() + .find(|artifact| artifact.name == installer.artifact.artifact) + .ok_or_else(|| { + AppOperationError::InstallerArtifactUnknown(installer.artifact.artifact.clone()) + })?; + let declared_file_name = declared_artifact + .file_name + .as_deref() + .unwrap_or(&declared_artifact.name); + if !declared_file_name.to_ascii_lowercase().ends_with(".apk") { + return Err(AppOperationError::InstallerArtifactUnsupported( + installer.artifact.artifact, + )); + } + let artifact = unique_staged_artifact(&download.artifacts, &declared_artifact.name)?; + + Ok(PlatformInstallHandoff { + format: PLATFORM_INSTALL_HANDOFF_FORMAT.into(), + version: PLATFORM_INSTALL_HANDOFF_VERSION, + package_id: download.package_id, + repository_id: download.repository_id, + package_version: download.version, + request: PlatformInstallRequest::AndroidApk { + target: AndroidInstallTarget { + kind: "android".into(), + package_name: package_name.clone(), + }, + artifact: artifact.clone(), + }, + }) +} + #[cfg(feature = "lua")] pub fn install_app( data_dir: &Path, @@ -586,7 +720,7 @@ pub fn install_app_with_dependencies_and_observer( runner: &dyn CommandRunner, observer: &dyn CommandObserver, ) -> Result { - let (download, candidate) = prepare_app_with_transports( + let (download, candidate, _) = prepare_app_with_transports( data_dir, package_id, github_release_transport, @@ -597,6 +731,9 @@ pub fn install_app_with_dependencies_and_observer( .ok_or(AppOperationError::InstallerMissing)? .parse() .map_err(|error| AppOperationError::InstallerSchema(error.to_string()))?; + let Installer::Command(installer) = installer else { + return Err(AppOperationError::InstallerTargetUnsupported); + }; validate_installer_command(&installer)?; let command = resolve_installer(&installer, &download.artifacts, resolver)?; observer @@ -623,6 +760,23 @@ pub fn install_app_with_dependencies_and_observer( }) } +#[cfg(feature = "lua")] +fn unique_staged_artifact<'a>( + artifacts: &'a [StagedArtifact], + name: &str, +) -> Result<&'a StagedArtifact, AppOperationError> { + let mut matches = artifacts.iter().filter(|artifact| artifact.name == name); + let artifact = matches + .next() + .ok_or_else(|| AppOperationError::InstallerArtifactUnknown(name.to_owned()))?; + if matches.next().is_some() { + return Err(AppOperationError::InstallerArtifactDuplicate( + name.to_owned(), + )); + } + Ok(artifact) +} + #[cfg(feature = "lua")] fn resolve_installer( installer: &InstallerCommand, diff --git a/crates/getter-operations/tests/installer.rs b/crates/getter-operations/tests/installer.rs index 7a48659..32c173b 100644 --- a/crates/getter-operations/tests/installer.rs +++ b/crates/getter-operations/tests/installer.rs @@ -4,7 +4,8 @@ use getter_core::repository::RepositoryMetadata; use getter_core::{RepositoryId, RepositoryPriority}; use getter_operations::app::{ download_app_with_transports, install_app_with_dependencies, - install_app_with_dependencies_and_observer, CommandObserver, CommandResolver, CommandRunner, + install_app_with_dependencies_and_observer, prepare_platform_install_with_transports, + CommandObserver, CommandResolver, CommandRunner, PlatformInstallRequest, ResolvedInstallerCommand, RunnerOutput, }; use getter_operations::download::{ @@ -79,16 +80,12 @@ fn fake_installer(temp: &Path) -> PathBuf { }"#, ) } -fn fixture(install: &str, bytes: &[u8]) -> tempfile::TempDir { +fn fixture_with_metadata(install: &str, bytes: &[u8], metadata: &str) -> tempfile::TempDir { let temp = tempfile::tempdir().unwrap(); getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); let package = temp.path().join("repo/official/android/com.example"); fs::create_dir_all(&package).unwrap(); - fs::write( - package.join("metadata.jsonc"), - r#"{"type":"android:app","android":{"package_name":"com.example"}}"#, - ) - .unwrap(); + fs::write(package.join("metadata.jsonc"), metadata).unwrap(); fs::write( package.join("Manifest"), format!("{} app.apk\n", digest(bytes)), @@ -120,6 +117,157 @@ return package_version {{ updates = {{{{ version="2", artifacts={{{{name="app",f temp } +fn fixture(install: &str, bytes: &[u8]) -> tempfile::TempDir { + fixture_with_metadata( + install, + bytes, + r#"{"type":"android:app","android":{"package_name":"com.example"}}"#, + ) +} + +#[test] +fn prepares_android_apk_handoff_from_verified_staging() { + let bytes = b"apk"; + let temp = fixture(r#"{kind="android_apk",artifact={artifact="app"}}"#, bytes); + + let handoff = prepare_platform_install_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + ) + .unwrap(); + + assert_eq!(handoff.format, "getter-platform-install-handoff"); + assert_eq!(handoff.version, 1); + assert_eq!(handoff.package_id.to_string(), "android/com.example"); + assert_eq!(handoff.repository_id.as_str(), "official"); + assert_eq!(handoff.package_version, "2"); + let artifact_path = match &handoff.request { + PlatformInstallRequest::AndroidApk { artifact, .. } => artifact.path.clone(), + }; + assert_eq!( + serde_json::to_value(&handoff).unwrap(), + serde_json::json!({ + "format": "getter-platform-install-handoff", + "version": 1, + "package_id": "android/com.example", + "repository_id": "official", + "package_version": "2", + "request": { + "kind": "android_apk", + "target": { + "kind": "android", + "package_name": "com.example" + }, + "artifact": { + "name": "app", + "path": artifact_path, + "sha256": digest(bytes), + "status": "downloaded" + } + } + }) + ); + let PlatformInstallRequest::AndroidApk { target, artifact } = handoff.request; + assert_eq!(target.kind, "android"); + assert_eq!(target.package_name, "com.example"); + assert_eq!(artifact.name, "app"); + assert!(artifact.path.is_absolute()); + assert_eq!(artifact.sha256, digest(bytes)); + assert_eq!(artifact.status, "downloaded"); +} + +#[test] +fn platform_prepare_rejects_command_unknown_non_apk_and_malformed_declarations_stably() { + let cases = [ + ( + r#"{executable="fake-installer",args={}}"#, + "installer.target_unsupported", + ), + ( + r#"{kind="android_apk",artifact={artifact="missing"}}"#, + "installer.artifact_unknown", + ), + ( + r#"{kind="android_apk",artifact={artifact="app"},surprise=true}"#, + "installer.schema_invalid", + ), + ]; + for (install, expected_code) in cases { + let bytes = b"apk"; + let temp = fixture(install, bytes); + let error = prepare_platform_install_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + ) + .unwrap_err(); + assert_eq!(error.code(), expected_code); + } + + let bytes = b"not apk"; + let temp = fixture(r#"{kind="android_apk",artifact={artifact="app"}}"#, bytes); + let package = temp.path().join("repo/official/android/com.example"); + fs::write( + package.join("Manifest"), + format!("{} payload.zip\n", digest(bytes)), + ) + .unwrap(); + fs::write(package.join("1.lua"), r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", artifacts={{name="app",file_name="payload.zip",url="mock://app"}}, install={kind="android_apk",artifact={artifact="app"}} }} }"#).unwrap(); + let error = prepare_platform_install_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.artifact_unsupported"); +} + +#[test] +fn platform_prepare_requires_exactly_one_nonempty_android_package_target() { + let bytes = b"apk"; + for metadata in [ + r#"{"type":"generic","generic":{"id":"com.example"}}"#, + r#"{"type":"android:app","android":{"package_name":""}}"#, + ] { + let temp = fixture_with_metadata( + r#"{kind="android_apk",artifact={artifact="app"}}"#, + bytes, + metadata, + ); + let error = prepare_platform_install_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.target_unsupported"); + } +} + +#[test] +fn direct_command_install_rejects_android_apk_without_resolving_or_running() { + let bytes = b"apk"; + let temp = fixture(r#"{kind="android_apk",artifact={artifact="app"}}"#, bytes); + let runner = Runner::default(); + let error = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &runner, + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.target_unsupported"); + assert!(runner.seen.borrow().is_none()); +} + #[test] fn malformed_installer_does_not_block_download_but_fails_install_after_staging() { let bytes = b"apk"; From e7cc5cee871c86ac655fc248f22661da5416dfe5 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 1 Sep 2026 01:47:44 +0800 Subject: [PATCH 72/72] feat(runtime): seal Android APK install handoffs Bind platform preparation to the exact runtime task, sealed candidate, and verified downloaded artifact while preserving installer.command compatibility. Return authoritative autogen apply paths for the existing bridge contract. --- crates/getter-core/src/runtime.rs | 28 ++ crates/getter-operations/src/app.rs | 278 ++++++++++++++++++-- crates/getter-operations/src/autogen.rs | 20 +- crates/getter-operations/src/download.rs | 3 +- crates/getter-operations/src/runtime.rs | 10 + crates/getter-operations/tests/installer.rs | 144 +++++++++- 6 files changed, 453 insertions(+), 30 deletions(-) diff --git a/crates/getter-core/src/runtime.rs b/crates/getter-core/src/runtime.rs index 6736506..c3610c7 100644 --- a/crates/getter-core/src/runtime.rs +++ b/crates/getter-core/src/runtime.rs @@ -16,11 +16,23 @@ pub struct PackageVersionLuaObject { pub dependency_digest: String, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SealedAndroidApkInstallPlan { + pub repository_id: String, + pub package_version: String, + pub package_name: String, + pub artifact_name: String, + pub artifact_file_name: String, + pub artifact_sha256: String, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SealedActionPlan { pub package_id: PackageId, #[serde(default)] pub actions: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub android_apk_install: Option, pub lua_object: PackageVersionLuaObject, } @@ -326,6 +338,13 @@ impl GetterRuntime { Ok(self.task_ref(task_id)?.plan.download_action()) } + pub fn android_apk_install_plan( + &self, + task_id: &str, + ) -> Result, RuntimeError> { + Ok(self.task_ref(task_id)?.plan.android_apk_install.clone()) + } + pub fn start_task(&mut self, task_id: &str) -> Result { { let clock = self.tick(); @@ -1027,6 +1046,14 @@ mod tests { file: "app.apk".to_owned(), }, ], + android_apk_install: Some(SealedAndroidApkInstallPlan { + repository_id: "official".to_owned(), + package_version: "1.0.0".to_owned(), + package_name: "org.fdroid.fdroid".to_owned(), + artifact_name: "app.apk".to_owned(), + artifact_file_name: "app.apk".to_owned(), + artifact_sha256: "a".repeat(64), + }), } } @@ -1042,6 +1069,7 @@ mod tests { url: "https://example.invalid/file.bin".to_owned(), file_name: "file.bin".to_owned(), }], + android_apk_install: None, } } } diff --git a/crates/getter-operations/src/app.rs b/crates/getter-operations/src/app.rs index c8000fb..dfe571e 100644 --- a/crates/getter-operations/src/app.rs +++ b/crates/getter-operations/src/app.rs @@ -8,14 +8,20 @@ use getter_core::autogen::{validate_installed_inventory, InstalledInventory}; use getter_core::manifest::{ManifestError, PackageManifest}; #[cfg(feature = "lua")] use getter_core::repository::PACKAGE_MANIFEST_FILE; -#[cfg(feature = "lua")] -use getter_core::runtime::GetterRuntime; use getter_core::runtime::IssuedAction; #[cfg(feature = "lua")] +use getter_core::runtime::{ + GetterRuntime, RuntimeError, RuntimeTaskStatus, SealedAndroidApkInstallPlan, TaskPhase, + TaskPhaseCategory, TaskPhaseReason, +}; +#[cfg(feature = "lua")] use getter_core::update::compare_versions; use getter_core::PackageId; #[cfg(feature = "lua")] -use getter_core::{InstalledTarget, Installer, InstallerArg, InstallerCommand, UpdateArtifact}; +use getter_core::{ + InstalledTarget, Installer, InstallerArg, InstallerCommand, ResolvedPackage, SelectedUpdate, + UpdateArtifact, +}; use getter_storage::{MainDb, StorageError, StoredTrackedPackage}; use serde::{Deserialize, Serialize}; #[cfg(feature = "lua")] @@ -94,6 +100,17 @@ pub enum AppOperationError { #[error("installer artifact '{0}' is unsupported")] InstallerArtifactUnsupported(String), #[cfg(feature = "lua")] + #[error("runtime task '{task_id}' has no sealed Android APK install plan")] + TaskInstallPlanMissing { task_id: String }, + #[cfg(feature = "lua")] + #[error("runtime task '{task_id}' has no staged install artifact")] + TaskInstallArtifactMissing { task_id: String }, + #[cfg(feature = "lua")] + #[error( + "runtime task '{task_id}' staged artifact does not match its sealed install plan: {detail}" + )] + TaskInstallArtifactMismatch { task_id: String, detail: String }, + #[cfg(feature = "lua")] #[error("installer executable '{0}' was not found")] InstallerCommandNotFound(String), #[cfg(feature = "lua")] @@ -154,6 +171,12 @@ impl AppOperationError { #[cfg(feature = "lua")] Self::InstallerArtifactUnsupported(_) => "installer.artifact_unsupported", #[cfg(feature = "lua")] + Self::TaskInstallPlanMissing { .. } => "installer.task_plan_missing", + #[cfg(feature = "lua")] + Self::TaskInstallArtifactMissing { .. } => "installer.task_artifact_missing", + #[cfg(feature = "lua")] + Self::TaskInstallArtifactMismatch { .. } => "installer.task_artifact_mismatch", + #[cfg(feature = "lua")] Self::InstallerCommandNotFound(_) => "installer.command_not_found", #[cfg(feature = "lua")] Self::InstallerCommandSpawnFailed(_) => "installer.command_spawn_failed", @@ -204,6 +227,18 @@ impl AppOperationError { #[cfg(feature = "lua")] Self::InstallerArtifactUnsupported(_) => "Getter installer artifact is unsupported", #[cfg(feature = "lua")] + Self::TaskInstallPlanMissing { .. } => { + "Getter runtime task has no sealed Android install plan" + } + #[cfg(feature = "lua")] + Self::TaskInstallArtifactMissing { .. } => { + "Getter runtime task has no staged install artifact" + } + #[cfg(feature = "lua")] + Self::TaskInstallArtifactMismatch { .. } => { + "Getter runtime task artifact does not match its sealed install plan" + } + #[cfg(feature = "lua")] Self::InstallerCommandNotFound(_) => "Getter installer command was not found", #[cfg(feature = "lua")] Self::InstallerCommandSpawnFailed(_) => "Getter installer command could not be started", @@ -238,6 +273,9 @@ impl AppOperationError { | Self::InstallerArtifactDuplicate(_) | Self::InstallerTargetUnsupported | Self::InstallerArtifactUnsupported(_) + | Self::TaskInstallPlanMissing { .. } + | Self::TaskInstallArtifactMissing { .. } + | Self::TaskInstallArtifactMismatch { .. } | Self::InstallerCommandNotFound(_) | Self::InstallerCommandSpawnFailed(_) | Self::InstallerCommandFailed(_) => self.to_string(), @@ -271,6 +309,8 @@ pub const PLATFORM_INSTALL_HANDOFF_VERSION: u32 = 1; pub struct PlatformInstallHandoff { pub format: String, pub version: u32, + #[serde(skip_serializing_if = "Option::is_none")] + pub task_id: Option, pub package_id: PackageId, pub repository_id: String, pub package_version: String, @@ -587,6 +627,178 @@ fn prepare_app_with_transports( )) } +#[cfg(feature = "lua")] +pub(crate) fn seal_android_apk_install_plan( + package_path: &Path, + package: &ResolvedPackage, + selected: Option<&SelectedUpdate>, +) -> Result, AppOperationError> { + let Some(selected) = selected else { + return Ok(None); + }; + let Some(declaration) = selected.candidate.install.clone() else { + return Ok(None); + }; + let installer = declaration + .parse() + .map_err(|error| AppOperationError::InstallerSchema(error.to_string()))?; + let Installer::AndroidApk(installer) = installer else { + return Ok(None); + }; + let [InstalledTarget::AndroidPackage { package_name }] = package.installed.as_slice() else { + return Err(AppOperationError::InstallerTargetUnsupported); + }; + if package_name.trim().is_empty() { + return Err(AppOperationError::InstallerTargetUnsupported); + } + let declared_artifact = selected + .candidate + .artifacts + .iter() + .find(|artifact| artifact.name == installer.artifact.artifact) + .ok_or_else(|| { + AppOperationError::InstallerArtifactUnknown(installer.artifact.artifact.clone()) + })?; + if selected + .artifact + .as_ref() + .map(|artifact| artifact.name.as_str()) + != Some(declared_artifact.name.as_str()) + { + return Err(AppOperationError::InstallerArtifactUnsupported( + declared_artifact.name.clone(), + )); + } + let declared_file_name = declared_artifact + .file_name + .as_deref() + .unwrap_or(declared_artifact.name.as_str()); + if !declared_file_name.to_ascii_lowercase().ends_with(".apk") { + return Err(AppOperationError::InstallerArtifactUnsupported( + declared_artifact.name.clone(), + )); + } + let manifest_content = + fs::read_to_string(package_path.join(PACKAGE_MANIFEST_FILE)).map_err(|error| { + if error.kind() == std::io::ErrorKind::NotFound { + AppOperationError::ManifestMissing + } else { + AppOperationError::ArtifactIo(error) + } + })?; + let manifest = + PackageManifest::parse(&manifest_content).map_err(AppOperationError::ManifestInvalid)?; + let artifact_sha256 = manifest_artifact_digest(&manifest, declared_artifact)?; + + Ok(Some(SealedAndroidApkInstallPlan { + repository_id: package.repository.to_string(), + package_version: selected.candidate.version.clone(), + package_name: package_name.clone(), + artifact_name: declared_artifact.name.clone(), + artifact_file_name: crate::download::safe_download_file_name(declared_file_name), + artifact_sha256, + })) +} + +#[cfg(feature = "lua")] +pub fn prepare_platform_install_for_task( + runtime: &GetterRuntime, + data_dir: &Path, + task_id: &str, +) -> Result { + let task = runtime + .task(task_id) + .map_err(crate::runtime::RuntimeOperationError::from)?; + if task.status != RuntimeTaskStatus::Running + || task.phase + != TaskPhase::with_reason( + TaskPhaseCategory::WaitingUser, + TaskPhaseReason::InstallHandoff, + ) + { + return Err(crate::runtime::RuntimeOperationError::from( + RuntimeError::TaskNotWaitingForUser(task_id.to_owned()), + ) + .into()); + } + let plan = runtime + .android_apk_install_plan(task_id) + .map_err(crate::runtime::RuntimeOperationError::from)? + .ok_or_else(|| AppOperationError::TaskInstallPlanMissing { + task_id: task_id.to_owned(), + })?; + let downloaded = + task.downloaded_file + .ok_or_else(|| AppOperationError::TaskInstallArtifactMissing { + task_id: task_id.to_owned(), + })?; + let expected_path = + data_dir + .join("downloads") + .join(task_id) + .join(crate::download::safe_download_file_name( + &plan.artifact_file_name, + )); + let actual_path = PathBuf::from(&downloaded.local_path); + if downloaded.file_name != plan.artifact_file_name || actual_path != expected_path { + return Err(AppOperationError::TaskInstallArtifactMismatch { + task_id: task_id.to_owned(), + detail: "file name or Getter-owned task path changed".to_owned(), + }); + } + if downloaded.sha256 != plan.artifact_sha256 { + return Err(AppOperationError::Sha256Mismatch { + artifact: plan.artifact_name, + expected: plan.artifact_sha256, + actual: downloaded.sha256, + }); + } + let metadata = fs::metadata(&actual_path).map_err(|error| { + if error.kind() == std::io::ErrorKind::NotFound { + AppOperationError::TaskInstallArtifactMissing { + task_id: task_id.to_owned(), + } + } else { + AppOperationError::ArtifactIo(error) + } + })?; + if metadata.len() != downloaded.size_bytes { + return Err(AppOperationError::TaskInstallArtifactMismatch { + task_id: task_id.to_owned(), + detail: "file size changed after Getter staging".to_owned(), + }); + } + let current_sha256 = sha256_file(&actual_path)?; + if current_sha256 != plan.artifact_sha256 { + return Err(AppOperationError::Sha256Mismatch { + artifact: plan.artifact_name, + expected: plan.artifact_sha256, + actual: current_sha256, + }); + } + + Ok(PlatformInstallHandoff { + format: PLATFORM_INSTALL_HANDOFF_FORMAT.into(), + version: PLATFORM_INSTALL_HANDOFF_VERSION, + task_id: Some(task.task_id), + package_id: task.package_id, + repository_id: plan.repository_id, + package_version: plan.package_version, + request: PlatformInstallRequest::AndroidApk { + target: AndroidInstallTarget { + kind: "android".into(), + package_name: plan.package_name, + }, + artifact: StagedArtifact { + name: plan.artifact_name, + path: actual_path, + sha256: plan.artifact_sha256, + status: "downloaded".to_owned(), + }, + }, + }) +} + #[cfg(feature = "lua")] pub fn prepare_platform_install( data_dir: &Path, @@ -650,6 +862,7 @@ pub fn prepare_platform_install_with_transports( Ok(PlatformInstallHandoff { format: PLATFORM_INSTALL_HANDOFF_FORMAT.into(), version: PLATFORM_INSTALL_HANDOFF_VERSION, + task_id: None, package_id: download.package_id, repository_id: download.repository_id, package_version: download.version, @@ -867,17 +1080,16 @@ fn absolute_staging_root(data_dir: &Path) -> Result } #[cfg(feature = "lua")] -fn prepare_artifact( - downloads: &Path, +fn manifest_artifact_digest( manifest: &PackageManifest, artifact: &UpdateArtifact, -) -> Result { +) -> Result { let file_name = artifact .file_name .as_deref() .unwrap_or(artifact.name.as_str()); let members = manifest.artifact_sha256_members(file_name); - let digest = if let Some(hint) = artifact.sha256.as_deref() { + if let Some(hint) = artifact.sha256.as_deref() { if hint.len() != 64 || !hint.bytes().all(|byte| byte.is_ascii_hexdigit()) { return Err(AppOperationError::ArtifactSha256Invalid { artifact: artifact.name.clone(), @@ -890,24 +1102,32 @@ fn prepare_artifact( file_name: file_name.to_owned(), }); } - hint - } else { - match members { - [] => { - return Err(AppOperationError::ManifestArtifactMissing { - artifact: artifact.name.clone(), - file_name: file_name.to_owned(), - }); - } - [digest] => digest.clone(), - _ => { - return Err(AppOperationError::ManifestArtifactAmbiguous { - artifact: artifact.name.clone(), - file_name: file_name.to_owned(), - }); - } - } - }; + return Ok(hint); + } + match members { + [] => Err(AppOperationError::ManifestArtifactMissing { + artifact: artifact.name.clone(), + file_name: file_name.to_owned(), + }), + [digest] => Ok(digest.clone()), + _ => Err(AppOperationError::ManifestArtifactAmbiguous { + artifact: artifact.name.clone(), + file_name: file_name.to_owned(), + }), + } +} + +#[cfg(feature = "lua")] +fn prepare_artifact( + downloads: &Path, + manifest: &PackageManifest, + artifact: &UpdateArtifact, +) -> Result { + let file_name = artifact + .file_name + .as_deref() + .unwrap_or(artifact.name.as_str()); + let digest = manifest_artifact_digest(manifest, artifact)?; let name = sanitized_artifact_file_name(file_name); let final_path = downloads.join(format!("{digest}-{name}")); let temporary_path = PathBuf::from(format!( @@ -922,6 +1142,14 @@ fn prepare_artifact( }) } +#[cfg(feature = "lua")] +fn sha256_file(path: &Path) -> Result { + let mut file = fs::File::open(path)?; + let mut hasher = Sha256::new(); + std::io::copy(&mut file, &mut hasher)?; + Ok(format!("{:x}", hasher.finalize())) +} + #[cfg(feature = "lua")] fn stage_artifact( prepared: PreparedArtifact, diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs index 826e45f..7ad65d4 100644 --- a/crates/getter-operations/src/autogen.rs +++ b/crates/getter-operations/src/autogen.rs @@ -186,6 +186,7 @@ pub(crate) struct AutogenApplySpec<'a> { struct PreparedCandidate { package_id: PackageId, + relative_path: PathBuf, target_dir: PathBuf, payload: PreviewCandidatePayload, } @@ -596,6 +597,7 @@ fn apply_preview_batch_locked( } prepared.push(PreparedCandidate { package_id, + relative_path, target_dir, payload, }); @@ -675,12 +677,21 @@ fn apply_preview_batch_locked( })?; let result_package_ids = journal.package_ids.clone(); + let result_packages = prepared + .iter() + .map(|candidate| { + json!({ + "package_id": candidate.package_id.to_string(), + "output_relative_path": candidate.relative_path, + }) + }) + .collect::>(); let committed_result = || { json!({ "operation": "autogen.apply", "target_repo_id": target_alias.as_str(), "accepted_package_ids": result_package_ids, - "applied": result_package_ids.iter().map(|id| json!({ "package_id": id })).collect::>(), + "applied": result_packages, "written": result_package_ids.len(), "applied_count": result_package_ids.len(), }) @@ -1460,6 +1471,13 @@ mod tests { .unwrap(); assert_eq!(result["target_repo_id"], "autogen"); + assert_eq!( + result["applied"][0], + json!({ + "package_id": "android/app/com.example.autogen", + "output_relative_path": "android/app/com.example.autogen", + }) + ); assert!(data_dir.join("repo/autogen").is_dir()); assert!(data_dir .join("repo/autogen/android/app/com.example.autogen/metadata.jsonc") diff --git a/crates/getter-operations/src/download.rs b/crates/getter-operations/src/download.rs index 9bd8e41..14bc34d 100644 --- a/crates/getter-operations/src/download.rs +++ b/crates/getter-operations/src/download.rs @@ -301,7 +301,7 @@ struct RuntimeFileDownloadResult { sha256: String, } -fn safe_download_file_name(raw: &str) -> String { +pub(crate) fn safe_download_file_name(raw: &str) -> String { let base = raw .trim() .rsplit(['/', '\\']) @@ -546,6 +546,7 @@ mod tests { url: url.to_owned(), file_name: file_name.to_owned(), }], + android_apk_install: None, } } diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 8bee8e2..edba615 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -158,6 +158,7 @@ pub fn issue_action_from_offline_update_check_json( runtime.issue_action(SealedActionPlan { package_id: update.package_id.clone(), actions: update.actions.clone(), + android_apk_install: None, lua_object: PackageVersionLuaObject { object_id: format!("offline-update:{}", update.package_id), dependency_digest: request @@ -252,10 +253,17 @@ pub(crate) fn issue_action_from_registered_evaluation( pin_version: request.pin_version, }, )?; + let android_apk_install = crate::app::seal_android_apk_install_plan( + &evaluated.package_path, + &evaluated.package, + update.selected.as_ref(), + ) + .map_err(|error| RuntimeOperationError::PackageEval(error.to_string()))?; let action = (!update.actions.is_empty()).then(|| { runtime.issue_action(SealedActionPlan { package_id: update.package_id.clone(), actions: update.actions.clone(), + android_apk_install, lua_object: PackageVersionLuaObject { object_id: format!("package-update:{}", update.package_id), dependency_digest: evaluated.dependency_digest, @@ -1722,6 +1730,7 @@ return package_version { file: "app.apk".to_owned(), }, ], + android_apk_install: None, lua_object: lua_object(package_id), } } @@ -1733,6 +1742,7 @@ return package_version { url: "https://example.invalid/archive.zip".to_owned(), file_name: "archive.zip".to_owned(), }], + android_apk_install: None, lua_object: lua_object(package_id), } } diff --git a/crates/getter-operations/tests/installer.rs b/crates/getter-operations/tests/installer.rs index 32c173b..4f3a6fd 100644 --- a/crates/getter-operations/tests/installer.rs +++ b/crates/getter-operations/tests/installer.rs @@ -1,17 +1,22 @@ #![cfg(feature = "lua")] use getter_core::repository::RepositoryMetadata; +use getter_core::runtime::GetterRuntime; use getter_core::{RepositoryId, RepositoryPriority}; use getter_operations::app::{ download_app_with_transports, install_app_with_dependencies, - install_app_with_dependencies_and_observer, prepare_platform_install_with_transports, - CommandObserver, CommandResolver, CommandRunner, PlatformInstallRequest, - ResolvedInstallerCommand, RunnerOutput, + install_app_with_dependencies_and_observer, prepare_platform_install_for_task, + prepare_platform_install_with_transports, CommandObserver, CommandResolver, CommandRunner, + PlatformInstallRequest, ResolvedInstallerCommand, RunnerOutput, }; use getter_operations::download::{ RuntimeDownloadSink, RuntimeDownloadTransport, RuntimeDownloadTransportError, RuntimeDownloadTransportRequest, }; +use getter_operations::runtime::{ + issue_action_from_registered_package_json_with_github_transport, + submit_action_and_download_json_with_transport, +}; use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; use sha2::{Digest, Sha256}; use std::cell::RefCell; @@ -125,6 +130,138 @@ fn fixture(install: &str, bytes: &[u8]) -> tempfile::TempDir { ) } +#[test] +fn task_scoped_prepare_uses_the_sealed_candidate_and_task_staging() { + let bytes = b"apk"; + let temp = fixture(r#"{kind="android_apk",artifact={artifact="app"}}"#, bytes); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + let mut runtime = GetterRuntime::new(); + let issued = issue_action_from_registered_package_json_with_github_transport( + &mut runtime, + temp.path(), + &db, + &serde_json::json!({ + "package_id": "android/com.example", + "installed_version": "1" + }) + .to_string(), + None, + ) + .unwrap(); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let task = submit_action_and_download_json_with_transport( + &mut runtime, + temp.path(), + &serde_json::json!({"action_id": action_id}).to_string(), + &Bytes(bytes.to_vec()), + ) + .unwrap(); + assert_eq!(task["status"], "running"); + assert_eq!(task["phase"]["category"], "waiting_user"); + assert_eq!(task["phase"]["reason"], "install_handoff"); + let task_id = task["task_id"].as_str().unwrap(); + let task_path = PathBuf::from(task["downloaded_file"]["local_path"].as_str().unwrap()); + + let package = temp.path().join("repo/official/android/com.example"); + fs::write( + package.join("metadata.jsonc"), + r#"{"type":"android:app","android":{"package_name":"com.changed"}}"#, + ) + .unwrap(); + fs::write( + package.join("Manifest"), + format!("{} app.apk\n", digest(b"changed")), + ) + .unwrap(); + fs::write( + package.join("1.lua"), + r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="99", artifacts={{name="app",file_name="app.apk",url="mock://changed"}}, install={kind="android_apk",artifact={artifact="app"}} }} }"#, + ) + .unwrap(); + + let handoff = prepare_platform_install_for_task(&runtime, temp.path(), task_id).unwrap(); + + assert_eq!(handoff.task_id.as_deref(), Some(task_id)); + assert_eq!(serde_json::to_value(&handoff).unwrap()["task_id"], task_id); + assert_eq!(handoff.package_id.to_string(), "android/com.example"); + assert_eq!(handoff.repository_id, "official"); + assert_eq!(handoff.package_version, "2"); + let PlatformInstallRequest::AndroidApk { target, artifact } = handoff.request; + assert_eq!(target.package_name, "com.example"); + assert_eq!(artifact.name, "app"); + assert_eq!(artifact.path, task_path); + assert_eq!(artifact.sha256, digest(bytes)); +} + +#[test] +fn task_scoped_prepare_rejects_staged_bytes_changed_after_getter_download() { + let bytes = b"apk"; + let temp = fixture(r#"{kind="android_apk",artifact={artifact="app"}}"#, bytes); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + let mut runtime = GetterRuntime::new(); + let issued = issue_action_from_registered_package_json_with_github_transport( + &mut runtime, + temp.path(), + &db, + &serde_json::json!({ + "package_id": "android/com.example", + "installed_version": "1" + }) + .to_string(), + None, + ) + .unwrap(); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let task = submit_action_and_download_json_with_transport( + &mut runtime, + temp.path(), + &serde_json::json!({"action_id": action_id}).to_string(), + &Bytes(bytes.to_vec()), + ) + .unwrap(); + let task_id = task["task_id"].as_str().unwrap(); + let task_path = task["downloaded_file"]["local_path"].as_str().unwrap(); + fs::write(task_path, b"bad").unwrap(); + + let error = prepare_platform_install_for_task(&runtime, temp.path(), task_id).unwrap_err(); + + assert_eq!(error.code(), "artifact.sha256_mismatch"); +} + +#[test] +fn task_scoped_prepare_reports_missing_sealed_android_plan() { + let bytes = b"apk"; + let temp = fixture(r#"{executable="fake-installer",args={}}"#, bytes); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + let mut runtime = GetterRuntime::new(); + let issued = issue_action_from_registered_package_json_with_github_transport( + &mut runtime, + temp.path(), + &db, + &serde_json::json!({ + "package_id": "android/com.example", + "installed_version": "1" + }) + .to_string(), + None, + ) + .unwrap(); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let task = submit_action_and_download_json_with_transport( + &mut runtime, + temp.path(), + &serde_json::json!({"action_id": action_id}).to_string(), + &Bytes(bytes.to_vec()), + ) + .unwrap(); + let task_id = task["task_id"].as_str().unwrap(); + + let error = prepare_platform_install_for_task(&runtime, temp.path(), task_id).unwrap_err(); + + assert_eq!(error.code(), "installer.task_plan_missing"); +} + #[test] fn prepares_android_apk_handoff_from_verified_staging() { let bytes = b"apk"; @@ -140,6 +277,7 @@ fn prepares_android_apk_handoff_from_verified_staging() { assert_eq!(handoff.format, "getter-platform-install-handoff"); assert_eq!(handoff.version, 1); + assert_eq!(handoff.task_id, None); assert_eq!(handoff.package_id.to_string(), "android/com.example"); assert_eq!(handoff.repository_id.as_str(), "official"); assert_eq!(handoff.package_version, "2");