diff --git a/.github/pr-build/large-import.patch.b64.1 b/.github/pr-build/large-import.patch.b64.1 deleted file mode 100644 index 7c5254ec..00000000 --- a/.github/pr-build/large-import.patch.b64.1 +++ /dev/null @@ -1 +0,0 @@ -ZGlmZiAtLWdpdCBhL2RvY3MvbW9kZGluZy5tZCBiL2RvY3MvbW9kZGluZy5tZAppbmRleCA0MDE2ZTFmLi4yNWU3NTk2IDEwMDY0NAotLS0gYS9kb2NzL21vZGRpbmcubWQKKysrIGIvZG9jcy9tb2RkaW5nLm1kCkBAIC0xMjIsMjEgKzEyMiw0MCBAQCBFYWNoIG9iamVjdCByZXF1aXJlcyBhIHN0YWJsZSBgaWRgLCBhIGRpc3BsYXkgYG5hbWVgLCBhIGRlc3RpbmF0aW9uIGBmaWxlYAogZGlnZXN0cy4gYGZvcm1hdGAgaXMgZWl0aGVyIGAicmF3ImAgKHRoZSBkZWZhdWx0KSBvciBgIm42NCJgLiBBbiBvcHRpb25hbAogYGRlc2NyaXB0aW9uYCBnaXZlcyBwbGF5ZXJzIGR1bXAgb3IgcmVnaW9uIGd1aWRhbmNlIGluIHRoZSBpbXBvcnQgcGFuZWwuCiBgc2l6ZWAgZGVjbGFyZXMgdGhlIGV4YWN0IGNhbm9uaWNhbCBieXRlIGxlbmd0aDsgYG1heF9zaXplYCBkZWNsYXJlcyBhIHNtYWxsZXIKLXBlci1pbXBvcnQgY2VpbGluZyB3aGVuIGFuIGV4YWN0IHNpemUgaXMgbm90IGFwcHJvcHJpYXRlLiBFdmVyeSBpbXBvcnQgYWxzbwotaGFzIGFuIGVuZ2luZS1lbmZvcmNlZCAxMjggTWlCIGNlaWxpbmcgYW5kIGlzIHJlamVjdGVkIGJlZm9yZSBoYXNoaW5nIHdoZW4gaXRzCi1maWxlc3lzdGVtIHJlcG9ydHMgYW4gaW52YWxpZCBzaXplLgorcGVyLWltcG9ydCBjZWlsaW5nIHdoZW4gYW4gZXhhY3Qgc2l6ZSBpcyBub3QgYXBwcm9wcmlhdGUuIFRoZSBlbmdpbmUgaGFyZCBsaW1pdAoraXMgMiBHaUIuIEltcG9ydHMgYWJvdmUgMTI4IE1pQiByZWNlaXZlIGFuIGV4cGxpY2l0IGZyZWUtc3BhY2UgY29uZmlybWF0aW9uIGFuZAordXNlIHRoZSBsYXVuY2hlcidzIHN0cmVhbWluZyBsYXJnZS1maWxlIHBhdGggcmF0aGVyIHRoYW4gYmVpbmcgbWF0ZXJpYWxpemVkIGFzCitvbmUgTHVhIHN0cmluZy4KIAogRm9yIGAibjY0ImAsIHRoZSBsYXVuY2hlciByZWNvZ25pemVzIGAuejY0YCwgYC52NjRgLCBhbmQgYC5uNjRgIGJ5dGUgb3JkZXJzLAogc3RyaXBzIGEgcmVjb2duaXplZCA1MTItYnl0ZSBjb3BpZXIgaGVhZGVyLCBjb252ZXJ0cyB0aGUgYnl0ZXMgdG8gY2Fub25pY2FsCiBiaWctZW5kaWFuIGAuejY0YCBvcmRlciwgYW5kIHRoZW4gY2hlY2tzIE1ENS4gVGhlIGNhbm9uaWNhbCBieXRlcyBhcmUgd3JpdHRlbgogdG8gYG1vZHMvPG1vZC1pZD4vYmFzZXJvbXMvPGZpbGU+YC4gRWFjaCBzZWxlY3Rpb24gaXMgYSBwcml2YXRlIGdyYW50IHRvIHRoYXQKIG1vZDogdGhlIGxhdW5jaGVyIG5ldmVyIHNjYW5zIG9yIGNvcGllcyBhbm90aGVyIG1vZCdzIGltcG9ydGVkIGZpbGVzIG1lcmVseQotYmVjYXVzZSBpdHMgbWFuaWZlc3QgbmFtZXMgdGhlIHNhbWUgZGlnZXN0LiBNb2RzIHJlYWQgdGhlIHJlc3VsdCB3aXRoIHRoZWlyIGV4aXN0aW5nIHNjb3BlZCBgbW9kOnJlYWRgIEFQSSwgZm9yCi1leGFtcGxlIGBtb2Q6cmVhZCgiYmFzZXJvbXMvc3RhZGl1bTIuejY0IilgOyBubyBob3N0IHBhdGggb3IgbmV3IGZpbGVzeXN0ZW0KK2JlY2F1c2UgaXRzIG1hbmlmZXN0IG5hbWVzIHRoZSBzYW1lIGRpZ2VzdC4gU21hbGwgc291cmNlcyBjYW4gc3RpbGwgYmUgcmVhZAord2l0aCB0aGUgZXhpc3Rpbmcgc2NvcGVkIGBtb2Q6cmVhZGAgQVBJLCBmb3IgZXhhbXBsZQorYG1vZDpyZWFkKCJiYXNlcm9tcy9zdGFkaXVtMi56NjQiKWAuIEZvciBsYXJnZSBzb3VyY2VzLCBwcmVmZXIgdGhlIGJvdW5kZWQKK2Btb2QuaW1wb3J0c2AgZmFjYWRlIGRlc2NyaWJlZCBiZWxvdzsgbm8gaG9zdCBwYXRoIG9yIG5ldyBnZW5lcmFsIGZpbGVzeXN0ZW0KIHBlcm1pc3Npb24gaXMgZXhwb3NlZC4gTWlzc2luZyBgcmVxdWlyZWRfaW1wb3J0c2AgYmxvY2sgdGhlIG1vZCBiZWZvcmUgaXRzCiBlbnRyeSBjaHVuayBydW5zOyBtaXNzaW5nIGBvcHRpb25hbF9pbXBvcnRzYCByZW1haW4gdmlzaWJsZSBpbiB0aGUgc2FtZQogbGF1bmNoZXIgcGFuZWwgYnV0IGRvIG5vdCBibG9jayBsb2FkaW5nLgogCisjIyMjIEJvdW5kZWQgYWNjZXNzIHRvIHZhbGlkYXRlZCBpbXBvcnRzCisKK0EgbG9hZGVkIG1vZCBjYW4gYWRkcmVzcyBvbmx5IGlkcyBkZWNsYXJlZCBieSBpdHMgb3duIGByZXF1aXJlZF9pbXBvcnRzYCBvcgorYG9wdGlvbmFsX2ltcG9ydHNgIGFycmF5czoKKworYGBgbHVhCitsb2NhbCBpbmZvLCBlcnIgPSBtb2QuaW1wb3J0czppbmZvKCJzdGFkaXVtMiIpCitsb2NhbCBoZWFkZXIsIGVyciA9IG1vZC5pbXBvcnRzOnJlYWQoInN0YWRpdW0yIiwgMCwgNDA5NikKK2BgYAorCitgcmVhZGAgdXNlcyB6ZXJvLWJhc2VkIG9mZnNldHMgYW5kIGlzIGNhcHBlZCBhdCA4IE1pQiBwZXIgY2FsbC4gVGhlIGVuZ2luZQorcmVjaGVja3MgdGhlIHN0b3JlZCBpbXBvcnQgYmVmb3JlIGV4cG9zaW5nIGl0LCBzZWVrcyBpbnRvIHRoZSBlbmdpbmUtb3duZWQKK2NvcHksIGFuZCBuZXZlciBnaXZlcyB0aGUgbW9kIGEgaG9zdCBwYXRoIG9yIGZpbGUgaGFuZGxlLiBUaGlzIGlzIGludGVuZGVkIGZvcgorbGFyZ2Ugc291cmNlIGZvcm1hdHMgd2hvc2UgdGFibGUvaW5kZXggY2FuIGJlIHBhcnNlZCB3aXRoIHNtYWxsIHJlYWRzIGJlZm9yZQorc2VsZWN0aXZlbHkgcmVhZGluZyB0aGUgcGF5bG9hZHMgYSB0cmFuc2Zvcm0gYWN0dWFsbHkgbmVlZHMuCisKIE1ENSBoZXJlIGlkZW50aWZpZXMgYSBrbm93biBkdW1wIGJlY2F1c2UgUk9NIGRhdGFiYXNlcyBjb21tb25seSBwdWJsaXNoIGl0OwogaXQgaXMgbm90IGEgc2VjdXJpdHkgb3IgYXV0aGVudGljaXR5IGd1YXJhbnRlZS4gRG8gbm90IHBhc3RlIHRoZSBTSEEtMSB1c2VkIGJ5CiBHZW4xUmVjb21wJ3Mgb3duIGdhbWUtUk9NIGltcG9ydGVyIGludG8gYW4gaW1wb3J0J3MgYG1kNWAgZmllbGQuIE1vZCBhcmNoaXZlcwpAQCAtNDQxLDYgKzQ2MCwyOCBAQCBkZWZhdWx0KiogKDF4IGZyb250LCAyeCBiYWNrKS4KICAgYmFsbC10by1waWMgZ3JvdyBtdWx0aXBsaWVzIHlvdXIgc2NhbGUgdGhyb3VnaCBlYWNoIHN0YWdlLCBzbyBhIHJlc2NhbGVkCiAgIG1vbiBzdGlsbCBncm93cyBpbnRvIHBsYWNlIGZyb20gdGhlIGJhbGwsIGdyb3VuZGVkIHRoZSB3aG9sZSB3YXkuCiAKKyMjIEluc3RhbGxhdGlvbi1zY29wZWQgZ2VuZXJhdGVkIGNhY2hlCisKK0dlbmVyYXRlZCBkYXRhIGRlcml2ZWQgZnJvbSBhIHZhbGlkYXRlZCB1c2VyIHNvdXJjZSBvZnRlbiBiZWxvbmdzIHRvIHRoZSBtb2QKK2luc3RhbGxhdGlvbiByYXRoZXIgdGhhbiB0byBvbmUgUG9rw6ltb24gc2F2ZS4gYG1vZC5jYWNoZWAgaXMgdGhhdCBuYW1lc3BhY2U6CisKK2BgYGx1YQorbG9jYWwgb2ssIGVyciA9IG1vZC5jYWNoZTp3cml0ZSgiZXh0cmFjdC92MS9hcmVuYS5iaW4iLCBlbmNvZGVkQXJlbmEpCitsb2NhbCBieXRlcywgZXJyID0gbW9kLmNhY2hlOnJlYWQoImV4dHJhY3QvdjEvYXJlbmEuYmluIikKK2xvY2FsIGluZm8gPSBtb2QuY2FjaGU6aW5mbygiZXh0cmFjdC92MS9hcmVuYS5iaW4iKQorbW9kLmNhY2hlOmRlbGV0ZSgiZXh0cmFjdC92MS9hcmVuYS5iaW4iKQorYGBgCisKK1RoZSBwaHlzaWNhbCByb290IGlzIGVuZ2luZS1vd25lZCAoYG1vZF9jYWNoZS88bW9kLWlkPi9gKSBhbmQgbmV2ZXIgZXhwb3NlZCB0bwordGhlIG1vZC4gS2V5cyBhcmUgc2FmZSByZWxhdGl2ZSBwYXRocyBhbmQgYSBzaW5nbGUgd3JpdGUgaXMgY2FwcGVkIGF0IDY0IE1pQi4KK1RoZSBjYWNoZSBkb2VzIG5vdCByZXdpbmQgd2l0aCBjaGVja3BvaW50cyBhbmQgaXMgbm90IHNjb3BlZCB0byBnYW1lIHZlcnNpb24sCitzbG90LCBvciBwbGF5dGhyb3VnaC4gVGhlIG1vZCBvd25zIGl0cyBnZW5lcmF0ZWQgZm9ybWF0LCBmaW5nZXJwcmludHMsIHJlYnVpbGQKK3BvbGljeSwgYW5kIGNvbXBsZXRpb24gbWFya2VyOyB0aGUgZW5naW5lIHRyZWF0cyB0aGUgYnl0ZXMgYXMgb3BhcXVlIGRhdGEuCisKK1VzZSBgbW9kLnN0b3JhZ2VgIGluc3RlYWQgd2hlbiB0aGUgZGF0YSBiZWxvbmdzIHRvIG9uZSBwbGF5dGhyb3VnaC4gVXNlCitgbW9kLmNhY2hlYCB3aGVuIGl0IGlzIGEgcmVwcm9kdWNpYmxlIGluc3RhbGxhdGlvbiBhcnRpZmFjdCB0aGF0IGNhbiBiZSByZWJ1aWx0Citmcm9tIGEgZGVjbGFyZWQgdXNlciBzb3VyY2UuCisKICMjIER1cmFibGUgdG9vbCBzdG9yYWdlIGFuZCBydW50aW1lIGNoZWNrcG9pbnRzCiAKIGBtb2Quc2F2ZWAgcmVtYWlucyB0aGUgcmlnaHQgcGxhY2UgZm9yIHN0YXRlIHRoYXQgc2hvdWxkIHRyYXZlbCB3aXRoIHRoZSBuZXh0CmRpZmYgLS1naXQgYS9zcmMvaW1wb3J0L0NhY2hlRnMubHVhIGIvc3JjL2ltcG9ydC9DYWNoZUZzLmx1YQppbmRleCBhM2JmZDg4Li4wMGVmOGU5IDEwMDY0NAotLS0gYS9zcmMvaW1wb3J0L0NhY2hlRnMubHVhCisrKyBiL3NyYy9pbXBvcnQvQ2FjaGVGcy5sdWEKQEAgLTI4Nyw2ICsyODcsNDMgQEAgZnVuY3Rpb24gQ2FjaGVGcy53cml0ZShyZWwsIGRhdGEpCiAgIHJldHVybiBsb3ZlLmZpbGVzeXN0ZW0ud3JpdGUocmVsLCBkYXRhKQogZW5kCiAKKy0tIE9wZW4gYSBjYWNoZS1yZWxhdGl2ZSBmaWxlIGZvciBzdHJlYW1pbmcgcmVwbGFjZW1lbnQuIFRoZSByZXR1cm5lZCBoYW5kbGUKKy0tIGhhcyB3cml0ZShieXRlcykgYW5kIGNsb3NlKCkgbWV0aG9kcyBhbmQgZm9sbG93cyB0aGUgc2FtZSBwb3J0YWJsZS9zYXZlLWRpcgorLS0gcm91dGluZyBhcyBDYWNoZUZzLndyaXRlIHdpdGhvdXQgZm9yY2luZyB0aGUgY2FsbGVyIHRvIGhvbGQgdGhlIHdob2xlIGZpbGUKKy0tIGluIG9uZSBMdWEgc3RyaW5nLgorZnVuY3Rpb24gQ2FjaGVGcy5vcGVuV3JpdGUocmVsKQorICByZWwgPSB3aXRoUHJlZml4KHJlbCkKKyAgbG9jYWwgcm9vdCA9IENhY2hlRnMucm9vdCgpCisgIGlmIHJvb3QgdGhlbgorICAgIGVuc3VyZVBhcmVudHMocm9vdCwgcmVsKQorICAgIGxvY2FsIGYsIGVyciA9IGlvLm9wZW4ocmVhbFBhdGgocm9vdCwgcmVsKSwgIndiIikKKyAgICBpZiBub3QgZiB0aGVuIHJldHVybiBuaWwsIGVyciBlbmQKKyAgICByZXR1cm4geworICAgICAgd3JpdGUgPSBmdW5jdGlvbihfLCBkYXRhKQorICAgICAgICBsb2NhbCBvaywgd3JpdGVFcnIgPSBmOndyaXRlKGRhdGEpCisgICAgICAgIGlmIG5vdCBvayB0aGVuIHJldHVybiBuaWwsIHdyaXRlRXJyIGVuZAorICAgICAgICByZXR1cm4gdHJ1ZQorICAgICAgZW5kLAorICAgICAgY2xvc2UgPSBmdW5jdGlvbigpIGY6Y2xvc2UoKSBlbmQsCisgICAgfQorICBlbmQKKyAgaWYgbm90IChsb3ZlIGFuZCBsb3ZlLmZpbGVzeXN0ZW0gYW5kIGxvdmUuZmlsZXN5c3RlbS5uZXdGaWxlKSB0aGVuCisgICAgcmV0dXJuIG5pbCwgInN0cmVhbWluZyBjYWNoZSB3cml0ZXMgYXJlIHVuYXZhaWxhYmxlIgorICBlbmQKKyAgbG9jYWwgcGFyZW50ID0gcmVsOm1hdGNoKCJeKC4qKS9bXi9dKyQiKQorICBpZiBwYXJlbnQgYW5kIG5vdCBsb3ZlLmZpbGVzeXN0ZW0uY3JlYXRlRGlyZWN0b3J5KHBhcmVudCkgdGhlbgorICAgIGxvY2FsIGluZm8gPSBsb3ZlLmZpbGVzeXN0ZW0uZ2V0SW5mbyhwYXJlbnQpCisgICAgbG9jYWwgcmVhc29uID0gaW5mbyBhbmQgKCJhICIgLi4gaW5mby50eXBlIC4uICIgYWxyZWFkeSBleGlzdHMgdGhlcmUiKQorICAgICAgb3IgInVua25vd24gcmVhc29uIgorICAgIHJldHVybiBuaWwsICJjb3VsZCBub3QgY3JlYXRlICIgLi4gcGFyZW50IC4uICI6ICIgLi4gcmVhc29uCisgIGVuZAorICBsb2NhbCBmaWxlLCBtYWtlRXJyID0gbG92ZS5maWxlc3lzdGVtLm5ld0ZpbGUocmVsKQorICBpZiBub3QgZmlsZSB0aGVuIHJldHVybiBuaWwsIG1ha2VFcnIgb3IgImNvdWxkIG5vdCBjcmVhdGUgY2FjaGUgZmlsZSIgZW5kCisgIGxvY2FsIG9rLCBvcGVuRXJyID0gZmlsZTpvcGVuKCJ3IikKKyAgaWYgbm90IG9rIHRoZW4gcmV0dXJuIG5pbCwgb3BlbkVyciBvciAiY291bGQgbm90IG9wZW4gY2FjaGUgZmlsZSIgZW5kCisgIHJldHVybiBmaWxlCitlbmQKKwogLS0gcmVhZCBjYWNoZS1yZWxhdGl2ZSBgcmVsYDsgcmV0dXJucyB0aGUgYnl0ZXMgb3IgbmlsCiBmdW5jdGlvbiBDYWNoZUZzLnJlYWQocmVsKQogICByZWwgPSB3aXRoUHJlZml4KHJlbCkKZGlmZiAtLWdpdCBhL3NyYy9pbXBvcnQvUm9tSW1wb3J0ZXIubHVhIGIvc3JjL2ltcG9ydC9Sb21JbXBvcnRlci5sdWEKaW5kZXggOWNlMTc2Zi4uNDQ2YmI5MCAxMDA2NDQKLS0tIGEvc3JjL2ltcG9ydC9Sb21JbXBvcnRlci5sdWEKKysrIGIvc3JjL2ltcG9ydC9Sb21JbXBvcnRlci5sdWEKQEAgLTM4Miw2ICszODIsMTAyIEBAIGxvY2FsIGZ1bmN0aW9uIGV4dGVybmFsRmlsZVNpemUocGF0aCkKICAgcmV0dXJuIHNpemUKIGVuZAogCitsb2NhbCBmdW5jdGlvbiBvcGVuSW1wb3J0U291cmNlKHBhdGgpCisgIC0tIERlc2t0b3AgcGlja2VyIHBhdGhzIGxpdmUgb3V0c2lkZSBMw5ZWRSdzIHZpcnR1YWwgZmlsZXN5c3RlbS4gUHJlZmVyIHRoZQorICAtLSBuYXRpdmUgZmlsZSBoYW5kbGUgc28gYSAxLjQ2IEdpQiBkaXNjIGlzIG5ldmVyIGNvcGllZCB0byBhIHRlbXAgZmlsZSBvcgorICAtLSByZWFkIGludG8gb25lIEx1YSBzdHJpbmcgYmVmb3JlIHZhbGlkYXRpb24uCisgIGxvY2FsIG5hdGl2ZSA9IGlvLm9wZW4ocGF0aCwgInJiIikKKyAgaWYgbmF0aXZlIHRoZW4KKyAgICBsb2NhbCBzaXplID0gbmF0aXZlOnNlZWsoImVuZCIpCisgICAgaWYgc2l6ZSB0aGVuIG5hdGl2ZTpzZWVrKCJzZXQiLCAwKSBlbmQKKyAgICByZXR1cm4geworICAgICAgc2l6ZSA9IHNpemUsCisgICAgICByZWFkID0gZnVuY3Rpb24oXywgbikgcmV0dXJuIG5hdGl2ZTpyZWFkKG4pIGVuZCwKKyAgICAgIGNsb3NlID0gZnVuY3Rpb24oKSBuYXRpdmU6Y2xvc2UoKSBlbmQsCisgICAgfQorICBlbmQKKyAgaWYgbG92ZSBhbmQgbG92ZS5maWxlc3lzdGVtIGFuZCBsb3ZlLmZpbGVzeXN0ZW0ubmV3RmlsZSB0aGVuCisgICAgbG9jYWwgZmlsZSwgbWFrZUVyciA9IGxvdmUuZmlsZXN5c3RlbS5uZXdGaWxlKHBhdGgpCisgICAgaWYgbm90IGZpbGUgdGhlbiByZXR1cm4gbmlsLCBtYWtlRXJyIG9yICJjb3VsZCBub3Qgb3BlbiBzb3VyY2UgZmlsZSIgZW5kCisgICAgbG9jYWwgb2ssIG9wZW5FcnIgPSBmaWxlOm9wZW4oInIiKQorICAgIGlmIG5vdCBvayB0aGVuIHJldHVybiBuaWwsIG9wZW5FcnIgb3IgImNvdWxkIG5vdCBvcGVuIHNvdXJjZSBmaWxlIiBlbmQKKyAgICBsb2NhbCBzaXplID0gZmlsZS5nZXRTaXplIGFuZCBmaWxlOmdldFNpemUoKSBvciBuaWwKKyAgICByZXR1cm4geworICAgICAgc2l6ZSA9IHNpemUsCisgICAgICByZWFkID0gZnVuY3Rpb24oXywgbikgcmV0dXJuIGZpbGU6cmVhZChuKSBlbmQsCisgICAgICBjbG9zZSA9IGZ1bmN0aW9uKCkgZmlsZTpjbG9zZSgpIGVuZCwKKyAgICB9CisgIGVuZAorICByZXR1cm4gbmlsLCAic3RyZWFtaW5nIHNvdXJjZSBhY2Nlc3MgaXMgdW5hdmFpbGFibGUiCitlbmQKKworbG9jYWwgZnVuY3Rpb24gc3RyZWFtUmVxdWlyZWRJbXBvcnQobWFuaWZlc3QsIGltcG9ydElkLCBzb3VyY2UpCisgIGxvY2FsIFJlcXVpcmVkSW1wb3J0cyA9IHJlcXVpcmUoInNyYy5tb2RzLlJlcXVpcmVkSW1wb3J0cyIpCisgIGxvY2FsIHNwZWMgPSBSZXF1aXJlZEltcG9ydHMuc3BlYyhtYW5pZmVzdCwgaW1wb3J0SWQpCisgIGlmIG5vdCBzcGVjIHRoZW4gcmV0dXJuIG5pbCwgIkltcG9ydCBkZWNsYXJhdGlvbiB3YXMgbm90IGZvdW5kLiIgZW5kCisgIGlmIHNwZWMuZm9ybWF0ID09ICJuNjQiIHRoZW4KKyAgICByZXR1cm4gbmlsLCAic3RyZWFtaW5nIGNhbm9uaWNhbGl6YXRpb24gaXMgdW5hdmFpbGFibGUgZm9yIE42NCBpbXBvcnRzIgorICBlbmQKKyAgbG9jYWwgaW5wdXQsIG9wZW5FcnIgPSBvcGVuSW1wb3J0U291cmNlKHNvdXJjZSkKKyAgaWYgbm90IGlucHV0IHRoZW4gcmV0dXJuIG5pbCwgb3BlbkVyciBlbmQKKyAgbG9jYWwgc2l6ZUVyciA9IFJlcXVpcmVkSW1wb3J0cy5zaXplRXJyb3Ioc3BlYywgaW5wdXQuc2l6ZSwgZmFsc2UpCisgIGlmIHNpemVFcnIgdGhlbiBpbnB1dDpjbG9zZSgpOyByZXR1cm4gbmlsLCBzaXplRXJyIGVuZAorCisgIGxvY2FsIENhY2hlRnMgPSByZXF1aXJlKCJzcmMuaW1wb3J0LkNhY2hlRnMiKQorICBsb2NhbCBkZXN0aW5hdGlvbiA9IFJlcXVpcmVkSW1wb3J0cy5wYXRoKG1hbmlmZXN0LCBzcGVjKQorICBsb2NhbCBzYXZlZFByZWZpeCA9IENhY2hlRnMucHJlZml4CisgIENhY2hlRnMucHJlZml4ID0gIiIKKyAgQ2FjaGVGcy5yZW1vdmUoUmVxdWlyZWRJbXBvcnRzLnJlY2VpcHRQYXRoKG1hbmlmZXN0LCBzcGVjKSkKKyAgQ2FjaGVGcy5yZW1vdmUoZGVzdGluYXRpb24pCisgIGxvY2FsIG91dHB1dCwgbWFrZUVyciA9IENhY2hlRnMub3BlbldyaXRlKGRlc3RpbmF0aW9uKQorICBpZiBub3Qgb3V0cHV0IHRoZW4KKyAgICBDYWNoZUZzLnByZWZpeCA9IHNhdmVkUHJlZml4CisgICAgaW5wdXQ6Y2xvc2UoKQorICAgIHJldHVybiBuaWwsIG1ha2VFcnIgb3IgImNvdWxkIG5vdCBjcmVhdGUgaW1wb3J0ZWQgZmlsZSIKKyAgZW5kCisKKyAgbG9jYWwgZnVuY3Rpb24gY2xlYW51cERlc3RpbmF0aW9uKCkKKyAgICBDYWNoZUZzLnJlbW92ZShkZXN0aW5hdGlvbikKKyAgICBDYWNoZUZzLnByZWZpeCA9IHNhdmVkUHJlZml4CisgIGVuZAorCisgIGxvY2FsIE1ENSA9IHJlcXVpcmUoInNyYy5tb2RzLlN0cmVhbU1ENSIpCisgIGxvY2FsIG1kNSA9IE1ENS5uZXcoKQorICBsb2NhbCB0b3RhbCwgY2h1bmtCeXRlcyA9IDAsIDQgKiAxMDI0ICogMTAyNAorICB3aGlsZSB0cnVlIGRvCisgICAgbG9jYWwgY2h1bmsgPSBpbnB1dDpyZWFkKGNodW5rQnl0ZXMpCisgICAgaWYgbm90IGNodW5rIG9yICNjaHVuayA9PSAwIHRoZW4gYnJlYWsgZW5kCisgICAgbWQ1OnVwZGF0ZShjaHVuaykKKyAgICBsb2NhbCB3cm90ZSwgd3JpdGVFcnIgPSBvdXRwdXQ6d3JpdGUoY2h1bmspCisgICAgaWYgd3JvdGUgPT0gZmFsc2Ugb3Igd3JvdGUgPT0gbmlsIHRoZW4KKyAgICAgIGlucHV0OmNsb3NlKCk7IG91dHB1dDpjbG9zZSgpOyBjbGVhbnVwRGVzdGluYXRpb24oKQorICAgICAgcmV0dXJuIG5pbCwgImNvdWxkIG5vdCBjb3B5IGltcG9ydDogIiAuLiB0 \ No newline at end of file diff --git a/.github/pr-build/large-import.patch.b64.2 b/.github/pr-build/large-import.patch.b64.2 deleted file mode 100644 index e7aac75f..00000000 --- a/.github/pr-build/large-import.patch.b64.2 +++ /dev/null @@ -1 +0,0 @@ -b3N0cmluZyh3cml0ZUVyciBvciAid3JpdGUgZmFpbGVkIikKKyAgICBlbmQKKyAgICB0b3RhbCA9IHRvdGFsICsgI2NodW5rCisgICAgaWYgI2NodW5rIDwgY2h1bmtCeXRlcyB0aGVuIGJyZWFrIGVuZAorICBlbmQKKyAgaW5wdXQ6Y2xvc2UoKQorICBvdXRwdXQ6Y2xvc2UoKQorCisgIGlmIGlucHV0LnNpemUgYW5kIHRvdGFsIH49IGlucHV0LnNpemUgdGhlbgorICAgIGNsZWFudXBEZXN0aW5hdGlvbigpCisgICAgcmV0dXJuIG5pbCwgKCJzb3VyY2UgcmVhZCBlbmRlZCBlYXJseSAoZXhwZWN0ZWQgJWQgYnl0ZXMsIGNvcGllZCAlZCkiKTpmb3JtYXQoaW5wdXQuc2l6ZSwgdG90YWwpCisgIGVuZAorICBsb2NhbCBzdG9yZWRTaXplRXJyID0gUmVxdWlyZWRJbXBvcnRzLnNpemVFcnJvcihzcGVjLCB0b3RhbCwgdHJ1ZSkKKyAgaWYgc3RvcmVkU2l6ZUVyciB0aGVuIGNsZWFudXBEZXN0aW5hdGlvbigpOyByZXR1cm4gbmlsLCBzdG9yZWRTaXplRXJyIGVuZAorICBsb2NhbCBkaWdlc3QgPSBtZDU6ZmluYWwoKQorICBDYWNoZUZzLnByZWZpeCA9IHNhdmVkUHJlZml4CisgIGxvY2FsIGFjY2VwdGVkLCBkZXRhaWwgPSBSZXF1aXJlZEltcG9ydHMuYWNjZXB0U3RvcmVkRGlnZXN0KAorICAgIG1hbmlmZXN0LCBpbXBvcnRJZCwgZGlnZXN0LCBsb3ZlLmZpbGVzeXN0ZW0pCisgIGlmIG5vdCBhY2NlcHRlZCB0aGVuCisgICAgQ2FjaGVGcy5wcmVmaXggPSAiIgorICAgIENhY2hlRnMucmVtb3ZlKGRlc3RpbmF0aW9uKQorICAgIENhY2hlRnMucHJlZml4ID0gc2F2ZWRQcmVmaXgKKyAgICByZXR1cm4gbmlsLCBkZXRhaWwKKyAgZW5kCisgIHJldHVybiB0cnVlLCBkZXRhaWwKK2VuZAorCiBsb2NhbCBmdW5jdGlvbiByZWFkRHJvcHBlZEZpbGUoZmlsZSkKICAgbG9jYWwgb2ssIG9wZW5FcnJvciA9IGZpbGU6b3BlbigiciIpCiAgIGlmIG5vdCBvayB0aGVuIHJldHVybiBuaWwsIG9wZW5FcnJvciBlbmQKQEAgLTEyMjgsMTEgKzEzMjQsMTMgQEAgbG9jYWwgZnVuY3Rpb24gY2hvb3NlUmVxdWlyZWRGaWxlKCkKICAgICAgICIkZD1OZXctT2JqZWN0IFN5c3RlbS5XaW5kb3dzLkZvcm1zLk9wZW5GaWxlRGlhbG9nOyIsCiAgICAgICAiJGQuVGl0bGU9JyIgLi4gcHJvbXB0IC4uICInOyIsCiAgICAgICAiJGQuRmlsdGVyPSdBbGwgZmlsZXMgKCouKil8Ki4qJzsiLAorICAgICAgLS0gUmVxdWlyZWQgaW1wb3J0cyBjYW4gYmUgbXVsdGktZ2lnYWJ5dGUgb3B0aWNhbC1kaXNjIGltYWdlcy4gRG8gTk9UCisgICAgICAtLSBzdGFnZSB0aGVtIHRocm91Z2ggJVRFTVAlOiB0aGF0IGRvdWJsZXMgZnJlZS1zcGFjZSByZXF1aXJlbWVudHMgYW5kIGEKKyAgICAgIC0tIGZhaWxlZCBDb3B5LUl0ZW0gY2FuIGxlYXZlIGEgcGxhdXNpYmxlLWxvb2tpbmcgdHJ1bmNhdGVkIHRlbXAgZmlsZS4KKyAgICAgIC0tIFN0cmVhbSB0aGUgc2VsZWN0ZWQgc291cmNlIGRpcmVjdGx5IGludG8gdGhlIG1vZC1vd25lZCBkZXN0aW5hdGlvbi4KICAgICAgICJpZigkZC5TaG93RGlhbG9nKCkgLWVxICdPSycpeyIsCi0gICAgICAiJHQ9Sm9pbi1QYXRoICRlbnY6VEVNUCAncG9rZXBvcnRfcmVxdWlyZWRfaW1wb3J0LmJpbic7IiwKLSAgICAgICJDb3B5LUl0ZW0gLUxpdGVyYWxQYXRoICRkLkZpbGVOYW1lIC1EZXN0aW5hdGlvbiAkdCAtRm9yY2U7IiwKICAgICAgICJbQ29uc29sZV06Ok91dHB1dEVuY29kaW5nPVtUZXh0LkVuY29kaW5nXTo6VVRGODsiLAotICAgICAgIltDb25zb2xlXTo6V3JpdGUoJHQpfSIsCisgICAgICAiW0NvbnNvbGVdOjpXcml0ZSgkZC5GaWxlTmFtZSl9IiwKICAgICB9KQogICAgIHJldHVybiBjb21tYW5kT3V0cHV0KAogICAgICAgJ3Bvd2Vyc2hlbGwgLU5vUHJvZmlsZSAtU1RBIC1Db21tYW5kICInIC4uIHNjcmlwdCAuLiAnIicpCkBAIC0yMDY5LDggKzIxNzAsMTMgQEAgZnVuY3Rpb24gUm9tSW1wb3J0ZXI6X2ltcG9ydFJlcXVpcmVkU291cmNlKG1vZElkLCBpbXBvcnRJZCwgc291cmNlLCBjb25maXJtZWQpCiAgICAgcmV0dXJuIG5pbAogICBlbmQKICAgbG9jYWwgUmVxdWlyZWRJbXBvcnRzID0gcmVxdWlyZSgic3JjLm1vZHMuUmVxdWlyZWRJbXBvcnRzIikKLSAgbG9jYWwgaW5mbyA9IGxvdmUuZmlsZXN5c3RlbS5nZXRJbmZvKHNvdXJjZSwgImZpbGUiKQotICBsb2NhbCBzaXplID0gaW5mbyBhbmQgaW5mby5zaXplIG9yIGV4dGVybmFsRmlsZVNpemUoc291cmNlKQorICAtLSBBIGRlc2t0b3AgcGlja2VyIHJldHVybnMgYSBob3N0IHBhdGguIEFzayB0aGUgaG9zdCBmaWxlIGhhbmRsZSBmaXJzdDsKKyAgLS0gbG92ZS5maWxlc3lzdGVtLmdldEluZm8gaXMgb25seSBhdXRob3JpdGF0aXZlIGZvciB2aXJ0dWFsL3NhdmUgcGF0aHMuCisgIGxvY2FsIHNpemUgPSBleHRlcm5hbEZpbGVTaXplKHNvdXJjZSkKKyAgaWYgbm90IHNpemUgdGhlbgorICAgIGxvY2FsIGluZm8gPSBsb3ZlLmZpbGVzeXN0ZW0uZ2V0SW5mbyhzb3VyY2UsICJmaWxlIikKKyAgICBzaXplID0gaW5mbyBhbmQgaW5mby5zaXplIG9yIG5pbAorICBlbmQKICAgbG9jYWwgc2l6ZUVyciA9IFJlcXVpcmVkSW1wb3J0cy5zaXplRXJyb3Ioc3BlYywgc2l6ZSwgZmFsc2UpCiAgIGlmIHNpemVFcnIgdGhlbgogICAgIHJlcXVpcmVkSW1wb3J0Tm90aWNlKHNlbGYsIG1vZElkLCBpbXBvcnRJZCwgc2l6ZUVycikKQEAgLTIwOTMsNiArMjE5OSwyMCBAQCBmdW5jdGlvbiBSb21JbXBvcnRlcjpfaW1wb3J0UmVxdWlyZWRTb3VyY2UobW9kSWQsIGltcG9ydElkLCBzb3VyY2UsIGNvbmZpcm1lZCkKICAgICB9CiAgICAgcmV0dXJuIG5pbAogICBlbmQKKyAgaWYgdHlwZShzaXplKSA9PSAibnVtYmVyIiBhbmQgc2l6ZSA+IFJlcXVpcmVkSW1wb3J0cy5MQVJHRV9XQVJOX0JZVEVTCisgICAgICBhbmQgc3BlYy5mb3JtYXQgfj0gIm42NCIgdGhlbgorICAgIGxvY2FsIG9rLCByZXN1bHQgPSBzdHJlYW1SZXF1aXJlZEltcG9ydChtYW5pZmVzdCwgaW1wb3J0SWQsIHNvdXJjZSkKKyAgICBpZiBvayB0aGVuCisgICAgICBzZWxmLnJlcXVpcmVkSW1wb3J0Tm90aWNlID0gbmlsCisgICAgICBzZWxmLm1vZE5vdGljZSA9IHsgb2sgPSB0cnVlLCB0ZXh0ID0gIkltcG9ydGVkICIgLi4gdG9zdHJpbmcoaW1wb3J0SWQpCisgICAgICAgIC4uICIgZm9yICIgLi4gdG9zdHJpbmcobWFuaWZlc3QubmFtZSBvciBtYW5pZmVzdC5pZCkgLi4gIi4iIH0KKyAgICAgIHNlbGY6X3JlZnJlc2hNb2RzKCkKKyAgICAgIHJldHVybiB0cnVlCisgICAgZW5kCisgICAgcmVxdWlyZWRJbXBvcnROb3RpY2Uoc2VsZiwgbW9kSWQsIGltcG9ydElkLCByZXN1bHQpCisgICAgc2VsZi5tb2ROb3RpY2UgPSBuaWwKKyAgICByZXR1cm4gbmlsCisgIGVuZAogICBsb2NhbCBkYXRhID0gbG92ZS5maWxlc3lzdGVtLnJlYWQoc291cmNlKQogICBpZiBub3QgZGF0YSB0aGVuIGRhdGEgPSByZWFkRXh0ZXJuYWxQYXRoKHNvdXJjZSkgZW5kCiAgIGlmIG5vdCBkYXRhIHRoZW4KZGlmZiAtLWdpdCBhL3NyYy9tb2RzL0xvYWRlci5sdWEgYi9zcmMvbW9kcy9Mb2FkZXIubHVhCmluZGV4IDhlYzQ3NjcuLmRkZmMzZjEgMTAwNjQ0Ci0tLSBhL3NyYy9tb2RzL0xvYWRlci5sdWEKKysrIGIvc3JjL21vZHMvTG9hZGVyLmx1YQpAQCAtOTYyLDYgKzk2Miw4IEBAIGZ1bmN0aW9uIExvYWRlcjpfYXBpKG1vZCkKICAgbG9jYWwgU3RvcmFnZSA9IGVuZ2luZVJlcXVpcmUoInNyYy5tb2RzLlN0b3JhZ2UiKQogICBsb2NhbCBzdG9yYWdlID0gU3RvcmFnZSBhbmQgU3RvcmFnZS5uZXcobW9kSWQsIGxvYWRlci5mcykKICAgbG9jYWwgQ2hlY2twb2ludCA9IGVuZ2luZVJlcXVpcmUoInNyYy5jb3JlLkNoZWNrcG9pbnQiKQorICBsb2NhbCBJbXBvcnRBY2Nlc3MgPSBlbmdpbmVSZXF1aXJlKCJzcmMubW9kcy5JbXBvcnRBY2Nlc3MiKQorICBsb2NhbCBpbXBvcnRBcGksIGluc3RhbGxDYWNoZSA9IEltcG9ydEFjY2Vzcy5uZXcobW9kLm1hbmlmZXN0LCBsb2FkZXIuZnMpCiAgIGxvY2FsIGFwaSA9IHsKICAgICBpZCA9IG1vZElkLAogICAgIHZlcnNpb24gPSBtb2QubWFuaWZlc3QudmVyc2lvbiwKQEAgLTExNjEsNiArMTE2MywxMiBAQCBmdW5jdGlvbiBMb2FkZXI6X2FwaShtb2QpCiAgICAgLS0gY2hlY2twb2ludC4gVGhlCiAgICAgLS0gZW5naW5lIGJpbmRzIHZlcnNpb24vcGxheXRocm91Z2gvbW9kIHNjb3BlIGFuZCBwb3J0YWJsZSBwZXJzaXN0ZW5jZTsKICAgICAtLSBjYWxsZXJzIG5ldmVyIHJlY2VpdmUgcGF0aHMgb3IgYSByYXcgZmlsZXN5c3RlbSBoYW5kbGUuCisgICAgLS0gUmVhZC1vbmx5IGJvdW5kZWQgYWNjZXNzIHRvIHRoaXMgbW9kJ3MgbWFuaWZlc3QtZGVjbGFyZWQsIGxhdW5jaGVyLXZhbGlkYXRlZAorICAgIC0tIGltcG9ydHMuIE5vIGhvc3QgcGF0aCBpcyBleHBvc2VkOyBsYXJnZSBzb3VyY2VzIGFyZSByZWFkIGluIGJvdW5kZWQgcmFuZ2VzLgorICAgIGltcG9ydHMgPSBpbXBvcnRBcGksCisgICAgLS0gSW5zdGFsbGF0aW9uLXNjb3BlZCBnZW5lcmF0ZWQgZGF0YSwgaW5kZXBlbmRlbnQgZnJvbSBQb2vDqW1vbiBzYXZlIHNsb3RzLgorICAgIC0tIFRoaXMgaXMgd2hlcmUgUk9NLWRlcml2ZWQgY2FjaGVzIGJlbG9uZzsgbW9kLnN0b3JhZ2UgcmVtYWlucyBwbGF5dGhyb3VnaC1zY29wZWQuCisgICAgY2FjaGUgPSBpbnN0YWxsQ2FjaGUsCiAgICAgc3RvcmFnZSA9IHsKICAgICAgIGNvbnRleHQgPSBmdW5jdGlvbihfLCBnYW1lKSByZXR1cm4gc3RvcmFnZTpjb250ZXh0KGdhbWUpIGVuZCwKICAgICAgIHNlbGVjdGVkID0gZnVuY3Rpb24oXywgZ2FtZSkgcmV0dXJuIHN0b3JhZ2U6c2VsZWN0ZWQoZ2FtZSkgZW5kLApkaWZmIC0tZ2l0IGEvc3JjL21vZHMvUmVxdWlyZWRJbXBvcnRzLmx1YSBiL3NyYy9tb2RzL1JlcXVpcmVkSW1wb3J0cy5sdWEKaW5kZXggODA2ZTkxNi4uMjVlOGFhOCAxMDA2NDQKLS0tIGEvc3JjL21vZHMvUmVxdWlyZWRJbXBvcnRzLmx1YQorKysgYi9zcmMvbW9kcy9SZXF1aXJlZEltcG9ydHMubHVhCkBAIC0xMjAsNiArMTIwLDM2IEBAIGxvY2FsIGZ1bmN0aW9uIGFjY2VwdHMoc3BlYywgZGlnZXN0KQogICByZXR1cm4gZmFsc2UKIGVuZAogCitsb2NhbCBmdW5jdGlvbiBzcGVjQnlJZChtYW5pZmVzdCwgaW1wb3J0SWQpCisgIGZvciBfLCBjYW5kaWRhdGUgaW4gaXBhaXJzKGFsbFNwZWNzKG1hbmlmZXN0KSkgZG8KKyAgICBpZiBjYW5kaWRhdGUuaWQgPT0gaW1wb3J0SWQgdGhlbiByZXR1cm4gY2FuZGlkYXRlIGVuZAorICBlbmQKKyAgcmV0dXJuIG5pbAorZW5kCisKK1JlcXVpcmVkSW1wb3J0cy5zcGVjID0gc3BlY0J5SWQKKworbG9jYWwgZnVuY3Rpb24gc3RyZWFtRGlnZXN0KGZzLCBwYXRoLCBjaHVua0J5dGVzKQorICBpZiBub3QgKGZzIGFuZCBmcy5uZXdGaWxlKSB0aGVuIHJldHVybiBuaWwsICJzdHJlYW1pbmcgZmlsZSBhY2Nlc3MgaXMgdW5hdmFpbGFibGUiIGVuZAorICBsb2NhbCBmaWxlLCBtYWtlRXJyID0gZnMubmV3RmlsZShwYXRoKQorICBpZiBub3QgZmlsZSB0aGVuIHJldHVybiBuaWwsIG1ha2VFcnIgb3IgImNvdWxkIG5vdCBvcGVuIHN0b3JlZCBpbXBvcnQiIGVuZAorICBsb2NhbCBvaywgb3BlbkVyciA9IGZpbGU6b3BlbigiciIpCisgIGlmIG5vdCBvayB0aGVuIHJldHVybiBuaWwsIG9wZW5FcnIgb3IgImNvdWxkIG5vdCBvcGVuIHN0b3JlZCBpbXBvcnQiIGVuZAorICBsb2NhbCBNRDUgPSByZXF1aXJlKCJzcmMubW9kcy5TdHJlYW1NRDUiKQorICBsb2NhbCBjdHggPSBNRDUubmV3KCkKKyAgY2h1bmtCeXRlcyA9IGNodW5rQnl0ZXMgb3IgKDQgKiAxMDI0ICogMTAyNCkKKyAgd2hpbGUgdHJ1ZSBkbworICAgIGxvY2FsIGRhdGEsIHJlYWRFcnIgPSBmaWxlOnJlYWQoY2h1bmtCeXRlcykKKyAgICBpZiBkYXRhIGFuZCAjZGF0YSA+IDAgdGhlbiBjdHg6dXBkYXRlKGRhdGEpIGVuZAorICAgIGlmIG5vdCBkYXRhIG9yICNkYXRhIDwgY2h1bmtCeXRlcyB0aGVuCisgICAgICBpZiByZWFkRXJyIHRoZW4gZmlsZTpjbG9zZSgpOyByZXR1cm4gbmlsLCByZWFkRXJyIGVuZAorICAgICAgYnJlYWsKKyAgICBlbmQKKyAgZW5kCisgIGZpbGU6Y2xvc2UoKQorICByZXR1cm4gY3R4OmZpbmFsKCkKK2VuZAorCiBmdW5jdGlvbiBSZXF1aXJlZEltcG9ydHMucGF0aChtYW5pZmVzdCwgc3BlYykKICAgcmV0dXJuIG1hbmlmZXN0LnBhdGggLi4gIi9iYXNlcm9tcy8iIC4uIHNwZWMuZmlsZQogZW5kCkBAIC0xODAsNiArMjEwLDM1IEBAIGxvY2FsIGZ1bmN0aW9uIHJlbW92ZVJlY2VpcHQobWFuaWZlc3QsIHNwZWMsIGZzKQogICBlbmQKIGVuZAogCistLSBGaW5hbGl6ZSBhIGNhbGxlci1zdHJlYW1lZCBpbXBvcnQgYWZ0ZXIgdGhlIGRlc3RpbmF0aW9uIGJ5dGVzIGhhdmUgYWxyZWFkeQorLS0gYmVlbiBjb3BpZWQgaW50byB0aGUgZW5naW5lLW93bmVkIGJhc2Vyb21zIHBhdGguIFRoaXMga2VlcHMgbGFyZ2UgaW1wb3J0cworLS0gb3V0IG9mIGEgc2luZ2xlIEx1YSBzdHJpbmcgd2hpbGUgcHJlc2VydmluZyB0aGUgc2FtZSBzaXplL01ENSByZWNlaXB0IHJ1bGVzLgorZnVuY3Rpb24gUmVxdWlyZWRJbXBvcnRzLmFjY2VwdFN0b3JlZERpZ2VzdChtYW5pZmVzdCwgaW1wb3J0SWQsIGRpZ2VzdCwgZnMpCisgIGZzID0gZnMgb3IgKGxvdmUgYW5kIGxvdmUuZmlsZXN5c3RlbSkKKyAgbG9jYWwgc3BlYyA9IHNwZWNCeUlkKG1hbmlmZXN0LCBpbXBvcnRJZCkKKyAgaWYgbm90IHNwZWMgdGhlbiByZXR1cm4gbmlsLCAidW5rbm93biByZXF1aXJlZCBpbXBvcnQ6ICIgLi4gdG9zdHJpbmcoaW1wb3J0SWQpIGVuZAorICBkaWdlc3QgPSB0b3N0cmluZyhkaWdlc3Qgb3IgIiIpOmxvd2VyKCkKKyAgaWYgbm90IGFjY2VwdHMoc3BlYywgZGlnZXN0KSB0aGVuCisgICAgcmV0dXJuIG5pbCwgKCJNRDUgbWlzbWF0Y2ggKGdvdCAlcykiKTpmb3JtYXQoZGlnZXN0IH49ICIiIGFuZCBkaWdlc3Qgb3IgInVuYXZhaWxhYmxlIikKKyAgZW5kCisgIGxvY2FsIHBhdGggPSBSZXF1aXJlZEltcG9ydHMucGF0aChtYW5pZmVzdCwgc3BlYykKKyAgbG9jYWwgaW5mbyA9IGZzIGFuZCBmcy5nZXRJbmZvIGFuZCBmcy5nZXRJbmZvKHBhdGgsICJmaWxlIikgb3IgbmlsCisgIGlmIG5vdCBpbmZvIHRoZW4gcmV0dXJuIG5pbCwgImNvcGllZCBpbXBvcnQgaXMgbWlzc2luZyIgZW5kCisgIGxvY2FsIHNpemVFcnIgPSBSZXF1aXJlZEltcG9ydHMuc2l6ZUVycm9yKHNwZWMsIGluZm8uc2l6ZSwgdHJ1ZSkKKyAgaWYgc2l6ZUVyciB0aGVuIHJldHVybiBuaWwsIHNpemVFcnIgZW5kCisgIGlmIGxvdmUgYW5kIGZzID09IGxvdmUuZmlsZXN5c3RlbSB0aGVuCisgICAgbG9jYWwgc2F2ZWRQcmVmaXggPSBDYWNoZUZzLnByZWZpeAorICAgIENhY2hlRnMucHJlZml4ID0gIiIKKyAgICBDYWNoZUZzLnJlbW92ZShyZW1vdmVkTWFya2VyKG1hbmlmZXN0LCBzcGVjKSkKKyAgICBDYWNoZUZzLnByZWZpeCA9IHNhdmVkUHJlZml4CisgIGVsc2VpZiBmcyBhbmQgZnMucmVtb3ZlIHRoZW4KKyAgICBmcy5yZW1vdmUocmVtb3ZlZE1hcmtlcihtYW5pZmVzdCwgc3BlYykpCisgIGVuZAorICB3cml0ZVJlY2VpcHQobWFuaWZlc3QsIHNwZWMsIGRpZ2VzdCwgaW5mbywgZnMpCisgIHJldHVybiB0cnVlLCBkaWdlc3QKK2VuZAorCisKIC0tIFZhbGlkYXRlIGJ5dGVzIGFnYWluc3QgYSBkZWNsYXJhdGlvbi4gIFRoZSByZXR1cm5lZCBkYXRhIGlzIGNhbm9uaWNhbGl6ZWQKIC0tIChub3RhYmx5IGZvciBONjQgYnl0ZSBvcmRlci9oZWFkZXIgdmFyaWFudHMpIGFuZCBpcyB3aGF0IG11c3QgYmUgc3RvcmVkLgogZnVuY3Rpb24gUmVxdWlyZWRJbXBvcnRzLnZhbGlkYXRlRGF0YShzcGVjLCBkYXRhLCBoYXNoRm4pCkBAIC0yMTcsNiArMjc2LDIyIEBAIGZ1bmN0aW9uIFJlcXVpcmVkSW1wb3J0cy52YWxpZGF0ZVN0b3JlZChtYW5pZmVzdCwgc3BlYywgZnMsIGhhc2hGbikKICAgbG9jYWwgY2FjaGVkID0gY2FjaGVkRGlnZXN0KG1hbmlmZXN0LCBzcGVjLCBmcywgaW5mbykKICAgaWYgY2FjaGVkIHRoZW4gcmV0dXJuIHRydWUsIGNhY2hlZCwgdHJ1ZSBlbmQKICAgcmVtb3ZlUmVjZWlwdChtYW5pZmVzdCwgc3BlYywgZnMpCisgIC0tIExhcmdlIHJhdyBpbXBvcnRzIChHYW1lQ3ViZSBkaXNjcywgZnV0dXJlIG9wdGljYWwgaW1hZ2VzLCBldGMuKSBtdXN0IG5ldmVyCisgIC0tIGJlIG1hdGVyaWFsaXplZCBpbnRvIG9uZSBMdWEgc3RyaW5nIG1lcmVseSBiZWNhdXNlIHRoZWlyIHZhbGlkYXRpb24KKyAgLS0gcmVjZWlwdCB3YXMgbG9zdC4gU3RyZWFtIHRoZSBNRDUgZGlyZWN0bHkgZnJvbSB0aGUgaW5zdGFsbGVkIGZpbGUuIE42NAorICAtLSBzb3VyY2VzIHN0YXkgb24gdGhlIGNhbm9uaWNhbGl6YXRpb24gcGF0aCBiZWNhdXNlIGJ5dGUtb3JkZXIvaGVhZGVyCisgIC0tIG5vcm1hbGl6YXRpb24gaXMgcGFydCBvZiB0aGVpciB2YWxpZGF0aW9uIGNvbnRyYWN0LgorICBpZiBpbmZvLnNpemUgYW5kIGluZm8uc2l6ZSA+IFJlcXVpcmVkSW1wb3J0cy5MQVJHRV9XQVJOX0JZVEVTCisgICAgICBhbmQgc3BlYy5mb3JtYXQgfj0gIm42NCIgYW5kIGZzLm5ld0ZpbGUgdGhlbgorICAgIGxvY2FsIGRpZ2VzdCwgaGFzaEVyciA9IHN0cmVhbURpZ2VzdChmcywgcGF0aCkKKyAgICBpZiBub3QgZGlnZXN0IHRoZW4gcmV0dXJuIG5pbCwgaGFzaEVyciBlbmQKKyAgICBpZiBub3QgYWNjZXB0cyhzcGVjLCBkaWdlc3QpIHRoZW4KKyAgICAgIHJldHVybiBuaWwsICgiTUQ1IG1pc21hdGNoIChnb3QgJXMpIik6Zm9ybWF0KGRpZ2VzdCkKKyAgICBlbmQKKyAgICBpbmZvID0gZnMuZ2V0SW5mbyhwYXRoLCAiZmlsZSIpIG9yIGluZm8KKyAgICB3cml0ZVJlY2VpcHQobWFuaWZlc3QsIHNwZWMsIGRpZ2VzdCwgaW5mbywgZnMpCisgICAgcmV0dXJuIHRydWUsIGRpZ2VzdCwgZmFsc2UKKyAgZW5kCiAgIGlmIG5vdCBmcy5yZWFkIHRoZW4gcmV0dXJuIG5pbCwgImZpbGUgY291bGQgbm90IGJlIHJlYWQiIGVuZAogICBsb2NhbCBkYXRhID0gZnMucmVhZChwYXRoKQogICBsb2NhbCBub3JtYWxpemVkLCBkZXRhaWwgPSBSZXF1aXJlZEltcG9ydHMudmFsaWRhdGVTdG9yZWREYXRhKHNwZWMsIGRhdGEsIGhhc2hGbikK \ No newline at end of file diff --git a/.github/workflows/apply-large-import-pr.yml b/.github/workflows/apply-large-import-pr.yml deleted file mode 100644 index 1ec658d0..00000000 --- a/.github/workflows/apply-large-import-pr.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Apply prepared large-import patch - -on: - push: - branches: - - fix/large-required-imports-v2 - -permissions: - contents: write - -jobs: - apply: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Reconstruct and apply patch - shell: bash - run: | - set -euo pipefail - base64 -d .github/pr-build/large-import.patch.b64.1 > /tmp/large-import.patch - base64 -d .github/pr-build/large-import.patch.b64.2 >> /tmp/large-import.patch - git apply --check /tmp/large-import.patch - git apply /tmp/large-import.patch - rm -rf .github/pr-build - rm -f .github/workflows/apply-large-import-pr.yml - - name: Commit prepared change - shell: bash - run: | - set -euo pipefail - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git diff --cached --check - git commit -m "fix(mods): support large required imports end-to-end" - git push origin HEAD:fix/large-required-imports-v2 diff --git a/docs/modding.md b/docs/modding.md index 4016e1f1..25e7596d 100644 --- a/docs/modding.md +++ b/docs/modding.md @@ -122,21 +122,40 @@ Each object requires a stable `id`, a display `name`, a destination `file` digests. `format` is either `"raw"` (the default) or `"n64"`. An optional `description` gives players dump or region guidance in the import panel. `size` declares the exact canonical byte length; `max_size` declares a smaller -per-import ceiling when an exact size is not appropriate. Every import also -has an engine-enforced 128 MiB ceiling and is rejected before hashing when its -filesystem reports an invalid size. +per-import ceiling when an exact size is not appropriate. The engine hard limit +is 2 GiB. Imports above 128 MiB receive an explicit free-space confirmation and +use the launcher's streaming large-file path rather than being materialized as +one Lua string. For `"n64"`, the launcher recognizes `.z64`, `.v64`, and `.n64` byte orders, strips a recognized 512-byte copier header, converts the bytes to canonical big-endian `.z64` order, and then checks MD5. The canonical bytes are written to `mods//baseroms/`. Each selection is a private grant to that mod: the launcher never scans or copies another mod's imported files merely -because its manifest names the same digest. Mods read the result with their existing scoped `mod:read` API, for -example `mod:read("baseroms/stadium2.z64")`; no host path or new filesystem +because its manifest names the same digest. Small sources can still be read +with the existing scoped `mod:read` API, for example +`mod:read("baseroms/stadium2.z64")`. For large sources, prefer the bounded +`mod.imports` facade described below; no host path or new general filesystem permission is exposed. Missing `required_imports` block the mod before its entry chunk runs; missing `optional_imports` remain visible in the same launcher panel but do not block loading. +#### Bounded access to validated imports + +A loaded mod can address only ids declared by its own `required_imports` or +`optional_imports` arrays: + +```lua +local info, err = mod.imports:info("stadium2") +local header, err = mod.imports:read("stadium2", 0, 4096) +``` + +`read` uses zero-based offsets and is capped at 8 MiB per call. The engine +rechecks the stored import before exposing it, seeks into the engine-owned +copy, and never gives the mod a host path or file handle. This is intended for +large source formats whose table/index can be parsed with small reads before +selectively reading the payloads a transform actually needs. + MD5 here identifies a known dump because ROM databases commonly publish it; it is not a security or authenticity guarantee. Do not paste the SHA-1 used by Gen1Recomp's own game-ROM importer into an import's `md5` field. Mod archives @@ -441,6 +460,28 @@ default** (1x front, 2x back). ball-to-pic grow multiplies your scale through each stage, so a rescaled mon still grows into place from the ball, grounded the whole way. +## Installation-scoped generated cache + +Generated data derived from a validated user source often belongs to the mod +installation rather than to one Pokémon save. `mod.cache` is that namespace: + +```lua +local ok, err = mod.cache:write("extract/v1/arena.bin", encodedArena) +local bytes, err = mod.cache:read("extract/v1/arena.bin") +local info = mod.cache:info("extract/v1/arena.bin") +mod.cache:delete("extract/v1/arena.bin") +``` + +The physical root is engine-owned (`mod_cache//`) and never exposed to +the mod. Keys are safe relative paths and a single write is capped at 64 MiB. +The cache does not rewind with checkpoints and is not scoped to game version, +slot, or playthrough. The mod owns its generated format, fingerprints, rebuild +policy, and completion marker; the engine treats the bytes as opaque data. + +Use `mod.storage` instead when the data belongs to one playthrough. Use +`mod.cache` when it is a reproducible installation artifact that can be rebuilt +from a declared user source. + ## Durable tool storage and runtime checkpoints `mod.save` remains the right place for state that should travel with the next diff --git a/src/import/CacheFs.lua b/src/import/CacheFs.lua index a3bfd889..00ef8e99 100644 --- a/src/import/CacheFs.lua +++ b/src/import/CacheFs.lua @@ -287,6 +287,43 @@ function CacheFs.write(rel, data) return love.filesystem.write(rel, data) end +-- Open a cache-relative file for streaming replacement. The returned handle +-- has write(bytes) and close() methods and follows the same portable/save-dir +-- routing as CacheFs.write without forcing the caller to hold the whole file +-- in one Lua string. +function CacheFs.openWrite(rel) + rel = withPrefix(rel) + local root = CacheFs.root() + if root then + ensureParents(root, rel) + local f, err = io.open(realPath(root, rel), "wb") + if not f then return nil, err end + return { + write = function(_, data) + local ok, writeErr = f:write(data) + if not ok then return nil, writeErr end + return true + end, + close = function() f:close() end, + } + end + if not (love and love.filesystem and love.filesystem.newFile) then + return nil, "streaming cache writes are unavailable" + end + local parent = rel:match("^(.*)/[^/]+$") + if parent and not love.filesystem.createDirectory(parent) then + local info = love.filesystem.getInfo(parent) + local reason = info and ("a " .. info.type .. " already exists there") + or "unknown reason" + return nil, "could not create " .. parent .. ": " .. reason + end + local file, makeErr = love.filesystem.newFile(rel) + if not file then return nil, makeErr or "could not create cache file" end + local ok, openErr = file:open("w") + if not ok then return nil, openErr or "could not open cache file" end + return file +end + -- read cache-relative `rel`; returns the bytes or nil function CacheFs.read(rel) rel = withPrefix(rel) diff --git a/src/import/RomImporter.lua b/src/import/RomImporter.lua index d5b4aecf..446bb907 100644 --- a/src/import/RomImporter.lua +++ b/src/import/RomImporter.lua @@ -382,6 +382,102 @@ local function externalFileSize(path) return size end +local function openImportSource(path) + -- Desktop picker paths live outside LÖVE's virtual filesystem. Prefer the + -- native file handle so a 1.46 GiB disc is never copied to a temp file or + -- read into one Lua string before validation. + local native = io.open(path, "rb") + if native then + local size = native:seek("end") + if size then native:seek("set", 0) end + return { + size = size, + read = function(_, n) return native:read(n) end, + close = function() native:close() end, + } + end + if love and love.filesystem and love.filesystem.newFile then + local file, makeErr = love.filesystem.newFile(path) + if not file then return nil, makeErr or "could not open source file" end + local ok, openErr = file:open("r") + if not ok then return nil, openErr or "could not open source file" end + local size = file.getSize and file:getSize() or nil + return { + size = size, + read = function(_, n) return file:read(n) end, + close = function() file:close() end, + } + end + return nil, "streaming source access is unavailable" +end + +local function streamRequiredImport(manifest, importId, source) + local RequiredImports = require("src.mods.RequiredImports") + local spec = RequiredImports.spec(manifest, importId) + if not spec then return nil, "Import declaration was not found." end + if spec.format == "n64" then + return nil, "streaming canonicalization is unavailable for N64 imports" + end + local input, openErr = openImportSource(source) + if not input then return nil, openErr end + local sizeErr = RequiredImports.sizeError(spec, input.size, false) + if sizeErr then input:close(); return nil, sizeErr end + + local CacheFs = require("src.import.CacheFs") + local destination = RequiredImports.path(manifest, spec) + local savedPrefix = CacheFs.prefix + CacheFs.prefix = "" + CacheFs.remove(RequiredImports.receiptPath(manifest, spec)) + CacheFs.remove(destination) + local output, makeErr = CacheFs.openWrite(destination) + if not output then + CacheFs.prefix = savedPrefix + input:close() + return nil, makeErr or "could not create imported file" + end + + local function cleanupDestination() + CacheFs.remove(destination) + CacheFs.prefix = savedPrefix + end + + local MD5 = require("src.mods.StreamMD5") + local md5 = MD5.new() + local total, chunkBytes = 0, 4 * 1024 * 1024 + while true do + local chunk = input:read(chunkBytes) + if not chunk or #chunk == 0 then break end + md5:update(chunk) + local wrote, writeErr = output:write(chunk) + if wrote == false or wrote == nil then + input:close(); output:close(); cleanupDestination() + return nil, "could not copy import: " .. tostring(writeErr or "write failed") + end + total = total + #chunk + if #chunk < chunkBytes then break end + end + input:close() + output:close() + + if input.size and total ~= input.size then + cleanupDestination() + return nil, ("source read ended early (expected %d bytes, copied %d)"):format(input.size, total) + end + local storedSizeErr = RequiredImports.sizeError(spec, total, true) + if storedSizeErr then cleanupDestination(); return nil, storedSizeErr end + local digest = md5:final() + CacheFs.prefix = savedPrefix + local accepted, detail = RequiredImports.acceptStoredDigest( + manifest, importId, digest, love.filesystem) + if not accepted then + CacheFs.prefix = "" + CacheFs.remove(destination) + CacheFs.prefix = savedPrefix + return nil, detail + end + return true, detail +end + local function readDroppedFile(file) local ok, openError = file:open("r") if not ok then return nil, openError end @@ -1228,11 +1324,13 @@ local function chooseRequiredFile() "$d=New-Object System.Windows.Forms.OpenFileDialog;", "$d.Title='" .. prompt .. "';", "$d.Filter='All files (*.*)|*.*';", + -- Required imports can be multi-gigabyte optical-disc images. Do NOT + -- stage them through %TEMP%: that doubles free-space requirements and a + -- failed Copy-Item can leave a plausible-looking truncated temp file. + -- Stream the selected source directly into the mod-owned destination. "if($d.ShowDialog() -eq 'OK'){", - "$t=Join-Path $env:TEMP 'pokeport_required_import.bin';", - "Copy-Item -LiteralPath $d.FileName -Destination $t -Force;", "[Console]::OutputEncoding=[Text.Encoding]::UTF8;", - "[Console]::Write($t)}", + "[Console]::Write($d.FileName)}", }) return commandOutput( 'powershell -NoProfile -STA -Command "' .. script .. '"') @@ -2072,8 +2170,13 @@ function RomImporter:_importRequiredSource(modId, importId, source, confirmed) return nil end local RequiredImports = require("src.mods.RequiredImports") - local info = love.filesystem.getInfo(source, "file") - local size = info and info.size or externalFileSize(source) + -- A desktop picker returns a host path. Ask the host file handle first; + -- love.filesystem.getInfo is only authoritative for virtual/save paths. + local size = externalFileSize(source) + if not size then + local info = love.filesystem.getInfo(source, "file") + size = info and info.size or nil + end local sizeErr = RequiredImports.sizeError(spec, size, false) if sizeErr then requiredImportNotice(self, modId, importId, sizeErr) @@ -2096,6 +2199,20 @@ function RomImporter:_importRequiredSource(modId, importId, source, confirmed) } return nil end + if type(size) == "number" and size > RequiredImports.LARGE_WARN_BYTES + and spec.format ~= "n64" then + local ok, result = streamRequiredImport(manifest, importId, source) + if ok then + self.requiredImportNotice = nil + self.modNotice = { ok = true, text = "Imported " .. tostring(importId) + .. " for " .. tostring(manifest.name or manifest.id) .. "." } + self:_refreshMods() + return true + end + requiredImportNotice(self, modId, importId, result) + self.modNotice = nil + return nil + end local data = love.filesystem.read(source) if not data then data = readExternalPath(source) end if not data then diff --git a/src/mods/Loader.lua b/src/mods/Loader.lua index 8ec4767d..ddfc3f12 100644 --- a/src/mods/Loader.lua +++ b/src/mods/Loader.lua @@ -962,6 +962,8 @@ function Loader:_api(mod) local Storage = engineRequire("src.mods.Storage") local storage = Storage and Storage.new(modId, loader.fs) local Checkpoint = engineRequire("src.core.Checkpoint") + local ImportAccess = engineRequire("src.mods.ImportAccess") + local importApi, installCache = ImportAccess.new(mod.manifest, loader.fs) local api = { id = modId, version = mod.manifest.version, @@ -1161,6 +1163,12 @@ function Loader:_api(mod) -- checkpoint. The -- engine binds version/playthrough/mod scope and portable persistence; -- callers never receive paths or a raw filesystem handle. + -- Read-only bounded access to this mod's manifest-declared, launcher-validated + -- imports. No host path is exposed; large sources are read in bounded ranges. + imports = importApi, + -- Installation-scoped generated data, independent from Pokémon save slots. + -- This is where ROM-derived caches belong; mod.storage remains playthrough-scoped. + cache = installCache, storage = { context = function(_, game) return storage:context(game) end, selected = function(_, game) return storage:selected(game) end, diff --git a/src/mods/RequiredImports.lua b/src/mods/RequiredImports.lua index 806e916e..25e8aa8c 100644 --- a/src/mods/RequiredImports.lua +++ b/src/mods/RequiredImports.lua @@ -120,6 +120,36 @@ local function accepts(spec, digest) return false end +local function specById(manifest, importId) + for _, candidate in ipairs(allSpecs(manifest)) do + if candidate.id == importId then return candidate end + end + return nil +end + +RequiredImports.spec = specById + +local function streamDigest(fs, path, chunkBytes) + if not (fs and fs.newFile) then return nil, "streaming file access is unavailable" end + local file, makeErr = fs.newFile(path) + if not file then return nil, makeErr or "could not open stored import" end + local ok, openErr = file:open("r") + if not ok then return nil, openErr or "could not open stored import" end + local MD5 = require("src.mods.StreamMD5") + local ctx = MD5.new() + chunkBytes = chunkBytes or (4 * 1024 * 1024) + while true do + local data, readErr = file:read(chunkBytes) + if data and #data > 0 then ctx:update(data) end + if not data or #data < chunkBytes then + if readErr then file:close(); return nil, readErr end + break + end + end + file:close() + return ctx:final() +end + function RequiredImports.path(manifest, spec) return manifest.path .. "/baseroms/" .. spec.file end @@ -180,6 +210,35 @@ local function removeReceipt(manifest, spec, fs) end end +-- Finalize a caller-streamed import after the destination bytes have already +-- been copied into the engine-owned baseroms path. This keeps large imports +-- out of a single Lua string while preserving the same size/MD5 receipt rules. +function RequiredImports.acceptStoredDigest(manifest, importId, digest, fs) + fs = fs or (love and love.filesystem) + local spec = specById(manifest, importId) + if not spec then return nil, "unknown required import: " .. tostring(importId) end + digest = tostring(digest or ""):lower() + if not accepts(spec, digest) then + return nil, ("MD5 mismatch (got %s)"):format(digest ~= "" and digest or "unavailable") + end + local path = RequiredImports.path(manifest, spec) + local info = fs and fs.getInfo and fs.getInfo(path, "file") or nil + if not info then return nil, "copied import is missing" end + local sizeErr = RequiredImports.sizeError(spec, info.size, true) + if sizeErr then return nil, sizeErr end + if love and fs == love.filesystem then + local savedPrefix = CacheFs.prefix + CacheFs.prefix = "" + CacheFs.remove(removedMarker(manifest, spec)) + CacheFs.prefix = savedPrefix + elseif fs and fs.remove then + fs.remove(removedMarker(manifest, spec)) + end + writeReceipt(manifest, spec, digest, info, fs) + return true, digest +end + + -- Validate bytes against a declaration. The returned data is canonicalized -- (notably for N64 byte order/header variants) and is what must be stored. function RequiredImports.validateData(spec, data, hashFn) @@ -217,6 +276,22 @@ function RequiredImports.validateStored(manifest, spec, fs, hashFn) local cached = cachedDigest(manifest, spec, fs, info) if cached then return true, cached, true end removeReceipt(manifest, spec, fs) + -- Large raw imports (GameCube discs, future optical images, etc.) must never + -- be materialized into one Lua string merely because their validation + -- receipt was lost. Stream the MD5 directly from the installed file. N64 + -- sources stay on the canonicalization path because byte-order/header + -- normalization is part of their validation contract. + if info.size and info.size > RequiredImports.LARGE_WARN_BYTES + and spec.format ~= "n64" and fs.newFile then + local digest, hashErr = streamDigest(fs, path) + if not digest then return nil, hashErr end + if not accepts(spec, digest) then + return nil, ("MD5 mismatch (got %s)"):format(digest) + end + info = fs.getInfo(path, "file") or info + writeReceipt(manifest, spec, digest, info, fs) + return true, digest, false + end if not fs.read then return nil, "file could not be read" end local data = fs.read(path) local normalized, detail = RequiredImports.validateStoredData(spec, data, hashFn)