From 95876c43afd3e8381c4920a5794007e8f87b237b Mon Sep 17 00:00:00 2001 From: Noah Martin Date: Wed, 17 Dec 2025 20:02:19 -0500 Subject: [PATCH 01/12] feat(preprod): Add snapshots subcommand --- Cargo.lock | 689 +++++++++++++++++- Cargo.toml | 3 + src/commands/build/mod.rs | 2 + src/commands/build/snapshots.rs | 208 ++++++ .../_cases/build/build-help.trycmd | 5 +- .../_cases/build/build-snapshots-help.trycmd | 50 ++ tests/integration/build/mod.rs | 5 + 7 files changed, 954 insertions(+), 8 deletions(-) create mode 100644 src/commands/build/snapshots.rs create mode 100644 tests/integration/_cases/build/build-snapshots-help.trycmd diff --git a/Cargo.lock b/Cargo.lock index 92f3b7c653..a74867d89d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,6 +193,30 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "async-compression" +version = "0.4.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" +dependencies = [ + "compression-codecs", + "compression-core", + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -483,6 +507,23 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "compression-codecs" +version = "0.4.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" +dependencies = [ + "compression-core", + "zstd", + "zstd-safe", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + [[package]] name = "console" version = "0.15.11" @@ -524,6 +565,16 @@ dependencies = [ "memchr", ] +[[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.7" @@ -563,6 +614,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -630,7 +696,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "socket2", + "socket2 0.6.0", "windows-sys 0.59.0", ] @@ -823,6 +889,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -897,6 +975,21 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1180,6 +1273,52 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hickory-proto" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna", + "ipnet", + "once_cell", + "rand 0.9.2", + "ring", + "thiserror 2.0.17", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "moka", + "once_cell", + "parking_lot", + "rand 0.9.2", + "resolv-conf", + "smallvec", + "thiserror 2.0.17", + "tokio", + "tracing", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1291,6 +1430,39 @@ dependencies = [ "pin-utils", "smallvec", "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", ] [[package]] @@ -1299,13 +1471,24 @@ version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ + "base64 0.22.1", "bytes", + "futures-channel", "futures-core", + "futures-util", "http", "http-body", "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", + "socket2 0.6.0", + "system-configuration", "tokio", + "tower-service", + "tracing", + "windows-registry", ] [[package]] @@ -1491,6 +1674,12 @@ dependencies = [ "regex", ] +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" + [[package]] name = "inout" version = "0.1.4" @@ -1534,6 +1723,34 @@ dependencies = [ "libc", ] +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2 0.5.10", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-terminal" version = "0.4.16" @@ -1748,6 +1965,12 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" +[[package]] +name = "mediatype" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120fa187be19d9962f0926633453784691731018a2bf936ddb4e29101b79c4a7" + [[package]] name = "memchr" version = "2.7.6" @@ -1763,6 +1986,12 @@ dependencies = [ "libc", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1813,6 +2042,41 @@ dependencies = [ "tokio", ] +[[package]] +name = "moka" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" +dependencies = [ + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "equivalent", + "parking_lot", + "portable-atomic", + "rustc_version", + "smallvec", + "tagptr", + "uuid", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -1878,11 +2142,48 @@ dependencies = [ "memchr", ] +[[package]] +name = "objectstore-client" +version = "0.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "260ccf94ed55dc83c0a5b470a4d9404d9f15e4b975d6c6e734e89296eef75b59" +dependencies = [ + "async-compression", + "bytes", + "futures-util", + "infer", + "objectstore-types", + "reqwest", + "sentry-core", + "serde", + "thiserror 2.0.17", + "tokio", + "tokio-util", + "url", +] + +[[package]] +name = "objectstore-types" +version = "0.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da8b79e6156554da409ff350f06dea1b5ea0b1f863ca753fe9222a74e917592b" +dependencies = [ + "http", + "humantime", + "mediatype", + "serde", + "thiserror 2.0.17", +] + [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "once_cell_polyfill" @@ -1900,6 +2201,32 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "openssl-probe" version = "0.1.6" @@ -1917,9 +2244,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -2164,6 +2491,12 @@ dependencies = [ "time", ] +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + [[package]] name = "potential_utf" version = "0.1.3" @@ -2428,6 +2761,71 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" +[[package]] +name = "reqwest" +version = "0.12.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "hickory-resolver", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "resolv-conf" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rstest" version = "0.18.2" @@ -2526,6 +2924,39 @@ dependencies = [ "windows-sys 0.61.1", ] +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -2621,6 +3052,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.9.4", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework-sys" version = "2.15.0" @@ -2708,6 +3152,7 @@ dependencies = [ "mac-process-info", "magic_string", "mockito", + "objectstore-client", "open", "openssl-probe", "parking_lot", @@ -2728,10 +3173,12 @@ dependencies = [ "serde_json", "serial_test", "sha1_smol", + "sha2", "sourcemap", "symbolic", "tempfile", "thiserror 1.0.69", + "tokio", "trycmd", "url", "uuid", @@ -2973,6 +3420,16 @@ dependencies = [ "anstream", ] +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.0" @@ -3144,6 +3601,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -3155,6 +3621,33 @@ dependencies = [ "syn", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.9.4", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tap" version = "1.0.1" @@ -3282,6 +3775,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.47.1" @@ -3296,10 +3804,42 @@ dependencies = [ "parking_lot", "pin-project-lite", "slab", - "socket2", + "socket2 0.6.0", + "tokio-macros", "windows-sys 0.59.0", ] +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.16" @@ -3342,6 +3882,51 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.9.4", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.41" @@ -3373,6 +3958,12 @@ 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 = "trycmd" version = "0.14.21" @@ -3434,6 +4025,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +[[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.7" @@ -3514,6 +4111,15 @@ dependencies = [ "winapi-util", ] +[[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.1+wasi-snapshot-preview1" @@ -3571,6 +4177,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.104" @@ -3603,6 +4222,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.214.0" @@ -3660,6 +4292,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + [[package]] name = "winapi" version = "0.3.9" @@ -3700,8 +4338,8 @@ dependencies = [ "windows-implement", "windows-interface", "windows-link 0.2.0", - "windows-result", - "windows-strings", + "windows-result 0.4.0", + "windows-strings 0.5.0", ] [[package]] @@ -3738,6 +4376,26 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-result" version = "0.4.0" @@ -3747,6 +4405,15 @@ dependencies = [ "windows-link 0.2.0", ] +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-strings" version = "0.5.0" @@ -4053,6 +4720,16 @@ dependencies = [ "memchr", ] +[[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 = "wit-bindgen" version = "0.46.0" diff --git a/Cargo.toml b/Cargo.toml index 189837ad44..7fa90334bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,9 @@ chrono-tz = "0.8.4" secrecy = "0.8.0" lru = "0.16.0" backon = { version = "1.5.2", features = ["std", "std-blocking-sleep"] } +objectstore-client = "0.0.14" +sha2 = "0.10" +tokio = { version = "1.47", features = ["rt", "macros"] } [dev-dependencies] assert_cmd = "2.0.11" diff --git a/src/commands/build/mod.rs b/src/commands/build/mod.rs index 72b2216b78..d302aa0791 100644 --- a/src/commands/build/mod.rs +++ b/src/commands/build/mod.rs @@ -3,10 +3,12 @@ use clap::{ArgMatches, Command}; use crate::utils::args::ArgExt as _; +pub mod snapshots; pub mod upload; macro_rules! each_subcommand { ($mac:ident) => { + $mac!(snapshots); $mac!(upload); }; } diff --git a/src/commands/build/snapshots.rs b/src/commands/build/snapshots.rs new file mode 100644 index 0000000000..2c0e3ff99c --- /dev/null +++ b/src/commands/build/snapshots.rs @@ -0,0 +1,208 @@ +use std::fs; +use std::path::Path; + +use anyhow::{Context as _, Result}; +use clap::{Arg, ArgMatches, Command}; +use console::style; +use log::{debug, info}; +use objectstore_client::{Client, Usecase}; +use sha2::{Digest as _, Sha256}; +use walkdir::WalkDir; + +use crate::config::Config; +use crate::utils::args::ArgExt as _; + +const EXPERIMENTAL_WARNING: &str = + "[EXPERIMENTAL] The \"build snapshots\" command is experimental. \ + The command is subject to breaking changes, including removal, in any Sentry CLI release."; + +pub fn make_command(command: Command) -> Command { + command + .about("[EXPERIMENTAL] Upload build snapshots to a project.") + .long_about(format!( + "Upload build snapshots to a project.\n\n{EXPERIMENTAL_WARNING}" + )) + .org_arg() + .project_arg(true) + .arg( + Arg::new("path") + .value_name("PATH") + .help("The path to the folder containing build snapshots.") + .required(true), + ) + .arg( + Arg::new("snapshot_id") + .long("snapshot-id") + .value_name("ID") + .help("The snapshot identifier to associate with the upload.") + .required(true), + ) + .arg( + Arg::new("shard_index") + .long("shard-index") + .value_name("INDEX") + .value_parser(clap::value_parser!(u32)) + .default_value("0") + .help("The shard index for this snapshot upload."), + ) +} + +pub fn execute(matches: &ArgMatches) -> Result<()> { + eprintln!("{EXPERIMENTAL_WARNING}"); + + let config = Config::current(); + let org = config.get_org(matches)?; + let project = config.get_project(matches)?; + + let path = matches + .get_one::("path") + .expect("path argument is required"); + let snapshot_id = matches + .get_one::("snapshot_id") + .expect("snapshot_id argument is required"); + let shard_index = matches + .get_one::("shard_index") + .expect("shard_index has a default value"); + + info!("Processing build snapshots from: {path}"); + info!("Using snapshot ID: {snapshot_id}"); + info!("Shard index: {shard_index}"); + info!("Organization: {org}"); + info!("Project: {project}"); + + // Collect files to upload + let files = collect_files(Path::new(path))?; + + if files.is_empty() { + println!("{} No files found to upload", style("!").yellow()); + return Ok(()); + } + + println!( + "{} Found {} {} to upload", + style(">").dim(), + style(files.len()).yellow(), + if files.len() == 1 { "file" } else { "files" } + ); + + // Upload files using objectstore client + upload_files(&files, &org, &project, snapshot_id, *shard_index)?; + + println!("{} Successfully uploaded snapshots", style(">").dim()); + Ok(()) +} + +fn collect_files(path: &Path) -> Result> { + if !path.exists() { + anyhow::bail!("Path does not exist: {}", path.display()); + } + + let mut files = Vec::new(); + + if path.is_file() { + // Only add if not hidden + if !is_hidden_file(path) { + files.push(path.to_path_buf()); + } + } else if path.is_dir() { + for entry in WalkDir::new(path) + .follow_links(true) + .into_iter() + .filter_map(Result::ok) + { + if entry.metadata()?.is_file() { + let entry_path = entry.path(); + // Skip hidden files + if !is_hidden_file(entry_path) { + files.push(entry_path.to_path_buf()); + } + } + } + } else { + anyhow::bail!("Path is neither a file nor directory: {}", path.display()); + } + + Ok(files) +} + +fn is_hidden_file(path: &Path) -> bool { + path.file_name() + .and_then(|name| name.to_str()) + .map(|name| name.starts_with('.')) + .unwrap_or(false) +} + +fn is_json_file(path: &Path) -> bool { + path.extension() + .and_then(|ext| ext.to_str()) + .map(|ext| ext.eq_ignore_ascii_case("json")) + .unwrap_or(false) +} + +fn upload_files( + files: &[std::path::PathBuf], + org: &str, + project: &str, + snapshot_id: &str, + _shard_index: u32, +) -> Result<()> { + // Create objectstore client + let client = Client::new("http://127.0.0.1:8888/")?; + let session = Usecase::new("preprod").for_project(1, 2).session(&client)?; + + // Create a multi-threaded tokio runtime for async operations + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .context("Failed to create tokio runtime")?; + + for file_path in files { + debug!("Processing file: {}", file_path.display()); + + // Read file contents + let contents = fs::read(file_path) + .with_context(|| format!("Failed to read file: {}", file_path.display()))?; + + // Determine the key based on file type + let key = if is_json_file(file_path) { + // For JSON files, use {org}/{snapshotId}/{filename} + let filename = file_path + .file_name() + .and_then(|name| name.to_str()) + .unwrap_or("unknown.json"); + format!("{org}/{snapshot_id}/{filename}") + } else { + // For other files, use {org}/{project}/{hash} + let hash = compute_sha256_hash(&contents); + format!("{org}/{project}/{hash}") + }; + + info!("Uploading {} as {key}", file_path.display()); + + // Upload to objectstore using the runtime thread pool + runtime.block_on(async { + session + .put(contents) + .key(&key) + .send() + .await + .with_context(|| format!("Failed to upload file: {}", file_path.display())) + })?; + + println!( + "{} Uploaded {} (key: {})", + style(">").dim(), + file_path.display(), + style(&key).cyan() + ); + } + + Ok(()) +} + +fn compute_sha256_hash(data: &[u8]) -> String { + let mut hasher = Sha256::new(); + hasher.update(data); + let result = hasher.finalize(); + format!("{result:x}") +} diff --git a/tests/integration/_cases/build/build-help.trycmd b/tests/integration/_cases/build/build-help.trycmd index ca926a2b6b..a04443131c 100644 --- a/tests/integration/_cases/build/build-help.trycmd +++ b/tests/integration/_cases/build/build-help.trycmd @@ -6,8 +6,9 @@ Manage builds. Usage: sentry-cli[EXE] build [OPTIONS] Commands: - upload Upload builds to a project. - help Print this message or the help of the given subcommand(s) + snapshots [EXPERIMENTAL] Upload build snapshots to a project. + upload Upload builds to a project. + help Print this message or the help of the given subcommand(s) Options: -o, --org The organization ID or slug. diff --git a/tests/integration/_cases/build/build-snapshots-help.trycmd b/tests/integration/_cases/build/build-snapshots-help.trycmd new file mode 100644 index 0000000000..e9daddb3d6 --- /dev/null +++ b/tests/integration/_cases/build/build-snapshots-help.trycmd @@ -0,0 +1,50 @@ +``` +$ sentry-cli build snapshots --help +? success +Upload build snapshots to a project. + +[EXPERIMENTAL] The "build snapshots" command is experimental. The command is subject to breaking +changes, including removal, in any Sentry CLI release. + +Usage: sentry-cli build snapshots [OPTIONS] --snapshot-id + +Arguments: + + The path to the folder containing build snapshots. + +Options: + -o, --org + The organization ID or slug. + + --header + Custom headers that should be attached to all requests + in key:value format. + + -p, --project + The project ID or slug. + + --auth-token + Use the given Sentry auth token. + + --snapshot-id + The snapshot identifier to associate with the upload. + + --log-level + Set the log output verbosity. [possible values: trace, debug, info, warn, error] + + --shard-index + The shard index for this snapshot upload. + + [default: 0] + + --quiet + Do not print any output while preserving correct exit code. This flag is currently + implemented only for selected subcommands. + + [aliases: --silent] + + -h, --help + Print help (see a summary with '-h') + +``` + diff --git a/tests/integration/build/mod.rs b/tests/integration/build/mod.rs index f73e2c8f36..239def73c5 100644 --- a/tests/integration/build/mod.rs +++ b/tests/integration/build/mod.rs @@ -6,3 +6,8 @@ mod upload; fn command_build_help() { TestManager::new().register_trycmd_test("build/build-help.trycmd"); } + +#[test] +fn command_build_snapshots_help() { + TestManager::new().register_trycmd_test("build/build-snapshots-help.trycmd"); +} From e45f588f9979162c1aff442ada3909b3b85f64e3 Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Tue, 27 Jan 2026 15:43:34 +0100 Subject: [PATCH 02/12] update deps --- Cargo.lock | 487 ++++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 6 +- 2 files changed, 483 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a74867d89d..a8ce28058a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,6 +206,28 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -255,6 +277,12 @@ dependencies = [ "windows-link 0.2.0", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" @@ -277,6 +305,12 @@ dependencies = [ "vsimd", ] +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "binary-merge" version = "0.1.2" @@ -550,6 +584,12 @@ dependencies = [ "windows-sys 0.61.1", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "constant_time_eq" version = "0.3.1" @@ -654,6 +694,18 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -715,6 +767,33 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "data-encoding" version = "2.9.0" @@ -737,6 +816,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "deranged" version = "0.5.4" @@ -770,6 +860,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -850,6 +941,44 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.15.0" @@ -865,6 +994,27 @@ dependencies = [ "string_cache", ] +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "elsa" version = "1.11.2" @@ -935,6 +1085,22 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "filetime" version = "0.2.26" @@ -1108,6 +1274,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1200,6 +1367,17 @@ dependencies = [ "scroll 0.12.0", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.4.12" @@ -1319,6 +1497,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1820,11 +2007,37 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "10.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1" +dependencies = [ + "base64 0.22.1", + "ed25519-dalek", + "getrandom 0.2.16", + "hmac", + "js-sys", + "p256", + "p384", + "pem", + "rand 0.8.5", + "rsa", + "serde", + "serde_json", + "sha2", + "signature", + "simple_asn1", +] + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "leb128" @@ -1850,6 +2063,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "libredox" version = "0.1.10" @@ -1992,6 +2211,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2060,6 +2289,23 @@ dependencies = [ "uuid", ] +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "memchr", + "mime", + "spin", + "version_check", +] + [[package]] name = "native-tls" version = "0.2.14" @@ -2112,12 +2358,58 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[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-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2125,6 +2417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2144,14 +2437,17 @@ dependencies = [ [[package]] name = "objectstore-client" -version = "0.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260ccf94ed55dc83c0a5b470a4d9404d9f15e4b975d6c6e734e89296eef75b59" +version = "0.0.15" +source = "git+https://github.com/getsentry/objectstore.git?branch=lcian%2Ffeat%2Frust-batch-client#75a144ab923a258395afdff98b65eb6839af68cc" dependencies = [ "async-compression", + "async-stream", + "base64 0.22.1", "bytes", "futures-util", "infer", + "jsonwebtoken", + "multer", "objectstore-types", "reqwest", "sentry-core", @@ -2160,13 +2456,13 @@ dependencies = [ "tokio", "tokio-util", "url", + "uuid", ] [[package]] name = "objectstore-types" -version = "0.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da8b79e6156554da409ff350f06dea1b5ea0b1f863ca753fe9222a74e917592b" +version = "0.0.15" +source = "git+https://github.com/getsentry/objectstore.git?branch=lcian%2Ffeat%2Frust-batch-client#75a144ab923a258395afdff98b65eb6839af68cc" dependencies = [ "http", "humantime", @@ -2293,6 +2589,30 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "parking_lot" version = "0.12.4" @@ -2366,6 +2686,25 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64 0.22.1", + "serde_core", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -2466,6 +2805,27 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -2568,6 +2928,15 @@ dependencies = [ "unicode-width 0.1.14", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.101" @@ -2637,6 +3006,8 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -2646,10 +3017,20 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.3", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -2665,6 +3046,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] [[package]] name = "rand_core" @@ -2784,6 +3168,7 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "native-tls", "once_cell", "percent-encoding", @@ -2812,6 +3197,16 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.17.14" @@ -2826,6 +3221,26 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rstest" version = "0.18.2" @@ -3043,6 +3458,20 @@ version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "secrecy" version = "0.8.0" @@ -3359,6 +3788,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -3371,6 +3810,18 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.17", + "time", +] + [[package]] name = "siphasher" version = "1.0.1" @@ -3459,6 +3910,22 @@ dependencies = [ "url", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -4001,6 +4468,12 @@ dependencies = [ "libc", ] +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-id-start" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index 7fa90334bc..8e0981d407 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ java-properties = "2.0.0" lazy_static = "1.4.0" libc = "0.2.139" log = { version = "0.4.17", features = ["std"] } +objectstore-client = { git = "https://github.com/getsentry/objectstore.git", branch = "lcian/feat/rust-batch-client" } open = "3.2.0" parking_lot = "0.12.1" percent-encoding = "2.2.0" @@ -62,6 +63,7 @@ sentry = { version = "0.46.0", default-features = false, features = [ serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.93" sha1_smol = { version = "1.0.0", features = ["serde", "std"] } +sha2 = "0.10.9" sourcemap = { version = "9.3.0", features = ["ram_bundle"] } symbolic = { version = "12.13.3", features = ["debuginfo-serde", "il2cpp"] } thiserror = "1.0.38" @@ -77,9 +79,7 @@ chrono-tz = "0.8.4" secrecy = "0.8.0" lru = "0.16.0" backon = { version = "1.5.2", features = ["std", "std-blocking-sleep"] } -objectstore-client = "0.0.14" -sha2 = "0.10" -tokio = { version = "1.47", features = ["rt", "macros"] } +tokio = { version = "1.47", features = ["rt", "rt-multi-thread", "macros"] } [dev-dependencies] assert_cmd = "2.0.11" From f3cfadb4180974992ecd048b2b8f7b871a1283e6 Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Tue, 27 Jan 2026 15:44:34 +0100 Subject: [PATCH 03/12] no multiple projects, get rid of shard index --- src/commands/build/snapshots.rs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/commands/build/snapshots.rs b/src/commands/build/snapshots.rs index 2c0e3ff99c..62f41e8d91 100644 --- a/src/commands/build/snapshots.rs +++ b/src/commands/build/snapshots.rs @@ -23,7 +23,7 @@ pub fn make_command(command: Command) -> Command { "Upload build snapshots to a project.\n\n{EXPERIMENTAL_WARNING}" )) .org_arg() - .project_arg(true) + .project_arg(false) .arg( Arg::new("path") .value_name("PATH") @@ -37,14 +37,6 @@ pub fn make_command(command: Command) -> Command { .help("The snapshot identifier to associate with the upload.") .required(true), ) - .arg( - Arg::new("shard_index") - .long("shard-index") - .value_name("INDEX") - .value_parser(clap::value_parser!(u32)) - .default_value("0") - .help("The shard index for this snapshot upload."), - ) } pub fn execute(matches: &ArgMatches) -> Result<()> { @@ -60,13 +52,9 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { let snapshot_id = matches .get_one::("snapshot_id") .expect("snapshot_id argument is required"); - let shard_index = matches - .get_one::("shard_index") - .expect("shard_index has a default value"); info!("Processing build snapshots from: {path}"); info!("Using snapshot ID: {snapshot_id}"); - info!("Shard index: {shard_index}"); info!("Organization: {org}"); info!("Project: {project}"); @@ -86,7 +74,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { ); // Upload files using objectstore client - upload_files(&files, &org, &project, snapshot_id, *shard_index)?; + upload_files(&files, &org, &project, snapshot_id)?; println!("{} Successfully uploaded snapshots", style(">").dim()); Ok(()) @@ -144,7 +132,6 @@ fn upload_files( org: &str, project: &str, snapshot_id: &str, - _shard_index: u32, ) -> Result<()> { // Create objectstore client let client = Client::new("http://127.0.0.1:8888/")?; From 47940469c9da9886bc3ca131b9c4d40556c3cb51 Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:20:28 +0100 Subject: [PATCH 04/12] add organization details API util --- src/api/mod.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/api/mod.rs b/src/api/mod.rs index d810137269..8a966e80d2 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -962,6 +962,13 @@ impl AuthenticatedApi<'_> { } Ok(rv) } + + /// Fetch organization details including ID and links + pub fn fetch_organization_details(&self, org: &str) -> ApiResult { + let path = format!("/api/0/organizations/{}/", PathArg(org)); + self.get(&path)? + .convert_rnf(ApiErrorKind::OrganizationNotFound) + } } /// Available datasets for fetching organization events @@ -1761,6 +1768,20 @@ pub struct Organization { pub features: Vec, } +#[derive(Deserialize, Debug)] +pub struct OrganizationLinks { + #[serde(rename = "organizationUrl")] + pub organization_url: String, + #[serde(rename = "regionUrl")] + pub region_url: String, +} + +#[derive(Deserialize, Debug)] +pub struct OrganizationDetails { + pub id: String, + pub links: OrganizationLinks, +} + #[derive(Deserialize, Debug)] pub struct Team { #[expect(dead_code)] From c6e67e90705dbed5a2be8dd67c0700a000c4b0c8 Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:49:01 +0100 Subject: [PATCH 05/12] introduce necessary utils --- src/commands/build/snapshots.rs | 14 +++++++---- src/utils/api/mod.rs | 41 +++++++++++++++++++++++++++++++++ src/utils/mod.rs | 2 ++ src/utils/objectstore/mod.rs | 11 +++++++++ 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/utils/api/mod.rs create mode 100644 src/utils/objectstore/mod.rs diff --git a/src/commands/build/snapshots.rs b/src/commands/build/snapshots.rs index 62f41e8d91..8fc6593a12 100644 --- a/src/commands/build/snapshots.rs +++ b/src/commands/build/snapshots.rs @@ -9,8 +9,11 @@ use objectstore_client::{Client, Usecase}; use sha2::{Digest as _, Sha256}; use walkdir::WalkDir; +use crate::api::Api; use crate::config::Config; +use crate::utils::api::get_org_project_id; use crate::utils::args::ArgExt as _; +use crate::utils::objectstore::get_objectstore_url; const EXPERIMENTAL_WARNING: &str = "[EXPERIMENTAL] The \"build snapshots\" command is experimental. \ @@ -133,11 +136,14 @@ fn upload_files( project: &str, snapshot_id: &str, ) -> Result<()> { - // Create objectstore client - let client = Client::new("http://127.0.0.1:8888/")?; - let session = Usecase::new("preprod").for_project(1, 2).session(&client)?; + let url = get_objectstore_url(Api::current(), org)?; + let client = Client::new(url)?; + + let (org, project) = get_org_project_id(Api::current(), org, project)?; + let session = Usecase::new("preprod") + .for_project(org, project) + .session(&client)?; - // Create a multi-threaded tokio runtime for async operations let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() .build() diff --git a/src/utils/api/mod.rs b/src/utils/api/mod.rs new file mode 100644 index 0000000000..d80d2ad705 --- /dev/null +++ b/src/utils/api/mod.rs @@ -0,0 +1,41 @@ +//! Utilities that rely on the Sentry API. + +use crate::api::{Api, AuthenticatedApi}; +use anyhow::{Context, Result}; + +/// Given an org and project slugs or IDs, returns the IDs of both. +pub fn get_org_project_id(api: impl AsRef, org: &str, project: &str) -> Result<(u64, u64)> { + let authenticated_api = api.as_ref().authenticated()?; + let org_id = get_org_id(authenticated_api, org)?; + let authenticated_api = api.as_ref().authenticated()?; + let project_id = get_project_id(authenticated_api, org, project)?; + Ok((org_id, project_id)) +} + +/// Given an org slug or ID, returns its ID as a number. +fn get_org_id(api: AuthenticatedApi<'_>, org: &str) -> Result { + if let Ok(id) = org.parse::() { + return Ok(id); + } + let details = api.fetch_organization_details(org)?; + Ok(details + .id + .parse::() + .context("Unable to parse org id")?) +} + +/// Given an org and project slugs or IDs, returns the project ID. +fn get_project_id(api: AuthenticatedApi<'_>, org: &str, project: &str) -> Result { + if let Ok(id) = project.parse::() { + return Ok(id); + } + + let projects = api.list_organization_projects(org)?; + for p in projects { + if p.slug == project { + return p.id.parse::().context("Unable to parse project id"); + } + } + + anyhow::bail!("Project not found") +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 9f47f2a88a..004f5cc83f 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,5 +1,6 @@ //! Various utility functionality. pub mod android; +pub mod api; pub mod args; pub mod auth_token; pub mod build; @@ -16,6 +17,7 @@ pub mod fs; pub mod http; pub mod logging; pub mod non_empty; +pub mod objectstore; pub mod progress; pub mod proguard; pub mod releases; diff --git a/src/utils/objectstore/mod.rs b/src/utils/objectstore/mod.rs new file mode 100644 index 0000000000..eba3bfa203 --- /dev/null +++ b/src/utils/objectstore/mod.rs @@ -0,0 +1,11 @@ +//! Utilities to work with the Objectstore service. + +use anyhow::Result; + +use crate::api::Api; + +pub fn get_objectstore_url(api: impl AsRef, org: &str) -> Result { + let api = api.as_ref().authenticated()?; + let base = api.fetch_organization_details(org)?.links.region_url; + Ok(format!("{base}/api/0/objectstore")) +} From 1cd38b72c538b3842c2aec5c9b0f5081d8c64c34 Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Wed, 28 Jan 2026 10:28:09 +0100 Subject: [PATCH 06/12] use batch API --- src/commands/build/snapshots.rs | 53 +++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/commands/build/snapshots.rs b/src/commands/build/snapshots.rs index 8fc6593a12..7854bf3c6d 100644 --- a/src/commands/build/snapshots.rs +++ b/src/commands/build/snapshots.rs @@ -79,7 +79,6 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { // Upload files using objectstore client upload_files(&files, &org, &project, snapshot_id)?; - println!("{} Successfully uploaded snapshots", style(">").dim()); Ok(()) } @@ -149,14 +148,14 @@ fn upload_files( .build() .context("Failed to create tokio runtime")?; + let mut many_builder = session.many(); + for file_path in files { debug!("Processing file: {}", file_path.display()); - // Read file contents let contents = fs::read(file_path) .with_context(|| format!("Failed to read file: {}", file_path.display()))?; - // Determine the key based on file type let key = if is_json_file(file_path) { // For JSON files, use {org}/{snapshotId}/{filename} let filename = file_path @@ -170,27 +169,37 @@ fn upload_files( format!("{org}/{project}/{hash}") }; - info!("Uploading {} as {key}", file_path.display()); - - // Upload to objectstore using the runtime thread pool - runtime.block_on(async { - session - .put(contents) - .key(&key) - .send() - .await - .with_context(|| format!("Failed to upload file: {}", file_path.display())) - })?; - - println!( - "{} Uploaded {} (key: {})", - style(">").dim(), - file_path.display(), - style(&key).cyan() - ); + info!("Queueing {} as {key}", file_path.display()); + + many_builder = many_builder.push(session.put(contents).key(&key)); } - Ok(()) + let upload = runtime + .block_on(async { many_builder.send().await }) + .context("Failed to upload files")?; + + match upload.error_for_failures() { + Ok(()) => { + println!( + "{} Uploaded {} {}", + style(">").dim(), + style(files.len()).yellow(), + if files.len() == 1 { "file" } else { "files" } + ); + Ok(()) + } + Err(errors) => { + eprintln!("There were errors uploading files:"); + for error in &errors { + eprintln!(" {}", style(error).red()); + } + anyhow::bail!( + "Failed to upload {} out of {} files", + errors.len(), + files.len() + ) + } + } } fn compute_sha256_hash(data: &[u8]) -> String { From 15a89afeffdbbb948febf3f0694480a8da266f83 Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Wed, 28 Jan 2026 10:45:35 +0100 Subject: [PATCH 07/12] improve --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/api/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8ce28058a..e54fb5f02f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2438,7 +2438,7 @@ dependencies = [ [[package]] name = "objectstore-client" version = "0.0.15" -source = "git+https://github.com/getsentry/objectstore.git?branch=lcian%2Ffeat%2Frust-batch-client#75a144ab923a258395afdff98b65eb6839af68cc" +source = "git+https://github.com/getsentry/objectstore.git?branch=lcian%2Ffeat%2Frust-batch-client#1364c9d7ff5d1fe39c8c7be30c5c2f9d2c8c2ed2" dependencies = [ "async-compression", "async-stream", @@ -2462,7 +2462,7 @@ dependencies = [ [[package]] name = "objectstore-types" version = "0.0.15" -source = "git+https://github.com/getsentry/objectstore.git?branch=lcian%2Ffeat%2Frust-batch-client#75a144ab923a258395afdff98b65eb6839af68cc" +source = "git+https://github.com/getsentry/objectstore.git?branch=lcian%2Ffeat%2Frust-batch-client#1364c9d7ff5d1fe39c8c7be30c5c2f9d2c8c2ed2" dependencies = [ "http", "humantime", diff --git a/Cargo.toml b/Cargo.toml index 8e0981d407..f956fa4d59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,7 @@ chrono-tz = "0.8.4" secrecy = "0.8.0" lru = "0.16.0" backon = { version = "1.5.2", features = ["std", "std-blocking-sleep"] } -tokio = { version = "1.47", features = ["rt", "rt-multi-thread", "macros"] } +tokio = { version = "1.47", features = ["rt", "rt-multi-thread"] } [dev-dependencies] assert_cmd = "2.0.11" diff --git a/src/api/mod.rs b/src/api/mod.rs index 8a966e80d2..1e7a652c72 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -963,7 +963,7 @@ impl AuthenticatedApi<'_> { Ok(rv) } - /// Fetch organization details including ID and links + /// Fetch organization details pub fn fetch_organization_details(&self, org: &str) -> ApiResult { let path = format!("/api/0/organizations/{}/", PathArg(org)); self.get(&path)? From 1bc440d5b54dd9c86c0f8e84bdc3274b34546483 Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Wed, 28 Jan 2026 10:49:42 +0100 Subject: [PATCH 08/12] improve --- src/api/mod.rs | 2 -- tests/integration/_cases/build/build-snapshots-help.trycmd | 5 ----- 2 files changed, 7 deletions(-) diff --git a/src/api/mod.rs b/src/api/mod.rs index 1e7a652c72..994dbe48ba 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1770,8 +1770,6 @@ pub struct Organization { #[derive(Deserialize, Debug)] pub struct OrganizationLinks { - #[serde(rename = "organizationUrl")] - pub organization_url: String, #[serde(rename = "regionUrl")] pub region_url: String, } diff --git a/tests/integration/_cases/build/build-snapshots-help.trycmd b/tests/integration/_cases/build/build-snapshots-help.trycmd index e9daddb3d6..7dc541a5c1 100644 --- a/tests/integration/_cases/build/build-snapshots-help.trycmd +++ b/tests/integration/_cases/build/build-snapshots-help.trycmd @@ -32,11 +32,6 @@ Options: --log-level Set the log output verbosity. [possible values: trace, debug, info, warn, error] - --shard-index - The shard index for this snapshot upload. - - [default: 0] - --quiet Do not print any output while preserving correct exit code. This flag is currently implemented only for selected subcommands. From 3417b532d87684cb4283763bb16be34637f01d57 Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Wed, 28 Jan 2026 10:57:09 +0100 Subject: [PATCH 09/12] cargo clippy --fix --- src/utils/api/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/api/mod.rs b/src/utils/api/mod.rs index d80d2ad705..d8655d3287 100644 --- a/src/utils/api/mod.rs +++ b/src/utils/api/mod.rs @@ -1,7 +1,7 @@ //! Utilities that rely on the Sentry API. use crate::api::{Api, AuthenticatedApi}; -use anyhow::{Context, Result}; +use anyhow::{Context as _, Result}; /// Given an org and project slugs or IDs, returns the IDs of both. pub fn get_org_project_id(api: impl AsRef, org: &str, project: &str) -> Result<(u64, u64)> { @@ -18,10 +18,10 @@ fn get_org_id(api: AuthenticatedApi<'_>, org: &str) -> Result { return Ok(id); } let details = api.fetch_organization_details(org)?; - Ok(details + details .id .parse::() - .context("Unable to parse org id")?) + .context("Unable to parse org id") } /// Given an org and project slugs or IDs, returns the project ID. From 3f206ddc091435e1734ebca1ba57ee7685698c5b Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Wed, 28 Jan 2026 11:03:51 +0100 Subject: [PATCH 10/12] improve --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/commands/build/snapshots.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e54fb5f02f..a8ce28058a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2438,7 +2438,7 @@ dependencies = [ [[package]] name = "objectstore-client" version = "0.0.15" -source = "git+https://github.com/getsentry/objectstore.git?branch=lcian%2Ffeat%2Frust-batch-client#1364c9d7ff5d1fe39c8c7be30c5c2f9d2c8c2ed2" +source = "git+https://github.com/getsentry/objectstore.git?branch=lcian%2Ffeat%2Frust-batch-client#75a144ab923a258395afdff98b65eb6839af68cc" dependencies = [ "async-compression", "async-stream", @@ -2462,7 +2462,7 @@ dependencies = [ [[package]] name = "objectstore-types" version = "0.0.15" -source = "git+https://github.com/getsentry/objectstore.git?branch=lcian%2Ffeat%2Frust-batch-client#1364c9d7ff5d1fe39c8c7be30c5c2f9d2c8c2ed2" +source = "git+https://github.com/getsentry/objectstore.git?branch=lcian%2Ffeat%2Frust-batch-client#75a144ab923a258395afdff98b65eb6839af68cc" dependencies = [ "http", "humantime", diff --git a/Cargo.toml b/Cargo.toml index f956fa4d59..33925fed40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,7 @@ chrono-tz = "0.8.4" secrecy = "0.8.0" lru = "0.16.0" backon = { version = "1.5.2", features = ["std", "std-blocking-sleep"] } -tokio = { version = "1.47", features = ["rt", "rt-multi-thread"] } +tokio = { version = "1.47", features = ["rt"] } [dev-dependencies] assert_cmd = "2.0.11" diff --git a/src/commands/build/snapshots.rs b/src/commands/build/snapshots.rs index 7854bf3c6d..48731e3754 100644 --- a/src/commands/build/snapshots.rs +++ b/src/commands/build/snapshots.rs @@ -143,7 +143,7 @@ fn upload_files( .for_project(org, project) .session(&client)?; - let runtime = tokio::runtime::Builder::new_multi_thread() + let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() .build() .context("Failed to create tokio runtime")?; From e902bfc380311e82a15234f49d782f10775b9391 Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Wed, 28 Jan 2026 11:11:48 +0100 Subject: [PATCH 11/12] fmt all --- src/utils/api/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/utils/api/mod.rs b/src/utils/api/mod.rs index d8655d3287..53cc3c1918 100644 --- a/src/utils/api/mod.rs +++ b/src/utils/api/mod.rs @@ -18,10 +18,7 @@ fn get_org_id(api: AuthenticatedApi<'_>, org: &str) -> Result { return Ok(id); } let details = api.fetch_organization_details(org)?; - details - .id - .parse::() - .context("Unable to parse org id") + details.id.parse::().context("Unable to parse org id") } /// Given an org and project slugs or IDs, returns the project ID. From e5bd2d7496069214d573ad698cfe4f2da8ebb2c7 Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Wed, 28 Jan 2026 11:20:07 +0100 Subject: [PATCH 12/12] fix test --- tests/integration/_cases/build/build-snapshots-help.trycmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/_cases/build/build-snapshots-help.trycmd b/tests/integration/_cases/build/build-snapshots-help.trycmd index 7dc541a5c1..0525bb588c 100644 --- a/tests/integration/_cases/build/build-snapshots-help.trycmd +++ b/tests/integration/_cases/build/build-snapshots-help.trycmd @@ -6,7 +6,7 @@ Upload build snapshots to a project. [EXPERIMENTAL] The "build snapshots" command is experimental. The command is subject to breaking changes, including removal, in any Sentry CLI release. -Usage: sentry-cli build snapshots [OPTIONS] --snapshot-id +Usage: sentry-cli[EXE] build snapshots [OPTIONS] --snapshot-id Arguments: