diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..591b01c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,22 @@ +name: Build pierport and test it + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + docker-tests: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v1 + - name: Cache pierpot_vere_cache + uses: actions/cache@v3 + with: + key: vere-cache + path: pierport_vere_cache + - run: docker build . -t pierport + - run: sh scripts/test.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08b2d47 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/pierport_vere_cache diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8f5ed1e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2696 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +dependencies = [ + "backtrace", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +dependencies = [ + "concurrent-queue", + "event-listener 5.2.0", + "event-listener-strategy 0.5.1", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "bzip2", + "flate2", + "futures-core", + "futures-io", + "memchr", + "pin-project-lite", + "xz2", + "zstd 0.11.2+zstd.1.5.2", + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "async-compression" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86a9249d1447a85f95810c620abea82e001fe58a31713fcce614caf52499f905" +dependencies = [ + "futures-core", + "futures-io", + "memchr", + "pin-project-lite", + "zstd 0.13.1", + "zstd-safe 7.1.0", +] + +[[package]] +name = "async-executor" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10b3e585719c2358d2660232671ca8ca4ddb4be4ce8a1842d6c2dc8685303316" +dependencies = [ + "async-lock 3.3.0", + "async-task", + "concurrent-queue", + "fastrand 2.0.2", + "futures-lite 2.3.0", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.2.0", + "async-executor", + "async-io 2.3.2", + "async-lock 3.3.0", + "blocking", + "futures-lite 2.3.0", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2 0.4.10", + "waker-fn", +] + +[[package]] +name = "async-io" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +dependencies = [ + "async-lock 3.3.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.3.0", + "parking", + "polling 3.6.0", + "rustix 0.38.32", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", + "pin-project-lite", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io 1.13.0", + "async-lock 2.8.0", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 1.13.0", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-tar" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c49359998a76e32ef6e870dbc079ebad8f1e53e8441c5dd39d27b44493fe331" +dependencies = [ + "async-std", + "filetime", + "libc", + "pin-project", + "redox_syscall 0.2.16", + "xattr 0.2.3", +] + +[[package]] +name = "async-task" +version = "4.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" + +[[package]] +name = "async-trait" +version = "0.1.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "async_zip" +version = "0.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "795310de3218cde15219fc98c1cf7d8fe9db4865aab27fcf1d535d6cb61c6b54" +dependencies = [ + "async-compression 0.3.15", + "chrono", + "crc32fast", + "futures-util", + "log", + "pin-project", + "thiserror", + "tokio", + "tokio-util", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[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 = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel 2.2.0", + "async-lock 3.3.0", + "async-task", + "fastrand 2.0.2", + "futures-io", + "futures-lite 2.3.0", + "piper", + "tracing", +] + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.52.4", +] + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[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 = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +dependencies = [ + "event-listener 5.2.0", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand 2.0.2", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "h2" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.6", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "9.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +dependencies = [ + "base64", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +dependencies = [ + "value-bag", +] + +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "octocrab" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfeeafb5fa0da7046229ec3c7b3bd2981aae05c549871192c408d59fc0fffd5" +dependencies = [ + "arc-swap", + "async-trait", + "base64", + "bytes", + "cfg-if", + "chrono", + "either", + "futures", + "http", + "http-body", + "hyper", + "hyper-rustls", + "jsonwebtoken", + "once_cell", + "percent-encoding", + "pin-project", + "secrecy", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "snafu", + "tower", + "tower-http", + "url", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "pem" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +dependencies = [ + "base64", + "serde", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pierport" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-compression 0.4.7", + "async-tar", + "async-trait", + "async_zip", + "axum", + "clap", + "env_logger", + "flate2", + "futures", + "hyper", + "jobserver", + "jsonwebtoken", + "log", + "octocrab", + "once_cell", + "reqwest", + "serde", + "serde_json", + "sha256", + "tar", + "tokio", + "tokio-util", + "toml", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.2", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 0.38.32", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys 0.4.13", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha256" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2", + "tokio", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "snafu" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" +dependencies = [ + "backtrace", + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr 1.3.1", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +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.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.6", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "bitflags 2.5.0", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[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.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "value-bag" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.57", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[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-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys 0.4.13", + "rustix 0.38.32", +] + +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe 7.1.0", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.10+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7b94732 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "pierport" +version = "0.1.0" +edition = "2021" +rust-version = "1.76" +authors = ["Aurimas Blažulionis "] +license = "MIT" +repository = "https://github.com/ChorusOne/pierport" +documentation = "https://docs.rs/pierport" +description = "Urbit pier import protocol implementation" +keywords = [ "urbit" ] +categories = [ "asynchronous", "parsing" ] +readme = "README.md" + +[[bin]] +name = "pierport" +test = false +bench = false +required-features = ["bin-deps"] + +[dependencies] +async_zip = { version = "0.0.15", features = ["full"] } +async-compression = { version = "0.4", features = ["zstd", "futures-io"] } +async-tar = "0.4" +anyhow = { version = "1", features = ["backtrace"] } +env_logger = "0.10" +futures = "0.3" +sha256 = { version = "1", features = ["async"] } +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1" } +octocrab = { version = "0.32", default-features = false, features = ["rustls"] } +log = "0.4" +tar = "0.4" +flate2 = "1" +async-trait = "0.1" +hyper = "0.14" +axum = "0.6" +once_cell = "1" + +clap = { version = "4", features = ["cargo", "derive"], optional = true } +tokio = { version = "1", features = ["full"], optional = true } +tokio-util = { version = "0.7", optional = true } +jsonwebtoken = { version = "9", optional = true } +reqwest = { version = "0.11", features = ["stream", "rustls-tls", "json"], default-features = false, optional = true } +toml = { version = "0.5", optional = true} + +# See https://github.com/rust-lang/jobserver-rs/issues/79 +[build-dependencies] +jobserver = { version = "=0.1.28", default-features = false } + +[features] +default = ["bin-deps"] +bin-deps = ["clap", "anyhow/backtrace", "tokio", "tokio-util", "jsonwebtoken", "reqwest", "toml"] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ba8f65e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +from rust:1.76-alpine3.19 + +RUN apk add musl-dev +RUN mkdir pierport +COPY Cargo.lock Cargo.toml pierport + +# Prebuild the target dir, just for quicker testing when iterating +RUN cd pierport && \ + mkdir src && \ + touch src/lib.rs && \ + echo "fn main() {}" > src/main.rs && \ + cargo install --path . && \ + rm -rf src +COPY src/ pierport/src +# If we prebuild the target dir, we must touch the source files, because otherwise they are ignored +RUN touch pierport/src/* + +RUN cd pierport && cargo install --path . && rm -rf target +COPY pierport_vere_db.json / +COPY scripts/env_cfg.sh scripts/entrypoint.sh / +RUN chmod +x env_cfg.sh entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..00f53a4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Chorus One + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b5bc3ce --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# pierport + +## Reference implementation for the pierport protocol UIP + +This repository contains reference implementation of the [Pierport Protocol UIP](https://github.com/urbit/UIPs/pull/48), and acts as an intermediate proxy for verifying and cleaning up a pier before it gets imported. + +The default configuration is a bit aggressive, most notably, `cram` is being used to verify integrity of the pier, before and after performing cleanup tasks. + +### Docker usage + +#### Building + +You may build pierport inside a docker container. In which case, just do the following: + +``` +docker build . -t pierport +``` + +#### Running + +You may then run it as follows: + +``` +docker run -p 4242 --name pierport -it pierport +``` + +To configure pierport, you may choose to either set specific `PRT_` environment variables (eg.: `-e PRT_PU_VERIFY_CRAM=false`), or bind mount a config toml file to the container using `-v path/to/config.toml:/pierport_cfg.toml`. + +To see available configuration environment variables, see [`env_cfg.sh`](scripts/env_cfg.sh) file. + +#### Running tests + +Once you have a built the image, tagged as `pierport`, you may also run the tests: + +``` +sh scripts/test.sh +``` diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..6968b53 --- /dev/null +++ b/flake.lock @@ -0,0 +1,102 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1711703276, + "narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d8fe5e6c92d0d190646fb9f1056741a229980089", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, + "parts": { + "inputs": { + "nixpkgs-lib": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "parts": "parts", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712024007, + "narHash": "sha256-52cf+mHZJbSaDFdsBj6vN1hH52AXsMgEpS/ajzc9yQE=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "d45d957dc3c48792af7ce58eec5d84407655e8fa", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..cd1607c --- /dev/null +++ b/flake.nix @@ -0,0 +1,39 @@ +{ + description = "pierport"; + + inputs = { + nixpkgs.url = "nixpkgs/nixos-unstable"; + parts = { + url = "github:hercules-ci/flake-parts"; + inputs.nixpkgs-lib.follows = "nixpkgs"; + }; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = inputs@{self, nixpkgs, parts, rust-overlay }: parts.lib.mkFlake { inherit inputs; } { + systems = [ "x86_64-linux" "aarch64-darwin" ]; + + flake = {}; + + perSystem = { pkgs, system, ... }: { + devShells.default = let + pkgs = import nixpkgs { inherit system; overlays = [(import rust-overlay)]; }; + in + pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + # Rust toolchain + (rust-bin.stable."1.76.0".default.override { + extensions = [ "rust-analyzer" ]; + }) + ]; + + # Set a few default environment variables. Locale archive is needed + # to make locales work due to Nix' weird decision to split it out. + LOCALE_ARCHIVE = if pkgs.lib.strings.hasSuffix "linux" system then "${pkgs.glibcLocales}/lib/locale/locale-archive" else null; + }; + }; + }; +} diff --git a/pierport_vere_db.json b/pierport_vere_db.json new file mode 100644 index 0000000..6457ea2 --- /dev/null +++ b/pierport_vere_db.json @@ -0,0 +1,767 @@ +{ + "versions": { + "0122a610d508277a5ba0c1fe03b01b36a8d58a94e3152bc01d2652a33de113ba": { + "inner": { + "version": "3.0", + "os": "macos", + "arch": "aarch64" + }, + "asset": 156117370 + }, + "09081f1521e75fce5d30b91400ca490ecf2c0c014dd1568d37d94b1626642f38": { + "inner": { + "version": "3.0", + "os": "linux", + "arch": "x86_64" + }, + "asset": 156117363 + }, + "0952b17b7cfdfc48f4abcea3a1f2d8b12dcbdba2467a1f9040097fc74a832d9c": { + "inner": { + "version": "1.17", + "os": "macos", + "arch": "x86_64" + }, + "asset": 92209293 + }, + "097d74a36fc73f0b32b327168b5aaf4158503a299236a15310490264b62d44ab": { + "inner": { + "version": "2.11", + "os": "linux", + "arch": "aarch64" + }, + "asset": 115673853 + }, + "0d59aac27a5f52b19ace4c6ae46ea85b72da9a6a097d8ff46b0ba2343cd5a5ca": { + "inner": { + "version": "2.7", + "os": "macos", + "arch": "aarch64" + }, + "asset": 110493606 + }, + "11d2a718edd35159f0be53af07dcb292e207acd8f672c4f698d689946a362b9f": { + "inner": { + "version": "2.1", + "os": "linux", + "arch": "aarch64" + }, + "asset": 101347131 + }, + "128d5f2dba6d4537e8e4821ac56e0d6e9dfd44df3525d7e3ffc729ab9f422aa1": { + "inner": { + "version": "2.3", + "os": "linux", + "arch": "x86_64" + }, + "asset": 106731250 + }, + "16d0824614c57be67214b6cbe101d8ced100d14d9684c91a43b94a3518871885": { + "inner": { + "version": "2.4", + "os": "macos", + "arch": "x86_64" + }, + "asset": 107695886 + }, + "17c85041ff3382fee0fc204d2d412d22c2f240e26edad40319c85ecad04a194b": { + "inner": { + "version": "1.20", + "os": "macos", + "arch": "aarch64" + }, + "asset": 95638669 + }, + "188705cf4a9cf467b0d471b1f5dc871fcf0f07f92f1038d00135281772485541": { + "inner": { + "version": "1.21", + "os": "linux", + "arch": "x86_64" + }, + "asset": 96560938 + }, + "18fb2532440faac02f05c9b4615e7364cf516dab72d377ca9af68871947b24de": { + "inner": { + "version": "1.20", + "os": "linux", + "arch": "x86_64" + }, + "asset": 95638671 + }, + "1d444775c47cc5d2ea6cc666b46a1ed206536a30a2afcdadabf29ad303bf9827": { + "inner": { + "version": "1.21", + "os": "macos", + "arch": "aarch64" + }, + "asset": 96560944 + }, + "23aa5bf1fabb80b4721a84cf679a28ab6ffccccb97a20b4cd198c3c8cd962d7f": { + "inner": { + "version": "2.6", + "os": "linux", + "arch": "x86_64" + }, + "asset": 108612755 + }, + "28d02eea8188001c22b86377d22baf1137308a70f4cc03f7742cd9f52d047059": { + "inner": { + "version": "1.16", + "os": "linux", + "arch": "x86_64" + }, + "asset": 91481401 + }, + "2afc3b8d86b9239c23da3b8f08e2c9931c98849d517097dcaf8a0ee970341369": { + "inner": { + "version": "1.22", + "os": "linux", + "arch": "x86_64" + }, + "asset": 97476127 + }, + "30f0dbc32da87371ba613452e79825c4f1fb0f649887d22229386594559dce92": { + "inner": { + "version": "1.19", + "os": "linux", + "arch": "x86_64" + }, + "asset": 94674482 + }, + "33b20a10b14d434e15f10235d35537ae6c541eb5c44eb411f1c23054a847f729": { + "inner": { + "version": "1.22", + "os": "linux", + "arch": "aarch64" + }, + "asset": 97476120 + }, + "3457ccf039cce16d611b4b47db358267665b823bf351c9146a7a250d8e97da94": { + "inner": { + "version": "2.8", + "os": "macos", + "arch": "aarch64" + }, + "asset": 111039668 + }, + "34ecdf43bd5c42b2bd3900a09a86a8e61491256c42f205a3cdc0021ec5d4f8b4": { + "inner": { + "version": "2.4", + "os": "linux", + "arch": "aarch64" + }, + "asset": 107695859 + }, + "34ff7a95bc2426fee1cdfb790c95e2b8b8cf6703e184a50b8796dc37923c2098": { + "inner": { + "version": "1.17", + "os": "macos", + "arch": "aarch64" + }, + "asset": 92209288 + }, + "3547341bbad054b8a30b42d30cce12ed3bf186ae7be1ad8abc68bc0292ef907e": { + "inner": { + "version": "2.9", + "os": "macos", + "arch": "x86_64" + }, + "asset": 111883461 + }, + "36ce5b8e9466d21daa06afc45a37eae8b906fd8304cfce40511dcf5d9f550d41": { + "inner": { + "version": "2.6", + "os": "macos", + "arch": "x86_64" + }, + "asset": 108612784 + }, + "3827b51a96b7ab1d0fd665a650a3207085c8288aaa806d82f5e6a216c11a6d80": { + "inner": { + "version": "2.4", + "os": "linux", + "arch": "x86_64" + }, + "asset": 107695875 + }, + "4393251a6d6b41cd5cbcbd8e562f18f8955bf3d63f5f876e3e6523f22cd5182b": { + "inner": { + "version": "2.3", + "os": "macos", + "arch": "aarch64" + }, + "asset": 106731255 + }, + "455c8046242a329b9a5f66bef2fbd92b2f643c0dfbd83b5d432e5af1d2b7da94": { + "inner": { + "version": "2.2", + "os": "macos", + "arch": "x86_64" + }, + "asset": 106469458 + }, + "4baa68b547adaefc251ac41f62d1ef77467c0c379577748a9ee6c7a17979226c": { + "inner": { + "version": "2.0", + "os": "macos", + "arch": "x86_64" + }, + "asset": 100342383 + }, + "4e2dd61696cb5b9ce49d90023a27af9d6954170bc40c27030614fc15b6cb300f": { + "inner": { + "version": "2.5", + "os": "macos", + "arch": "x86_64" + }, + "asset": 107860249 + }, + "4f98ea6a0d495083a3a1a0a7808dc5906b680a606ce2d75572acc1570d614b3c": { + "inner": { + "version": "2.7", + "os": "linux", + "arch": "x86_64" + }, + "asset": 110493599 + }, + "5304df8bf7b05383df34d5f78b1c6860cb888dc1471cb89d6f7ec6178bb9c65b": { + "inner": { + "version": "2.0", + "os": "linux", + "arch": "aarch64" + }, + "asset": 100342377 + }, + "53c3e95131471b5b19b16c7c70480fb49ac622fd446d07a081702613416ec548": { + "inner": { + "version": "1.16", + "os": "macos", + "arch": "aarch64" + }, + "asset": 91481404 + }, + "5415fd89895202b662aa3ce6161ac66b52a03913f4aba90e097d1ba871dc291b": { + "inner": { + "version": "3.0", + "os": "macos", + "arch": "x86_64" + }, + "asset": 156117372 + }, + "567dbd6dde56e65415cb7b34b18da10eade8bf250f5ff3853f221ac602912f73": { + "inner": { + "version": "2.6", + "os": "macos", + "arch": "aarch64" + }, + "asset": 108612761 + }, + "56986210fbaefcb60cd61cac6871424dfee2638c3aa40cfe678ca32fb2e22333": { + "inner": { + "version": "1.19", + "os": "macos", + "arch": "x86_64" + }, + "asset": 94673543 + }, + "58f5e5cef1f5f0b6077c0bcc0f59feac845fb2cf0f5171395557cebbe55c5899": { + "inner": { + "version": "2.2", + "os": "macos", + "arch": "aarch64" + }, + "asset": 106469456 + }, + "58fe7e232ac5583a95487e16d0822752487c5b2d5e107abf1c92a6e882393248": { + "inner": { + "version": "1.18", + "os": "macos", + "arch": "aarch64" + }, + "asset": 93761248 + }, + "593871a693f8f9e5055e4fdc87d6fe0feb3dc8326a14a46fabd604c83c93cfcd": { + "inner": { + "version": "1.18", + "os": "macos", + "arch": "x86_64" + }, + "asset": 93761251 + }, + "5c5df89fe2e0e965b7e077f870cc9ebf29c990dccb3ec4dfbf6d42bbd4452e30": { + "inner": { + "version": "2.10", + "os": "linux", + "arch": "x86_64" + }, + "asset": 114580157 + }, + "5dc7364cbd9e03bd2ce68ae01b28b7c3365f176c282ae59db17e2849d8f2a2b0": { + "inner": { + "version": "2.5", + "os": "macos", + "arch": "aarch64" + }, + "asset": 107860246 + }, + "5f48c5e576847db68fc97db9ff0b0bb8a826882ba2b90a25a5649c82dacbd693": { + "inner": { + "version": "2.0", + "os": "linux", + "arch": "x86_64" + }, + "asset": 100342378 + }, + "609833af917729d66fded790245e4354494aec89ee55d8295d3b668e4b63ba60": { + "inner": { + "version": "2.11", + "os": "macos", + "arch": "aarch64" + }, + "asset": 115673862 + }, + "66067f2cf23ddd279424f769282a9ec3ebfb8622e80c03aa5b64a7b4e415e182": { + "inner": { + "version": "2.1", + "os": "linux", + "arch": "x86_64" + }, + "asset": 101347134 + }, + "6b9318054648528c2efdf6bdf5f32c6d28ceec19b37c961079d64c7036ac191a": { + "inner": { + "version": "2.12", + "os": "linux", + "arch": "x86_64" + }, + "asset": 126569666 + }, + "6e37b44010119055d2d3506665b322cc75648d282c7950a0484cd3947a7a7596": { + "inner": { + "version": "1.18", + "os": "linux", + "arch": "x86_64" + }, + "asset": 93761244 + }, + "6ead05f1132f36289d77e33650e9a979454d52661c2e9368b1158436321a9ed5": { + "inner": { + "version": "2.11", + "os": "macos", + "arch": "x86_64" + }, + "asset": 115673866 + }, + "77ead425469cecca77b3d60820b424ccf4c4e3a1ccfb0c55415f5a6218c01402": { + "inner": { + "version": "1.18", + "os": "linux", + "arch": "aarch64" + }, + "asset": 93760876 + }, + "79d9411a498e8df3a592a90a57b82435f71eb586c38b62916cccc41201278c3f": { + "inner": { + "version": "2.1", + "os": "macos", + "arch": "x86_64" + }, + "asset": 101347142 + }, + "79ebc5e7aeacd4fdf2fad5537108fda89e1d6963f08c3c57913bd2ad527874a4": { + "inner": { + "version": "1.16", + "os": "macos", + "arch": "x86_64" + }, + "asset": 91481407 + }, + "7c852a3b48350a2fdea6a57e87364e7978cb78b375cc0cde1cbac4e7557a5fd3": { + "inner": { + "version": "2.9", + "os": "linux", + "arch": "x86_64" + }, + "asset": 111883454 + }, + "7d30751393a2a47a00ed7c6eeafb07ade6f00ff5480d141f5ba78e76a1915cda": { + "inner": { + "version": "2.9", + "os": "linux", + "arch": "aarch64" + }, + "asset": 111883437 + }, + "7d40a6d6e20b29c2d29427a61caf7287d1a7d17cbd536525fcf8d79015f28394": { + "inner": { + "version": "1.21", + "os": "linux", + "arch": "aarch64" + }, + "asset": 96560935 + }, + "7d8c66d2220990e6c9391e36d28811f50cfe3aba230945988b5df2786d1fdf57": { + "inner": { + "version": "2.1", + "os": "macos", + "arch": "aarch64" + }, + "asset": 101347140 + }, + "7f3278775231d567e1398fe4eb55dfa98418b06cd648087dbcfe75050df75832": { + "inner": { + "version": "2.12", + "os": "linux", + "arch": "aarch64" + }, + "asset": 126636479 + }, + "84b1f49bd052eb204990e4ebf8a9da4957e9f9a66e58d3fafd17bb80a35b3ea7": { + "inner": { + "version": "1.21", + "os": "macos", + "arch": "x86_64" + }, + "asset": 96560947 + }, + "86642f390f75c632b68a51859c925df1ed518f1e8ea70ab6419c47b4e30c907d": { + "inner": { + "version": "2.5", + "os": "linux", + "arch": "x86_64" + }, + "asset": 107860240 + }, + "878716dfa019f5d885c4aca6368cf0e9bcc342f74e8f6a885804b6d27ea4d77f": { + "inner": { + "version": "1.19", + "os": "macos", + "arch": "aarch64" + }, + "asset": 94672522 + }, + "87c19acdd511c8a53ad21b3a27ebf2498277f49ded265d05f2ec8cfb2e9da0ae": { + "inner": { + "version": "1.16", + "os": "linux", + "arch": "aarch64" + }, + "asset": 91481394 + }, + "8ae15a146aef1dca35641e18e95298b83aa68c22e2deafc43b7739f9fbd537b8": { + "inner": { + "version": "2.10", + "os": "linux", + "arch": "aarch64" + }, + "asset": 114580124 + }, + "8b0136edd4c2d5ec16797d3a778df72ac945a10bd3bb8e4a89b090c73a16ac66": { + "inner": { + "version": "2.7", + "os": "macos", + "arch": "x86_64" + }, + "asset": 110493609 + }, + "925e7b0ecf86a029867f18316f4ef758acdc2c40886847856f95a9a4d8217385": { + "inner": { + "version": "2.4", + "os": "macos", + "arch": "aarch64" + }, + "asset": 107695884 + }, + "92a8af2f38ee65f624de296bb5bb2bfe19c8c2adb3f406fbaffd71731411d19f": { + "inner": { + "version": "2.8", + "os": "linux", + "arch": "aarch64" + }, + "asset": 111039662 + }, + "9cbaf1f32324cdd328978922fdf7ba822c76000bdc90669dca7928194ffe4a72": { + "inner": { + "version": "2.12", + "os": "macos", + "arch": "aarch64" + }, + "asset": 126569671 + }, + "9e3212322dafc2105bfdb9f3b2d640830f2e7a0408f8df3458eb44a67e62a6c4": { + "inner": { + "version": "1.22", + "os": "macos", + "arch": "x86_64" + }, + "asset": 97476169 + }, + "a7ad150393fc1b34dde9925e884e055e3a2b48c8b6407cc1bb1d689962b6d140": { + "inner": { + "version": "2.6", + "os": "linux", + "arch": "aarch64" + }, + "asset": 108612750 + }, + "ab6295abac185281dcabe4aa28246a89c7c2e3241ad9b27c0958243d2c888634": { + "inner": { + "version": "2.3", + "os": "macos", + "arch": "x86_64" + }, + "asset": 106731259 + }, + "ac1a969a5c18241153233563ba843dc5dda99b4db3c6f7060c23e464ee16c62f": { + "inner": { + "version": "2.2", + "os": "linux", + "arch": "aarch64" + }, + "asset": 106469449 + }, + "afc968d7c36a536f39ac318e6f60f6c4708e423657247fbf0b9b49a9dbb00a63": { + "inner": { + "version": "2.12", + "os": "macos", + "arch": "x86_64" + }, + "asset": 126569672 + }, + "bcf5cb4058da9ef998ca654b612def5e981430516e1ffbc5f6d9d2dfc9e800ba": { + "inner": { + "version": "2.3", + "os": "linux", + "arch": "aarch64" + }, + "asset": 106731247 + }, + "bda53de579bd066020879539680a2999749c2813464a1f8cf0cca2ed52aa77f7": { + "inner": { + "version": "1.22", + "os": "macos", + "arch": "aarch64" + }, + "asset": 97476161 + }, + "c0b1257772dc6e9ba348cf563b376b9eea5ef7f6be9cf71176ce07dbed5d1821": { + "inner": { + "version": "2.2", + "os": "linux", + "arch": "x86_64" + }, + "asset": 106469452 + }, + "c5613b72cde6bd82144de20e12c7f42c50624b8655842dbb96d327f5010f4269": { + "inner": { + "version": "2.7", + "os": "linux", + "arch": "aarch64" + }, + "asset": 110493593 + }, + "c7e5267430a02c1cbe92a9ea74c2153743dc32abf28d3671bab2f26537dffcbc": { + "inner": { + "version": "2.9", + "os": "macos", + "arch": "aarch64" + }, + "asset": 111883458 + }, + "cba5b1e6345d0fd0e2c8dbd0e3965d69da3c65368a771d5f305935e5e22ec4e6": { + "inner": { + "version": "2.10", + "os": "macos", + "arch": "aarch64" + }, + "asset": 114580186 + }, + "d2494f1b177f4d9318a668b9981701dbb8ac0aa56ff14356a385e58fe2d1c481": { + "inner": { + "version": "1.17", + "os": "linux", + "arch": "x86_64" + }, + "asset": 92209317 + }, + "e188967ddf74c20202d92a3d5c912c90eaf563695372702557a5dc086f42943f": { + "inner": { + "version": "1.20", + "os": "macos", + "arch": "x86_64" + }, + "asset": 95638665 + }, + "e48341353b0076083f3b1c7dfab8a6519c5c48e8307ffc915508f36f4df43fd8": { + "inner": { + "version": "2.8", + "os": "linux", + "arch": "x86_64" + }, + "asset": 111039664 + }, + "e5471b701b10faa62e6425ad391d91d7a11f1128a2230609b7a22a1ca8b8a38b": { + "inner": { + "version": "2.0", + "os": "macos", + "arch": "aarch64" + }, + "asset": 100342381 + }, + "e987abe97f302c478e5c0fe55368910e42f983177276c8fbaa2975488dc2be80": { + "inner": { + "version": "2.10", + "os": "macos", + "arch": "x86_64" + }, + "asset": 114580190 + }, + "eaf2af38f7709255e81da5694de07f9e983b8e8df4b46497350a996915640b9a": { + "inner": { + "version": "1.17", + "os": "linux", + "arch": "aarch64" + }, + "asset": 92209301 + }, + "ec222cd3c647f3bee96d20d86b23ad16e234f73ca50747949ef4c96e9e881a39": { + "inner": { + "version": "1.20", + "os": "linux", + "arch": "aarch64" + }, + "asset": 95638679 + }, + "ee4602a81de0b067dabfb2b5c3f097dd0e38fcdb1dee38bf8db93e533b06e5f8": { + "inner": { + "version": "2.5", + "os": "linux", + "arch": "aarch64" + }, + "asset": 107860235 + }, + "f2932c93cc0c8346c4a247af0ff61b280aa2fb1b4e426a19a8a4f609a3a78f3d": { + "inner": { + "version": "1.19", + "os": "linux", + "arch": "aarch64" + }, + "asset": 94672506 + }, + "f55184d95357dbcd965c8818c56821820232c73c71e89f5a2b6e444a9c82af58": { + "inner": { + "version": "2.8", + "os": "macos", + "arch": "x86_64" + }, + "asset": 111039671 + }, + "f9d6261b00297ebfd70342218caa024b7917840641463b5f238638de4d9f9fa5": { + "inner": { + "version": "2.11", + "os": "linux", + "arch": "x86_64" + }, + "asset": 115673859 + }, + "fc65e13c9205f2cde339e4942f825b99e1bf91c03579ec18d0e1e65e2bf75688": { + "inner": { + "version": "3.0", + "os": "linux", + "arch": "aarch64" + }, + "asset": 156117360 + } + }, + "latest_release": 145887790, + "processed_assets": [ + 91481394, + 91481401, + 91481404, + 91481407, + 92209288, + 92209293, + 92209301, + 92209317, + 93760876, + 93761244, + 93761248, + 93761251, + 94672506, + 94672522, + 94673543, + 94674482, + 95638665, + 95638669, + 95638671, + 95638679, + 96560935, + 96560938, + 96560944, + 96560947, + 97476120, + 97476127, + 97476161, + 97476169, + 100342377, + 100342378, + 100342381, + 100342383, + 101347131, + 101347134, + 101347140, + 101347142, + 106469449, + 106469452, + 106469456, + 106469458, + 106731247, + 106731250, + 106731255, + 106731259, + 107695859, + 107695875, + 107695884, + 107695886, + 107860235, + 107860240, + 107860246, + 107860249, + 108612750, + 108612755, + 108612761, + 108612784, + 110493593, + 110493599, + 110493606, + 110493609, + 111039662, + 111039664, + 111039668, + 111039671, + 111883437, + 111883454, + 111883458, + 111883461, + 114580124, + 114580157, + 114580186, + 114580190, + 115673853, + 115673859, + 115673862, + 115673866, + 126569666, + 126569671, + 126569672, + 126636479, + 156117360, + 156117363, + 156117370, + 156117372 + ], + "update_time": { + "secs_since_epoch": 1712916239, + "nanos_since_epoch": 855095720 + } +} \ No newline at end of file diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..32a6ce3 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +1.76.0 diff --git a/scripts/empty_zod.tar.zst b/scripts/empty_zod.tar.zst new file mode 100644 index 0000000..eb7df14 Binary files /dev/null and b/scripts/empty_zod.tar.zst differ diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh new file mode 100644 index 0000000..f1cc4cd --- /dev/null +++ b/scripts/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +if [ ! -f pierport_cfg.toml ]; then + ./env_cfg.sh pierport_cfg.toml +fi + +pierport -c pierport_cfg.toml diff --git a/scripts/env_cfg.sh b/scripts/env_cfg.sh new file mode 100644 index 0000000..e7b6ec5 --- /dev/null +++ b/scripts/env_cfg.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# Generates pierport config from environment variables + +if [ -n "$1" ] && [ "$1" != "-" ]; then + fd=3 + exec 3> "$1" +else + fd=1 +fi + +if [ -n "$PRT_STAGING_DIR" ]; then + echo "staging_dir = \"$PRT_STAGING_DIR\"" 1>&$fd +fi + +if [ -n "$PRT_IMPORTED_DIR" ]; then + echo "imported_dir = \"$PRT_IMPORTED_DIR\"" 1>&$fd +fi + +if [ -n "$PRT_PROXY_IMPORT_BASE" ]; then + echo "proxy_import_base = \"$PRT_PROXY_IMPORT_BASE\"" 1>&$fd +fi + +if [ -n "$PRT_DB_PATH" ]; then + echo "db_path = \"$PRT_DB_PATH\"" 1>&$fd +fi + +if [ -n "$PRT_CACHE_DIR" ]; then + echo "cache_dir = \"$PRT_CACHE_DIR\"" 1>&$fd +fi + +if [ -n "$PRT_BIND" ]; then + echo "bind = \"$PRT_BIND\"" 1>&$fd +fi + +if [ -n "$PRT_MAX_LOOM" ]; then + echo "max_loom = $PRT_MAX_LOOM" 1>&$fd +fi + +if [ -n "$PRT_UPLOAD_LIMIT" ]; then + echo "upload_limit = $PRT_UPLOAD_LIMIT" 1>&$fd +fi + +if [ -n "$PRT_SESSION_CHECK_INTERVAL_SECONDS" ]; then + echo "session_check_interval_seconds = $PRT_SESSION_CHECK_INTERVAL_SECONDS" 1>&$fd +fi + +if [ -n "$PRT_SESSION_REMOVE_TIME_SECONDS" ]; then + echo "session_remove_time_seconds = $PRT_SESSION_REMOVE_TIME_SECONDS" 1>&$fd +fi + +pu_written=0 + +if [ -n "$PRT_PU_PREP" ]; then + if [ $pu_written = 0 ]; then + echo "[post_unpack]" 1>&$fd + pu_written=1 + fi + echo "prep = $PRT_PU_PREP" 1>&$fd +fi + +if [ -n "$PRT_PU_VERIFY_CRAM" ]; then + if [ $pu_written = 0 ]; then + echo "[post_unpack]" 1>&$fd + pu_written=1 + fi + echo "verify_cram = $PRT_PU_VERIFY_CRAM" 1>&$fd +fi + +if [ -n "$PRT_PU_PACK" ]; then + if [ $pu_written = 0 ]; then + echo "[post_unpack]" 1>&$fd + pu_written=1 + fi + echo "pack = $PRT_PU_PACK" 1>&$fd +fi + +if [ -n "$PRT_PU_MELD" ]; then + if [ $pu_written = 0 ]; then + echo "[post_unpack]" 1>&$fd + pu_written=1 + fi + echo "meld = $PRT_PU_MELD" 1>&$fd +fi + +if [ -n "$PRT_PU_CHOP" ]; then + if [ $pu_written = 0 ]; then + echo "[post_unpack]" 1>&$fd + pu_written=1 + fi + echo "chop = $PRT_PU_CHOP" 1>&$fd +fi + diff --git a/scripts/malformed_zod.tar.zst b/scripts/malformed_zod.tar.zst new file mode 100644 index 0000000..840bd16 Binary files /dev/null and b/scripts/malformed_zod.tar.zst differ diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100644 index 0000000..792a1c8 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,112 @@ +#!/bin/sh + +col_reset='\033[0m' +col_green='\033[0;32m' +col_red='\033[0;31m' + +dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) + +tmp_file="test-tmp-out" + +# Run a container named as `pierport` to prevent stopping of the container inbetween the test runs. +#cont=pierport + +# Mount the files so that we do not unnecessarily spam the github API. +mkdir -p $dir/../pierport_vere_cache +cont=$(docker run --rm -p 4242 -v "$dir/../pierport_vere_db.json:/pierport_vere_db.json:Z" -v "$dir/../pierport_vere_cache:/pierport_vere_cache:Z" -e RUST_LOG=debug --name pierport -d pierport) + +port=$(docker port "$cont" | grep 4242 | sed 's/.*://' | head -n 1) + +trap "rm -f $tmp_file; if [ $cont != pierport ]; then docker kill $cont > /dev/null; fi; trap - EXIT" EXIT INT HUP TERM +test_num=0 + +echo_col() { + col=$1 + shift 1 + printf "${col}$@${col_reset}\n" +} + +echo_err() { + echo_col $col_red "$@" 1>&2 +} + +assert_import() { + expected="$1" + patp="$2" + content="$3" + shift 3 + ret=$(curl -s -o "$tmp_file" -w "%{http_code}" -X POST -H "Content-Type: $content" "http://localhost:$port/import/~$patp" "$@") + + if [ "$ret" -ge 400 ]; then + if [ "$expected" = "fail" ]; then + return 0 + else + echo "$(cat $tmp_file)" + exit 1 + fi + fi + + if [ "$ret" -eq 202 ]; then + id=$(cat "$tmp_file") + echo "Waiting on $id" + while true; do + sleep 1 + ret=$(exec curl -s -o "$tmp_file" -w "%{http_code}" "http://localhost:$port/import/~$patp/$id") + + if [ "$ret" -ne 200 ]; then + if [ "$expected" = "fail" ]; then + return 0 + else + echo "Unable to query import status ($patp/$id): $(cat $tmp_file)" + exit 1 + fi + fi + + val=$(cat "$tmp_file" | jq -r 'keys | .[]') + + if [ "$val" = "importing" ]; then + continue + elif [ "$val" = "done" ] && [ "$expected" = "success" ]; then + return 0 + elif [ "$val" = "failed" ] && [ "$expected" = "fail" ]; then + return 0 + else + echo "Reached unexpected state ($val): $(cat $tmp_file)" + exit 1 + fi + done + elif [ "$expected" = "fail" ]; then + echo "Expected failure, got status 200 ($(cat $tmp_file))" + exit 1 + fi +} + +run_test() { + test_num=$((test_num+1)) + + ret=$("$@") + r2="$?" + + if [ "$r2" -eq 0 ]; then + echo_col $col_green "Test $test_num: pass" + return 0 + else + echo_err "Test $test_num: failed ($ret)" + exit 1 + fi +} + +# Send malformed archives +run_test assert_import fail zod "application/zstd" +run_test assert_import fail zod "application/zstd" -d 'sdfsdfsdfsdfsdfsdfsfddfs' + +# Send an empty zst archive +run_test assert_import fail zod "application/zstd" --data-binary "@${dir}/empty_zod.tar.zst" + +# Send zstd with malformed .urb directory +run_test assert_import fail zod "application/zstd" --data-binary "@${dir}/malformed_zod.tar.zst" + +# Send a zip bomb +run_test assert_import fail zod "application/json" -d '{ "url": "https://www.bamsoftware.com/hacks/zipbomb/zbsm.zip", "format": "zip" }' + +echo_col ${col_green} "All tests pass" diff --git a/src/header.rs b/src/header.rs new file mode 100644 index 0000000..8037e79 --- /dev/null +++ b/src/header.rs @@ -0,0 +1,4 @@ +#![allow(clippy::declare_interior_mutable_const)] +use hyper::header::HeaderName; + +pub const SIZE: HeaderName = HeaderName::from_static("pierport-size"); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b4b749c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,1165 @@ +use anyhow::anyhow; +use async_compression::futures::{bufread::ZstdDecoder, write::ZstdEncoder}; +use async_tar::EntryType; +use async_trait::async_trait; +use async_zip::base::read; +use core::pin::{pin, Pin}; +use futures::{ + io::{ + AsyncRead, AsyncReadExt, AsyncSeek, AsyncWrite, AsyncWriteExt, BufReader, BufWriter, Cursor, + }, + StreamExt, +}; +use log::*; +use octocrab::{ + models::{repos::Release, AssetId, ReleaseId}, + Octocrab, +}; +use serde::{Deserialize, Serialize}; +use std::collections::{BTreeMap, BTreeSet}; +use std::io; +use std::os::unix::fs::PermissionsExt; +use std::path::{Component, Path, PathBuf}; +use std::time::{Duration, SystemTime}; +use tar::Archive; +use tokio::fs; +use tokio::process::Command; +use tokio_util::compat::TokioAsyncReadCompatExt; + +pub mod header; + +/// Supported import formats, as per pierport spec. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ImportFormat { + Zip, + Zstd, +} + +/// Import payload, as per pierport spec. +/// +/// This is an alternative way to performing import requests - push a JSON payload that instructs +/// the import destination to pull the pier from alternative source. This has the potential to +/// minimize the number of intermediate copies needed. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ImportPayload { + pub url: String, + pub authorization: Option, + pub format: ImportFormat, +} + +/// Status of the import session, as per pierport spec. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ImportStatus { + /// Successfully imported + Done, + /// Failed to import + Failed { reason: Option }, + /// The pier is importing, status may contain extra information. + Importing { status: Option }, +} + +/// Crate-wide error type. +/// +/// FIXME: this is a bit of a hack to simply wrap anyhow error, and we should split this into more +/// specific error enum, but this will do for now. +pub struct Error(anyhow::Error); + +impl core::fmt::Debug for Error { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(f) + } +} + +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(f) + } +} + +impl> From for Error { + fn from(e: T) -> Self { + Self(e.into()) + } +} + +impl axum::response::IntoResponse for Error { + fn into_response(self) -> axum::response::Response { + ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + format!("{:#?}", self.0.to_string()), + ) + .into_response() + } +} + +pub type Result = core::result::Result; + +#[async_trait] +pub trait AsyncUnpack { + /// Unpack a file at given path. + async fn unpack<'a>( + &'a mut self, + path: &'a Path, + in_subpath: bool, + stream: Pin<&'a mut (dyn AsyncRead + Send + 'a)>, + ) -> io::Result<()>; + + /// Unpack an executable at given path. + /// + /// The only difference from `unpack` is that this expects the resulting file to be marked as + /// executable. + async fn unpack_exec<'a>( + &'a mut self, + path: &'a Path, + in_subpath: bool, + stream: Pin<&'a mut (dyn AsyncRead + Send + 'a)>, + ) -> io::Result<()>; +} + +#[async_trait] +impl< + T: for<'a> Fn(&'a Path, bool, Pin<&'a mut (dyn AsyncRead + Send + 'a)>, bool) -> F + Send, + F: core::future::Future> + Send, + > AsyncUnpack for T +{ + async fn unpack<'a>( + &'a mut self, + path: &'a Path, + in_subpath: bool, + stream: Pin<&'a mut (dyn AsyncRead + Send + 'a)>, + ) -> io::Result<()> { + let f = (*self)(path, in_subpath, stream, false); + f.await + } + + async fn unpack_exec<'a>( + &'a mut self, + path: &'a Path, + in_subpath: bool, + stream: Pin<&'a mut (dyn AsyncRead + Send + 'a)>, + ) -> io::Result<()> { + let f = (*self)(path, in_subpath, stream, true); + f.await + } +} + +pub async fn find_files_by_suffix( + directory: impl AsRef, + suffix: &str, +) -> io::Result> { + let mut entries = vec![]; + if let Ok(mut read_dir) = fs::read_dir(directory).await { + while let Ok(Some(entry)) = read_dir.next_entry().await { + let filetype = entry.file_type().await?; + if filetype.is_dir() { + continue; + } + if let Some(true) = entry.path().to_str().map(|s| s.ends_with(suffix)) { + entries.push(entry) + } + } + } + Ok(entries) +} + +/// Actions to be taken after pier is unpacked. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct PostUnpackCfg { + /// Catch up the event log. + prep: bool, + /// Cram and verify that the cram hasn't changed after subsequent steps. + verify_cram: bool, + /// Run vere pack. + pack: bool, + /// Run vere meld. + meld: bool, + /// Run vere chop. + chop: bool, +} + +impl PostUnpackCfg { + pub fn verify_cram(self, verify_cram: bool) -> Self { + Self { + verify_cram, + ..self + } + } + + pub fn prep(self, prep: bool) -> Self { + Self { prep, ..self } + } + + pub fn pack(self, pack: bool) -> Self { + Self { pack, ..self } + } + + pub fn meld(self, meld: bool) -> Self { + Self { meld, ..self } + } + + pub fn chop(self, chop: bool) -> Self { + Self { chop, ..self } + } + + pub fn all() -> Self { + Self { + verify_cram: true, + prep: true, + pack: true, + meld: true, + chop: true, + } + } +} + +#[derive(Clone)] +pub struct StandardUnpack> { + path: T, + loom: Option, +} + +impl> StandardUnpack { + pub fn loom(&self) -> Option { + self.loom + } +} + +impl> Drop for StandardUnpack { + fn drop(&mut self) { + let path = self.path.as_ref(); + debug!("Drop {path:?}"); + if path.exists() { + if let Err(e) = std::fs::remove_dir_all(path) { + error!("StandardUnpack: unable to remove dir ({e:?})"); + } + } + } +} + +impl> core::ops::Deref for StandardUnpack { + type Target = Path; + + fn deref(&self) -> &Self::Target { + self.path.as_ref() + } +} + +impl> StandardUnpack { + pub async fn new(path: T, loom: Option) -> Result> { + if path.as_ref().exists() { + info!("Remove {:?}", path.as_ref()); + fs::remove_dir_all(path.as_ref()).await?; + } + + fs::create_dir_all(path.as_ref()).await?; + + Ok(Self { path, loom }) + } + + pub async fn detect_loom(&mut self) -> Result> { + // TODO: autodetect loom based on the snapshot size + Ok(None) + } + + pub fn set_loom(&mut self, loom: Option) { + self.loom = loom; + } + + async fn run_cmd(&mut self, args: &[&str]) -> Result<()> { + let mut cmd = Command::new("./.run"); + + cmd.current_dir(&**self).args(args); + + if let Some(loom) = self.loom { + cmd.args(["--loom", &loom.to_string(), "-t"]); + } + + let output = cmd.output().await?; + + trace!("{:?}", std::str::from_utf8(&output.stdout)); + + if !output.status.success() { + Err(anyhow!( + "Command failed: {} {:?}", + output.status, + std::str::from_utf8(&output.stderr) + ) + .into()) + } else { + Ok(()) + } + } + + pub async fn cram(mut self) -> Result> { + debug!("Pre-work cram"); + self.run_cmd(&["cram"]).await?; + Ok(self) + } + + pub async fn verify_cram(mut self) -> Result> { + debug!("Post-work"); + + // Get the path to current jam + let roc_dir = self.join(".urb/roc"); + let entries = find_files_by_suffix(&roc_dir, ".jam").await?; + + let [entry] = &entries[..] else { + return Err(anyhow!("Invalid number of jams").into()); + }; + + let hash = sha256::async_digest::try_async_digest(entry.path()).await?; + fs::remove_file(entry.path()).await?; + + debug!("Pre-work hash: {hash}"); + + // Get the current hash of the current jam + + self.run_cmd(&["cram"]).await?; + + let hash2 = sha256::async_digest::try_async_digest(entry.path()).await?; + fs::remove_dir_all(roc_dir).await?; + + debug!("Post-work hash: {hash}"); + + if hash == hash2 { + Ok(self) + } else { + Err(anyhow!("Pre and post work jam mismatch").into()) + } + } + + pub async fn prep(mut self) -> Result> { + debug!("Prep"); + self.run_cmd(&["prep"]).await?; + Ok(self) + } + + pub async fn pack(mut self) -> Result> { + debug!("Pack"); + self.run_cmd(&["pack"]).await?; + Ok(self) + } + + pub async fn meld(mut self) -> Result> { + debug!("Meld"); + self.run_cmd(&["meld"]).await?; + Ok(self) + } + + pub async fn chop(mut self) -> Result> { + debug!("Chop"); + self.run_cmd(&["chop"]).await?; + + // Cleans up all pre-3.0 chops + let chop_dir = self.join(".urb/log/chop"); + if chop_dir.exists() { + fs::remove_dir_all(chop_dir).await?; + } + + // Cleans up all 3.0+ chops + // Remove all epochs, but the latest one + let log_dir = self.join(".urb/log"); + let mut max_epoch = None; + if let Ok(mut read_dir) = fs::read_dir(&log_dir).await { + while let Ok(Some(entry)) = read_dir.next_entry().await { + let filetype = entry.file_type().await?; + if !filetype.is_dir() { + continue; + } + let fname = entry.file_name(); + let Some(fname) = fname.to_str() else { + continue; + }; + + let Some(epoch) = fname + .strip_prefix("0i") + .and_then(|v| v.parse::().ok()) + else { + continue; + }; + + match max_epoch { + None => { + max_epoch = Some(epoch); + } + Some(e) if e < epoch => { + fs::remove_dir_all(log_dir.join(&format!("0i{e}"))).await?; + max_epoch = Some(epoch); + } + // This branch should always be hit, but don't make it unconditional, just to + // be sure that we are not deleting the latest epoch. + Some(e) if e != epoch => { + fs::remove_dir_all(log_dir.join(&format!("0i{epoch}"))).await?; + } + Some(_) => (), + } + } + } + + Ok(self) + } + + pub async fn post_unpack( + mut self, + vere_version: VereVersion, + db_path: &Path, + cache_path: &Path, + cfg: &PostUnpackCfg, + ) -> Result> { + // We need to correct the vere architecture to something + let db = VersionDb::load(db_path).await?; + + let vere = db.bin_from_version(&vere_version, cache_path).await?; + + let vere_path = self.join(".run"); + fs::write(&vere_path, vere).await?; + + let mut perms = fs::metadata(&vere_path).await?.permissions(); + perms.set_mode(0o755); + fs::set_permissions(&vere_path, perms).await?; + + if cfg.prep { + self = self.prep().await?; + } + + if cfg.verify_cram { + self = self.cram().await?; + } + + if cfg.pack { + self = self.pack().await?; + } + + if cfg.meld { + self = self.meld().await?; + } + + if cfg.chop { + self = self.chop().await?; + } + + if cfg.verify_cram { + self = self.verify_cram().await?; + } + + Ok(self) + } + + pub async fn lmdb_patp(self) -> Result { + Err(anyhow!("Not yet implemented").into()) + } +} + +#[async_trait] +impl + Send> AsyncUnpack for StandardUnpack { + async fn unpack<'a>( + &'a mut self, + path: &'a Path, + in_subpath: bool, + stream: Pin<&'a mut (dyn AsyncRead + Send + 'a)>, + ) -> io::Result<()> { + let path = if in_subpath { + path.components() + .skip_while(|c| c == &Component::CurDir) + .skip(1) + .collect() + } else { + path.to_path_buf() + }; + + let dir = if let Some(parent) = path.parent() { + self.join(parent) + } else { + self.to_path_buf() + }; + + trace!("Write file {path:?} {in_subpath} {dir:?}"); + + fs::create_dir_all(dir).await?; + + let out_path = self.join(&path); + + let file = fs::File::create(&out_path).await?; + + futures::io::copy(stream, &mut file.compat()).await?; + + trace!("Written file"); + + Ok(()) + } + + async fn unpack_exec<'a>( + &'a mut self, + path: &'a Path, + in_subpath: bool, + stream: Pin<&'a mut (dyn AsyncRead + Send + 'a)>, + ) -> io::Result<()> { + self.unpack(path, in_subpath, stream).await?; + + let path = if in_subpath { + path.components() + .skip_while(|c| c == &Component::CurDir) + .skip(1) + .collect() + } else { + path.to_path_buf() + }; + + let path = self.join(&path); + + trace!("Metadata {path:?}"); + + let mut perms = fs::metadata(&path).await?.permissions(); + perms.set_mode(0o755); + + trace!("Set perms {path:?}"); + + fs::set_permissions(&path, perms).await?; + + trace!("Unpacked"); + + Ok(()) + } +} + +// 16GB +const MAX_SIZE: u64 = 0x400000000; +// 128MB +const MAX_VERE_SIZE: u64 = 0x8000000; + +#[derive(Debug)] +pub enum Pace { + Live, + Once, +} + +#[derive(Default, Debug)] +pub struct UrbitPier { + pub pace: Option, + pub vere_hash: Option, + // Vere version hash matched against database of whitelisted runtimes + //pub vere_version: Option, +} + +impl UrbitPier { + // Attempts to match the pier's vere hash with the officially released vere. + // + // If the pier has no vere binary, and undocked_fallback specifies target OS and arch, then + // this will take the latest vere release that is for the given os-arch pair. + pub async fn vere_version( + &self, + db_path: &Path, + undocked_fallback: Option<(&str, &str)>, + ) -> Result { + let db = VersionDb::load(db_path).await.unwrap_or_default(); + // Refresh if database is older than 1 hour + let (db, updated) = db.refresh_if_older(Duration::from_secs(3600)).await?; + + if updated { + db.save(db_path).await?; + } + + if let Some(vere_hash) = self.vere_hash.as_ref() { + db.versions + .get(vere_hash) + .map(|v| v.inner.clone()) + .ok_or_else(|| anyhow!("Could not match vere hash with version").into()) + } else if let Some((os, arch)) = undocked_fallback { + debug!("No vere hash found. Falling back to latest."); + db.versions + .values() + .fold(None, |prev: Option<&VereVersion>, cur| { + if cur.inner.os == os && cur.inner.arch == arch { + if let Some(prev) = prev { + if prev + .version + .split('.') + .map(|v| v.parse::().unwrap_or_default()) + .cmp( + cur.inner + .version + .split('.') + .map(|v| v.parse::().unwrap_or_default()), + ) + == core::cmp::Ordering::Less + { + return Some(&cur.inner); + } + } else { + return Some(&cur.inner); + } + } + prev + }) + .cloned() + .ok_or_else(|| anyhow!("Could not find a version for given arch").into()) + } else { + Err(anyhow!("No vere in pier, and no undocked fallback set").into()) + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct VereVersion { + pub version: String, + pub os: String, + pub arch: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct VereVersionOuter { + inner: VereVersion, + asset: AssetId, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct VersionDb { + // HashMap would be more efficient, but the map is not going to be huge anyways, and sorting + // everything would be better. + versions: BTreeMap, + latest_release: Option, + processed_assets: BTreeSet, + update_time: Option, +} + +impl VersionDb { + pub async fn load(path: &Path) -> Result { + let data = fs::read_to_string(path).await?; + serde_json::from_str(&data).map_err(Into::into) + } + + async fn get_with_redirects(gh: &Octocrab, mut url: String) -> Result> { + let mut cnt = 0; + + loop { + cnt += 1; + + if cnt > 10 { + return Err(anyhow!("Too many redirects").into()); + } + + let response = gh._get(&url).await?; + + if response.status() != hyper::StatusCode::OK { + let Some(location) = response.headers().get(hyper::header::LOCATION) else { + return Err(anyhow!("Error downloading vere: {}", response.status()).into()); + }; + url = location.to_str()?.to_string(); + debug!("Redirect to {url}"); + } else { + break Ok(hyper::body::to_bytes(response.into_body()).await?.to_vec()); + } + } + } + + async fn ensure_downloaded(&self, id: AssetId, cache_dir: &Path) -> Result<()> { + fs::create_dir_all(cache_dir).await?; + + let asset_path = cache_dir.join(id.to_string()); + + // Check if there's existing asset with correct hash + if let Ok(bytes) = fs::read(&asset_path).await { + let digest = sha256::digest(&bytes); + + if self.versions.get(&digest).map(|v| v.asset == id) == Some(true) { + debug!("Cached vere version found for {id}."); + return Ok(()); + } + + debug!("Hash mismatch for {id}. Redownloading."); + } + + let gh = octocrab::instance(); + let repos = gh.repos("urbit", "vere"); + let releases = repos.releases(); + let asset = releases.get_asset(id).await?; + + let url = asset.browser_download_url.to_string(); + debug!("Download vere from {url}"); + let bytes = Self::get_with_redirects(&gh, url).await?; + + let bytes = tokio::task::spawn_blocking(move || Self::unpack_vere(bytes)) + .await?? + .1; + + fs::write(asset_path, bytes).await?; + + Ok(()) + } + + async fn bin_from_version(&self, version: &VereVersion, cache_dir: &Path) -> Result> { + let asset = self + .versions + .values() + .find_map(|v| { + if &v.inner == version { + Some(v.asset) + } else { + None + } + }) + .ok_or_else(|| anyhow!("Could not find compatible binary asset"))?; + + self.ensure_downloaded(asset, cache_dir).await?; + + Ok(fs::read(cache_dir.join(asset.to_string())).await?) + } + + fn unpack_vere(bytes: impl AsRef<[u8]>) -> Result<(String, Vec)> { + use std::io::Read; + + let gz = flate2::read::GzDecoder::new(bytes.as_ref()); + let mut a = Archive::new(gz); + + // We expect only a single file here + let mut f = a + .entries()? + .next() + .transpose()? + .ok_or_else(|| anyhow!("File empty"))?; + + let mut bytes = vec![]; + f.read_to_end(&mut bytes)?; + + // Compute hash of the executable + Ok((sha256::digest(&bytes), bytes)) + } + + async fn process_release(&mut self, gh: &Octocrab, release: Release) -> Result<()> { + self.latest_release = Some(release.id); + + let Some(version) = release.tag_name.strip_prefix("vere-v") else { + warn!( + "Skipping {}, because it has invalid prefix.", + release.tag_name + ); + return Ok(()); + }; + + for asset in release.assets { + // Only process tgz archives + let Some(archive) = asset.name.strip_suffix(".tgz") else { + continue; + }; + let Some((os, arch)) = archive + .split_once('-') + .map(|(a, b)| (a.to_string(), b.to_string())) + else { + warn!("Skipping {archive}, since it is not formatted as OS-Arch"); + continue; + }; + + if self.processed_assets.contains(&asset.id) { + continue; + } + + // Now, pull the binary + debug!("Downloading {version}: {archive}"); + + let bytes = + Self::get_with_redirects(gh, asset.browser_download_url.to_string()).await?; + + let inner = VereVersion { + version: version.into(), + os, + arch, + }; + + let hash = tokio::task::spawn_blocking(move || Self::unpack_vere(bytes)) + .await?? + .0; + + self.versions.insert( + hash.clone(), + VereVersionOuter { + inner, + asset: asset.id, + }, + ); + + self.processed_assets.insert(asset.id); + } + + Ok(()) + } + + pub async fn refresh_if_older(self, duration: Duration) -> Result<(Self, bool)> { + if self + .update_time + .and_then(|v| v.elapsed().ok()) + .map(|v| v >= duration) + != Some(false) + { + self.refresh().await + } else { + Ok((self, false)) + } + } + + pub async fn refresh(mut self) -> Result<(Self, bool)> { + let gh = octocrab::instance(); + + let repos = gh.repos("urbit", "vere"); + let releases = repos.releases(); + + // Fetch latest urbit release + let latest = releases.get_latest().await?; + + self.update_time = Some(SystemTime::now()); + + if Some(latest.id) == self.latest_release { + info!("Only syncing latest release ({})", latest.tag_name); + + // process assets of the latest release to make sure any asset changes are synced up. + self.process_release(&gh, latest).await?; + return Ok((self, false)); + } + + info!("Pulling new vere releases"); + + // If we have a mismatch of the latest release, pull all releases, sort them by created_at + // attribute, and update our ID. + + let mut out_rel = vec![]; + + for i in 0u32.. { + // Pull 10 releases at a time, to not hit timeout conditions + let Ok(list) = releases.list().per_page(5).page(i).send().await else { + break; + }; + + if list.items.is_empty() { + break; + } + + let incomplete = list.incomplete_results == Some(true); + + let mut hit_latest = false; + + out_rel.extend( + list.into_iter() + .filter(|v| !v.draft && !v.prerelease) + .inspect(|v| hit_latest = hit_latest || Some(v.id) == self.latest_release), + ); + + // We hit the latest release we have at the moment. We assume further releases will be + // old ones, so that we do not have to + if hit_latest { + break; + } + + if incomplete { + return Err(anyhow!( + "Got incomplete results, we do not support that at the moment" + ) + .into()); + } + } + + out_rel.sort_by_key(|v| v.created_at); + + for release in out_rel { + debug!("Process {}", release.tag_name); + self.process_release(&gh, release).await?; + } + + Ok((self, true)) + } + + pub async fn save(&self, path: &Path) -> Result<()> { + let data = serde_json::to_string_pretty(self)?; + fs::write(path, data.as_bytes()).await?; + Ok(()) + } +} + +/// Returns (is_subpath, has_subcomponents, in_subpath) +fn is_subpath(in_path: &Path, target_path: &Path) -> Option<(bool, bool)> { + let mut in_components = in_path.components().skip_while(|c| c == &Component::CurDir); + + let target_components = target_path + .components() + .skip_while(|c| c == &Component::CurDir); + + let mut in_subpath = false; + + for target in target_components { + // We may need to get 2 input components out given 1 target component. + loop { + let Some(inp) = in_components.next() else { + return None; + }; + + if target != inp { + if in_subpath { + return None; + } else { + in_subpath = true; + continue; + } + } + + // Intentionally break in all cases except 1 - the control flow is easier to reason + // about this way. + break; + } + } + + Some((in_components.next().is_some(), in_subpath)) +} + +fn is_path(in_path: &Path, target_path: &Path) -> (bool, bool) { + is_subpath(in_path, target_path) + .map(|(a, b)| if !a { (true, b) } else { (false, false) }) + .unwrap_or_default() +} + +fn is_vere(in_path: &Path) -> (bool, bool) { + is_path(in_path, Path::new(".run")) +} + +fn is_allowed_file(in_path: &Path) -> (bool, bool) { + for f in [".bin/pace", ".run"] { + if let Some((false, in_subpath)) = is_subpath(in_path, Path::new(f)) { + return (true, in_subpath); + } + } + + // Exclude any of the following, because we simply don't need them + for d in [".urb/get", ".urb/put", ".urb/roc"] { + if is_subpath(in_path, Path::new(d)).is_some() { + return (false, false); + } + } + + for d in [".urb"] { + if let Some((true, in_subpath)) = is_subpath(in_path, Path::new(d)) { + return (true, in_subpath); + } + } + + (false, false) +} + +/// Filters files on a zstd stream, and outputs them to given function +pub async fn import_zstd_stream( + stream_in: I, + file_out: &mut (impl AsyncUnpack + ?Sized), +) -> Result { + import_zstd_stream_with_vere(stream_in, file_out, false).await +} + +/// Filters files on a zstd stream, and outputs them to given function +pub async fn import_zstd_stream_with_vere( + stream_in: I, + file_out: &mut (impl AsyncUnpack + ?Sized), + unpack_vere: bool, +) -> Result { + let stream_in = BufReader::new(stream_in); + let mut stream_in = ZstdDecoder::new(stream_in); + let stream_in = pin!(stream_in); + let ar = async_tar::Archive::new(stream_in); + let mut entries = ar.entries()?; + + // This allows us to be consistent with parsing paths from `.//` and `./` + let mut subpath_mode = None; + + let mut pier = UrbitPier::default(); + + debug!("IMPORT ZSTD"); + + while let Some(entry) = entries.next().await { + let mut entry = entry?; + + debug!("ZSTD {entry:?}"); + + if !matches!( + entry.header().entry_type(), + EntryType::Regular + | EntryType::Continuous + | EntryType::GNULongName + | EntryType::GNUSparse + ) { + debug!("CONTINUE"); + continue; + } + + let path: PathBuf = (*entry.path()?).into(); + let size = entry.header().size()?; + + let (is_vere, in_subpath) = is_vere(&path); + + debug!("ZSTD: {path:?} is_vere={is_vere} in_subpath={in_subpath}"); + + if is_vere { + if subpath_mode.is_some() && Some(in_subpath) != subpath_mode { + warn!( + "Subpath mode does not match ({in_subpath} vs. {})", + subpath_mode.unwrap() + ); + continue; + } else { + subpath_mode = Some(in_subpath); + + if size > MAX_VERE_SIZE { + warn!("Vere too large ({size} bytes)",); + continue; + } + + // We need to read vere out, to compute its hash, and skip output, because we will + // write vere properly later with correct architecture. + let mut vere = vec![]; + entry.read_to_end(&mut vere).await?; + pier.vere_hash = Some(sha256::digest(&vere)); + + if unpack_vere { + file_out + .unpack_exec(&path, in_subpath, pin!(Cursor::new(vere))) + .await?; + } + + continue; + } + } + + let (is_allowed_file, in_subpath) = is_allowed_file(&path); + + if is_allowed_file { + if subpath_mode.is_some() && Some(in_subpath) != subpath_mode { + warn!( + "Subpath mode does not match ({in_subpath} vs. {})", + subpath_mode.unwrap() + ); + continue; + } else { + subpath_mode = Some(in_subpath); + + if size > MAX_SIZE { + warn!("File too large ({size} bytes)",); + continue; + } + + file_out.unpack(&path, in_subpath, pin!(entry)).await?; + } + } + } + + if subpath_mode.is_some() { + Ok(pier) + } else { + Err(anyhow!("Pier is empty").into()) + } +} + +/// Filters files on a zip file, and outputs them to a given function +/// +/// Note that for zip to work, we need to have a seekable stream, i.e. file. +pub async fn import_zip_file( + stream_in: I, + file_out: &mut (impl AsyncUnpack + ?Sized), +) -> Result { + let mut stream_in = BufReader::new(stream_in); + let stream_in = pin!(stream_in); + let mut zip = read::seek::ZipFileReader::new(stream_in).await?; + + // This allows us to be consistent with parsing paths from `.//` and `./` + let mut subpath_mode = None; + + let mut pier = UrbitPier::default(); + + for i in 0.. { + let Ok(mut file) = zip.reader_with_entry(i).await else { + break; + }; + + // This is not a file, we don't need it + if matches!(file.entry().dir(), Ok(true)) { + continue; + } + + let entry = file.entry(); + + let Ok(path) = entry.filename().as_str().map(PathBuf::from) else { + continue; + }; + + let (is_vere, in_subpath) = is_vere(&path); + + if is_vere { + if subpath_mode.is_some() && Some(in_subpath) != subpath_mode { + warn!( + "Subpath mode does not match ({in_subpath} vs. {})", + subpath_mode.unwrap() + ); + continue; + } else { + subpath_mode = Some(in_subpath); + + if file.entry().uncompressed_size() > MAX_VERE_SIZE { + warn!( + "Vere too large ({} bytes)", + file.entry().uncompressed_size() + ); + continue; + } + + // We need to read vere out, to compute its hash, and skip output, because we will + // write vere properly later with correct architecture. + let mut vere = vec![]; + file.read_to_end_checked(&mut vere).await?; + pier.vere_hash = Some(sha256::digest(&vere)); + continue; + } + } + + let (is_allowed_file, in_subpath) = is_allowed_file(&path); + + if is_allowed_file { + if subpath_mode.is_some() && Some(in_subpath) != subpath_mode { + warn!( + "Subpath mode does not match ({in_subpath} vs. {})", + subpath_mode.unwrap() + ); + continue; + } else { + subpath_mode = Some(in_subpath); + + if file.entry().uncompressed_size() > MAX_SIZE { + warn!( + "File too large ({} bytes)", + file.entry().uncompressed_size() + ); + continue; + } + + file_out.unpack(&path, in_subpath, pin!(file)).await?; + } + } + } + + Ok(pier) +} + +/// Compresses a pier directory using zstd compression algorithm. +pub async fn export_dir(path: &Path, stream_out: (impl AsyncWrite + Send + Sync)) -> Result<()> { + let stream_out = BufWriter::new(stream_out); + let stream_out = ZstdEncoder::new(stream_out); + let stream_out = pin!(stream_out); + + let mut ar = async_tar::Builder::new(stream_out); + ar.append_dir_all(".", path).await?; + ar.finish().await?; + let mut zstd = ar.into_inner().await?; + zstd.close().await?; + zstd.get_pin_mut().flush().await?; + + Ok(()) +} + +pub fn current_os_arch() -> (&'static str, &'static str) { + use std::env::consts::{ARCH, OS}; + (OS, ARCH) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..159ba47 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,608 @@ +use clap::Parser; +use core::pin::Pin; +use futures::{ + io::{AsyncRead, AsyncReadExt}, + TryStreamExt, +}; +use jsonwebtoken::{Algorithm, DecodingKey, Validation}; +use log::*; +use once_cell::sync::Lazy; +use pierport::*; +use serde::{Deserialize, Serialize}; +use std::collections::{BTreeMap, HashMap}; +use std::net::SocketAddr; +use std::path::PathBuf; +use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, +}; +use std::time::{Duration, Instant}; +use tokio::fs::OpenOptions; +use tokio::sync::Mutex; +use tokio::task::JoinHandle; +use tokio_util::{compat::TokioAsyncReadCompatExt, io::ReaderStream}; + +use axum::{ + extract::{DefaultBodyLimit, Json, Path, State}, + response::IntoResponse, + routing::{get, post}, + Router, +}; +use hyper::{Request, Response, StatusCode}; +use reqwest::Client; + +static CS: Lazy> = Lazy::new(Default::default); + +static CLIENT: Lazy = Lazy::new(Client::new); + +#[derive(Serialize, Deserialize)] +#[serde(default)] +struct Config { + /// Staging directory for imported piers. + /// + /// This directory stores and unpacks imported piers, before proxying them. + /// + /// The staging dir assumes unprocessed piers, so any files found there are assumed to be + /// deletable. + staging_dir: PathBuf, + /// Directory where imported piers are moved, before being proxied. + /// + /// When piers are proxied over, the import request already returns ACCEPTED. + imported_dir: PathBuf, + /// Where to invoke ship import after cleaning up pier data. + /// + /// The endpoint must be the base import address that speaks the pierport protocol. + /// + /// For instance: + /// + /// `http://internal-import.ts.infra.net/api/port/import`. + /// + /// If the value is supplied, then, pierport will remove the pier from `imported_dir`, after + /// successful output from the proxy base. If the value is unspecified, or proxy returns an + /// error, then pier directory will remain on the machine. + proxy_import_base: Option, + /// Path to vere database json. + db_path: PathBuf, + /// Path to vere binary cache. + cache_dir: PathBuf, + /// Where to bind the pierport server on. + bind: SocketAddr, + /// Maximum loom argument for cleanup tasks. + /// + /// If set, the importer will start listening to `Pierport-Loom` header, and clamp it to the + /// given value. Otherwise, the header is ignored. + /// + /// The size is given in bits, same as in vere. + max_loom: Option, + /// Limit for uploaded pier archives. + /// + /// Note that this does not consider the size of the uncompressed pier, instead, only considers + /// the compressed size. + upload_limit: usize, + /// Cleanup steps to run after the pier has been unpacked. + /// + /// Defaults to all steps, unless there is a section in the config negating it (sufficient to + /// declare empty post_unpack section). + post_unpack: PostUnpackCfg, + /// How often should we cleanup complete sessions. + session_check_interval_seconds: u64, + /// How old should completed sessions be before being removed. + session_remove_time_seconds: u64, +} + +impl Default for Config { + fn default() -> Self { + Self { + staging_dir: "./pierport_staging/".into(), + imported_dir: "./pierport_imported/".into(), + proxy_import_base: None, + db_path: "./pierport_vere_db.json".into(), + cache_dir: "./pierport_vere_cache/".into(), + bind: "0.0.0.0:4242".parse().unwrap(), + max_loom: None, + // 4G + upload_limit: 0x100000000, + post_unpack: PostUnpackCfg::all(), + session_check_interval_seconds: 60, + session_remove_time_seconds: 600, + } + } +} + +/// Pierport - urbit pier import service +/// +/// ## Ensuring authentication +/// +/// If a Json Web Token secret is specified in "JWT_AUTH_SECRET" environment variable, then all +/// incoming requests will need to have a valid Authorization header that contains a jwt, +/// verifiable by the given secret. +/// +/// The env var is interpreted as raw bytes, rather than a base64 encoded string. +#[derive(Parser)] +#[command(author, version, about)] +#[command(propagate_version = false)] +struct Args { + #[arg(short, long)] + config: Option, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + env_logger::init_from_env(env_logger::Env::default().default_filter_or("pierport=info")); + + let Args { config } = Args::parse(); + + // JWT secret is used to verify pier import requests. + let jwt_key = std::env::var_os("JWT_AUTH_SECRET"); + + let jwt_key = if let Some(secret) = jwt_key.as_deref().and_then(|v| v.to_str()) { + Some(Arc::new(DecodingKey::from_secret(secret.as_bytes()))) + } else { + warn!("No JWT_AUTH_SECRET specified - authentication is disabled!"); + None + }; + + let config: Config = if let Some(config) = config { + let bytes = tokio::fs::read_to_string(config).await?; + toml::from_str(&bytes)? + } else { + Default::default() + }; + + let config = Arc::new(config); + + let sessions = Arc::new(Sessions::default()); + + // Cleanup stale/completed sessions at fixed interval + tokio::spawn({ + let sessions = sessions.clone(); + let config = config.clone(); + async move { + loop { + tokio::time::sleep(Duration::from_secs(config.session_check_interval_seconds)) + .await; + sessions + .cleanup_stale_sessions(Duration::from_secs(config.session_remove_time_seconds)) + .await; + } + } + }); + + // TODO: should we include pierport/v1/ here? + let app = Router::new() + .route("/import/~:patp", post(import)) + .route("/import/~:patp/:id", get(import_status)) + .layer(DefaultBodyLimit::max(config.upload_limit)) + .with_state((config.clone(), jwt_key, sessions)); + + axum::Server::bind(&config.bind) + .serve(app.into_make_service()) + .await?; + + Ok(()) +} + +/// Check on the pier import status. +async fn import_status( + State((_, _, sessions)): State<(Arc, Option>, Arc)>, + Path((patp, id)): Path<(String, u64)>, +) -> axum::response::Result<(StatusCode, Json)> { + debug!("Get status {patp} {id}"); + let status = match sessions.get_session_status(&patp, id).await? { + Some(Ok(())) => ImportStatus::Done, + Some(Err(e)) => ImportStatus::Failed { + reason: Some(e.to_string()), + }, + None => ImportStatus::Importing { status: None }, + }; + + Ok((StatusCode::OK, Json(status))) +} + +/// The main pier import routine. +async fn import( + State((config, jwt_key, sessions)): State<( + Arc, + Option>, + Arc, + )>, + Path(patp): Path, + req: Request, +) -> axum::response::Result<(StatusCode, String)> { + let Config { + staging_dir, + imported_dir, + .. + } = &*config; + + let patp: Arc = Arc::from(patp); + + // If we have a jwt secret, then use it to verify the requests + if let Some(secret) = jwt_key { + let jwt = req + .headers() + .get(hyper::header::AUTHORIZATION) + .and_then(|v| v.to_str().ok()) + .ok_or((StatusCode::UNAUTHORIZED, "JWT authorization missing"))?; + + let jwt = jsonwebtoken::decode::(jwt, &secret, &Validation::new(Algorithm::HS256)) + .map_err(|e| { + error!("{e:?}"); + (StatusCode::UNAUTHORIZED, "Invalid JWT supplied") + })?; + + if jwt.claims.sub != &*patp { + debug!( + "Mismatched subject provided ({} vs. {})", + jwt.claims.sub, patp + ); + return Err((StatusCode::UNAUTHORIZED, "Invalid JWT supplied").into()); + } + } + + let out_dir = staging_dir.join(&*patp); + + // This critical section ensures that we cannot attempt to import twice to the same dir. + let guard = CS.lock().await; + + // Due to the given order we do not risk toctou - dir rename operation is atomic. + + if out_dir.exists() { + return Err(( + StatusCode::CONFLICT, + "Pier already being imported".to_string(), + ) + .into_response() + .into()); + } + + let imported_out_dir = imported_dir.join(&*patp); + + if imported_out_dir.exists() { + return Err((StatusCode::CONFLICT, "Pier already imported".to_string()) + .into_response() + .into()); + } + + debug!("Got request: {out_dir:?}"); + let mut pier = StandardUnpack::new(out_dir.clone(), None).await?; + + // Drop the critical section so we can have multiple imports at the same time + core::mem::drop(guard); + + let (parts, body) = req.into_parts(); + + let mut stream: Pin> = Box::pin( + body.map_err(|v| { + debug!("ERROR: {v:?}"); + std::io::Error::from(std::io::ErrorKind::UnexpectedEof) + }) + .into_async_read(), + ); + + let mut content_type = parts + .headers + .get(hyper::header::CONTENT_TYPE) + .and_then(|v| v.to_str().ok()); + + let info = loop { + match content_type { + Some("application/zip") => { + let mut temp_zip = out_dir.clone(); + temp_zip.set_extension("zip"); + + let mut temp_file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&temp_zip) + .await + .map_err(Error::from)?; + + // Immediately unlink the file - it will still be written to the filesystem, but it + // will be automatically released after the file is fully closed. + tokio::fs::remove_file(temp_zip) + .await + .map_err(Error::from)?; + + futures::io::copy(stream, &mut (&mut temp_file).compat()) + .await + .map_err(Error::from)?; + + // TODO: consider returning 202 before extracting the pier + break import_zip_file(temp_file.compat(), &mut pier).await?; + } + Some("application/zstd") => break import_zstd_stream(stream, &mut pier).await?, + Some("application/json") => { + content_type = { + let mut data = String::new(); + + stream + .read_to_string(&mut data) + .await + .map_err(Error::from)?; + + let payload: ImportPayload = + serde_json::from_str(&data).map_err(Error::from)?; + + let mut req = CLIENT.get(&payload.url); + + if let Some(auth) = payload.authorization { + req = req.header(hyper::header::AUTHORIZATION, auth); + } + + let resp = req.send().await.map_err(Error::from)?; + let resp = resp.error_for_status().map_err(Error::from)?; + + stream = Box::pin( + resp.bytes_stream() + .map_err(|v| { + debug!("ERROR: {v:?}"); + std::io::Error::from(std::io::ErrorKind::UnexpectedEof) + }) + .into_async_read(), + ); + + Some(match payload.format { + ImportFormat::Zip => "application/zip", + ImportFormat::Zstd => "application/zstd", + }) + } + } + _ => { + return Err(Response::builder() + .status(StatusCode::NOT_ACCEPTABLE) + .body("Only zip or zstd tar streams are accepted".to_string()) + .unwrap() + .into()) + } + } + }; + + // past this point we can return 202 ACCEPTED and move to another thread. + let session_id = sessions.alloc_session_id(); + + let jh = tokio::spawn({ + let patp = patp.clone(); + let sessions = sessions.clone(); + async move { + let ret = if let Err(e) = finish_import( + patp.clone(), + info, + pier, + out_dir, + imported_out_dir, + config.clone(), + ) + .await + { + error!("Could not finish import: {e:?}"); + sessions.finish_session(patp, session_id, Err(e)).await + } else { + debug!("Import finished"); + sessions.finish_session(patp, session_id, Ok(())).await + }; + + if let Err(e) = ret { + error!("Could not finish the session: {e:?}"); + } + } + }); + + sessions.new_session(patp, session_id, jh).await; + + Ok((StatusCode::ACCEPTED, session_id.to_string())) +} + +/// Asynchronous import, run under specific session ID. +async fn finish_import( + patp: Arc, + info: UrbitPier, + mut pier: StandardUnpack, + out_dir: PathBuf, + imported_out_dir: PathBuf, + config: Arc, +) -> Result<()> { + let Config { + db_path, + cache_dir, + imported_dir, + .. + } = &*config; + + debug!("{patp} - {info:?}"); + + let (os, arch) = current_os_arch(); + debug!("{patp} - {os} {arch}"); + let version = info.vere_version(db_path, Some((os, arch))).await?; + + debug!("{patp} - {version:?}"); + + let loom = pier.detect_loom().await?; + + if config + .max_loom + .zip(loom) + .map(|(a, b)| a > b) + .unwrap_or(false) + { + return Err(anyhow::anyhow!("Detected loom too large").into()); + } + + pier.set_loom(loom); + + let pier = pier + .post_unpack( + VereVersion { + os: os.to_string(), + arch: arch.to_string(), + ..version + }, + db_path, + cache_dir, + &config.post_unpack, + ) + .await?; + + tokio::fs::create_dir_all(imported_dir) + .await + .map_err(Error::from)?; + + tokio::fs::rename(out_dir, &imported_out_dir) + .await + .map_err(Error::from)?; + + if let Some(proxy) = config.proxy_import_base.as_deref() { + // 16MB buffer + let (tx, rx) = tokio::io::duplex(0x1000000); + + let proxy_url = format!("{proxy}/~{patp}"); + let mut req = CLIENT.post(&proxy_url); + + req = req + .header(hyper::header::CONTENT_TYPE, "application/zstd") + .body(reqwest::Body::wrap_stream(ReaderStream::new(rx))); + + let export = export_dir(&imported_out_dir, tx.compat()); + + let (a, b) = futures::join! { + export, + req.send() + }; + + a?; + let resp = b?; + + if let Err(e) = resp.error_for_status_ref() { + error!("Error importing: {e:?}"); + let text = resp.text().await; + error!("Import error text: {text:?}"); + return Err(e.into()); + } else { + // Poll the downstream source to completion. + if resp.status() == hyper::StatusCode::ACCEPTED { + let id = resp.text().await?; + let poll_url = format!("{proxy_url}/{id}"); + loop { + let req = CLIENT.get(&poll_url); + if let Ok(resp) = req.send().await { + if let Err(e) = resp.error_for_status_ref() { + error!("Error polling for downstream completion: {e:?}"); + let text = resp.text().await; + error!("Import poll error text: {text:?}"); + return Err(e.into()); + } + + let data = resp.json::().await?; + + match data { + ImportStatus::Done => break, + ImportStatus::Failed { reason } => { + error!("Error in downstream import: {reason:?}"); + return Err(Error::from(anyhow::anyhow!( + "Downstream failed to import: {reason:?}" + ))); + } + ImportStatus::Importing { .. } => (), + } + } + + // Do half a minute, because 60 seconds is how long the result must be held + // according to pierport spec. + // TODO: this should probably be configurable. + tokio::time::sleep(Duration::from_secs(30)).await; + } + } + + tokio::fs::remove_dir_all(imported_out_dir).await?; + } + } + + core::mem::drop(pier); + + Ok(()) +} + +// Auxiliary structures + +/// Required claims used to authenticate JWT +#[derive(Debug, Serialize, Deserialize)] +struct Claims { + iss: Option, + iat: Option, + exp: usize, + sub: String, +} + +struct Session { + result: core::result::Result<(Result<()>, Instant), JoinHandle<()>>, +} + +impl Session { + fn new(jh: JoinHandle<()>) -> Self { + Self { result: Err(jh) } + } + + fn finish_session(&mut self, res: Result<()>) { + self.result = Ok((res, Instant::now())); + } +} + +#[derive(Default)] +struct Sessions { + cnt: AtomicU64, + sessions: Mutex, BTreeMap>>, +} + +impl Sessions { + fn alloc_session_id(&self) -> u64 { + self.cnt.fetch_add(1, Ordering::Relaxed) + } + + async fn new_session(&self, patp: Arc, id: u64, jh: JoinHandle<()>) { + let session = Session::new(jh); + let mut sessions = self.sessions.lock().await; + sessions.entry(patp).or_default().insert(id, session); + } + + async fn finish_session(&self, patp: Arc, id: u64, res: Result<()>) -> Result<()> { + let mut sessions = self.sessions.lock().await; + + sessions + .get_mut(&patp) + .and_then(|v| v.get_mut(&id)) + .ok_or_else(|| anyhow::anyhow!("Session does not exist"))? + .finish_session(res); + + Ok(()) + } + + async fn cleanup_stale_sessions(&self, stale_time: Duration) { + let mut sessions = self.sessions.lock().await; + sessions.retain(|_, v| { + v.retain(|_, v| match &v.result { + Ok((_, time)) if time.elapsed() >= stale_time => true, + _ => false, + }); + !v.is_empty() + }); + } + + async fn get_session_status( + &self, + patp: &str, + id: u64, + ) -> Result>> { + let mut sessions = self.sessions.lock().await; + + Ok(sessions + .get_mut(patp) + .and_then(|v| v.get_mut(&id)) + .ok_or_else(|| anyhow::anyhow!("Session does not exist"))? + .result + .as_ref() + .ok() + .map(|v| v.0.as_ref().map(|_| ()).map_err(|v| v.to_string()))) + } +}