Skip to content

Commit 28c9a0f

Browse files
chore(debug-files): Remove non-chunked uploading support (#2952)
### Description Removed support for non-chunked uploads of debug files. ### Issues - Resolves #2950 - Resolves [CLI-227](https://linear.app/getsentry/issue/CLI-227/remove-support-for-non-chunked-debug-file-uploads)
1 parent 137ab16 commit 28c9a0f

File tree

6 files changed

+12
-290
lines changed

6 files changed

+12
-290
lines changed

src/api/mod.rs

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod pagination;
1313

1414
use std::borrow::Cow;
1515
use std::cell::RefCell;
16-
use std::collections::{HashMap, HashSet};
16+
use std::collections::HashMap;
1717
use std::ffi::OsStr;
1818
use std::fs::File;
1919
use std::io::{self, Read as _, Write};
@@ -90,7 +90,6 @@ pub struct AuthenticatedApi<'a> {
9090

9191
pub struct RegionSpecificApi<'a> {
9292
api: &'a AuthenticatedApi<'a>,
93-
org: &'a str,
9493
region_url: Option<Box<str>>,
9594
}
9695

@@ -863,34 +862,6 @@ impl<'a> AuthenticatedApi<'a> {
863862
.map(|_| true)
864863
}
865864

866-
/// Given a list of checksums for DIFs, this returns a list of those
867-
/// that do not exist for the project yet.
868-
pub fn find_missing_dif_checksums<I>(
869-
&self,
870-
org: &str,
871-
project: &str,
872-
checksums: I,
873-
) -> ApiResult<HashSet<Digest>>
874-
where
875-
I: IntoIterator<Item = Digest>,
876-
{
877-
let mut url = format!(
878-
"/projects/{}/{}/files/dsyms/unknown/?",
879-
PathArg(org),
880-
PathArg(project)
881-
);
882-
for (idx, checksum) in checksums.into_iter().enumerate() {
883-
if idx > 0 {
884-
url.push('&');
885-
}
886-
url.push_str("checksums=");
887-
url.push_str(&checksum.to_string());
888-
}
889-
890-
let state: MissingChecksumsResponse = self.get(&url)?.convert()?;
891-
Ok(state.missing)
892-
}
893-
894865
/// Get the server configuration for chunked file uploads.
895866
pub fn get_chunk_upload_options(&self, org: &str) -> ApiResult<ChunkServerOptions> {
896867
let url = format!("/organizations/{}/chunk-upload/", PathArg(org));
@@ -1252,7 +1223,6 @@ impl<'a> AuthenticatedApi<'a> {
12521223
// Do not specify a region URL unless the URL is configured to https://sentry.io (i.e. the default).
12531224
return RegionSpecificApi {
12541225
api: self,
1255-
org,
12561226
region_url: None,
12571227
};
12581228
}
@@ -1279,7 +1249,6 @@ impl<'a> AuthenticatedApi<'a> {
12791249

12801250
RegionSpecificApi {
12811251
api: self,
1282-
org,
12831252
region_url,
12841253
}
12851254
}
@@ -1363,22 +1332,6 @@ impl RegionSpecificApi<'_> {
13631332
.request(method, url, self.region_url.as_deref())
13641333
}
13651334

1366-
/// Uploads a ZIP archive containing DIFs from the given path.
1367-
pub fn upload_dif_archive(&self, project: &str, file: &Path) -> ApiResult<Vec<DebugInfoFile>> {
1368-
let path = format!(
1369-
"/projects/{}/{}/files/dsyms/",
1370-
PathArg(self.org),
1371-
PathArg(project)
1372-
);
1373-
let mut form = curl::easy::Form::new();
1374-
form.part("file").file(file).add()?;
1375-
self.request(Method::Post, &path)?
1376-
.with_form_data(form)?
1377-
.progress_bar_mode(ProgressBarMode::Request)
1378-
.send()?
1379-
.convert()
1380-
}
1381-
13821335
/// Uploads a new release file. The file is loaded directly from the file
13831336
/// system and uploaded as `name`.
13841337
pub fn upload_release_file(
@@ -2096,11 +2049,6 @@ impl DebugInfoFile {
20962049
}
20972050
}
20982051

2099-
#[derive(Deserialize)]
2100-
struct MissingChecksumsResponse {
2101-
missing: HashSet<Digest>,
2102-
}
2103-
21042052
#[derive(Clone, Debug, Deserialize)]
21052053
#[serde(rename_all = "camelCase")]
21062054
pub struct Issue {

src/config.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use sentry::types::Dsn;
1919

2020
use crate::constants::CONFIG_INI_FILE_PATH;
2121
use crate::constants::DEFAULT_MAX_DIF_ITEM_SIZE;
22-
use crate::constants::DEFAULT_MAX_DIF_UPLOAD_SIZE;
2322
use crate::constants::{CONFIG_RC_FILE_NAME, DEFAULT_RETRIES, DEFAULT_URL};
2423
use crate::utils::args;
2524
use crate::utils::auth_token::AuthToken;
@@ -457,17 +456,6 @@ impl Config {
457456
)
458457
}
459458

460-
/// Returns the maximum DIF upload size
461-
pub fn get_max_dif_archive_size(&self) -> u64 {
462-
let key = "max_upload_size";
463-
464-
self.ini
465-
.get_from(Some("dif"), key)
466-
.or_else(|| self.ini.get_from(Some("dsym"), key))
467-
.and_then(|x| x.parse().ok())
468-
.unwrap_or(DEFAULT_MAX_DIF_UPLOAD_SIZE)
469-
}
470-
471459
/// Returns the maximum file size of a single file inside DIF bundle
472460
pub fn get_max_dif_item_size(&self) -> u64 {
473461
let key = "max_item_size";

src/constants.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ pub const DEFAULT_MAX_DIF_SIZE: u64 = 2 * 1024 * 1024 * 1024; // 2GB
4242
/// Default maximum file size of a single file inside DIF bundle.
4343
pub const DEFAULT_MAX_DIF_ITEM_SIZE: u64 = 1024 * 1024; // 1MB
4444
/// Default maximum DIF upload size.
45-
pub const DEFAULT_MAX_DIF_UPLOAD_SIZE: u64 = 35 * 1024 * 1024; // 35MB
4645
/// Default maximum time to wait for file assembly.
4746
pub const DEFAULT_MAX_WAIT: Duration = Duration::from_secs(5 * 60);
4847
/// Maximum length for commit SHA values, enforced in backend.

src/utils/dif_upload/mod.rs

Lines changed: 10 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use std::fs::{self, File};
1414
use std::io::{BufReader, BufWriter, Read as _, Seek as _, Write as _};
1515
use std::iter::IntoIterator;
1616
use std::mem::transmute;
17-
use std::ops::Deref;
1817
use std::path::{Component, Path, PathBuf};
1918
use std::process::Command;
2019
use std::str;
@@ -23,7 +22,6 @@ use std::time::Duration;
2322
use anyhow::{bail, format_err, Error, Result};
2423
use console::style;
2524
use log::{debug, info, warn};
26-
use sha1_smol::Digest;
2725
use symbolic::common::{Arch, AsSelf, ByteView, DebugId, SelfCell, Uuid};
2826
use symbolic::debuginfo::macho::{BcSymbolMap, UuidMapping};
2927
use symbolic::debuginfo::pe::PeObject;
@@ -33,26 +31,21 @@ use symbolic::il2cpp::ObjectLineMapping;
3331
use walkdir::WalkDir;
3432
use which::which;
3533
use zip::result::ZipError;
36-
use zip::write::SimpleFileOptions;
37-
use zip::{ZipArchive, ZipWriter};
34+
use zip::ZipArchive;
3835

3936
use self::error::ValidationError;
4037
use crate::api::{Api, ChunkServerOptions, ChunkUploadCapability};
4138
use crate::config::Config;
4239
use crate::constants::{DEFAULT_MAX_DIF_SIZE, DEFAULT_MAX_WAIT};
4340
use crate::utils::chunks;
44-
use crate::utils::chunks::{Assemblable, BatchedSliceExt as _, ChunkOptions, Chunked, ItemSize};
41+
use crate::utils::chunks::{Assemblable, ChunkOptions, Chunked};
4542
use crate::utils::dif::ObjectDifFeatures;
46-
use crate::utils::fs::{get_sha1_checksum, TempDir, TempFile};
43+
use crate::utils::fs::{TempDir, TempFile};
4744
use crate::utils::progress::{ProgressBar, ProgressStyle};
48-
use crate::utils::ui::{copy_with_progress, make_byte_progress_bar};
4945

5046
/// A debug info file on the server.
5147
pub use crate::api::DebugInfoFile;
5248

53-
/// Fallback maximum number of chunks in a batch for the legacy upload.
54-
static MAX_CHUNKS: u64 = 64;
55-
5649
/// A Debug Information File.
5750
///
5851
/// This is primarily used to store inside the [`DifMatch`] so does not contain any
@@ -212,11 +205,6 @@ impl<'data> DifMatch<'data> {
212205
}
213206
}
214207

215-
/// Returns the size of of this DIF in bytes.
216-
pub fn size(&self) -> u64 {
217-
self.data().len() as u64
218-
}
219-
220208
/// Returns the path of this DIF relative to the search origin.
221209
pub fn path(&self) -> &str {
222210
&self.name
@@ -315,40 +303,6 @@ impl Assemblable for DifMatch<'_> {
315303
}
316304
}
317305

318-
/// A `DifMatch` with computed SHA1 checksum.
319-
#[derive(Debug)]
320-
struct HashedDifMatch<'data> {
321-
inner: DifMatch<'data>,
322-
checksum: Digest,
323-
}
324-
325-
impl<'data> HashedDifMatch<'data> {
326-
/// Calculates the SHA1 checksum for the given DIF.
327-
fn from(inner: DifMatch<'data>) -> Result<Self> {
328-
let checksum = get_sha1_checksum(inner.data())?;
329-
Ok(HashedDifMatch { inner, checksum })
330-
}
331-
332-
/// Returns the SHA1 checksum of this DIF.
333-
fn checksum(&self) -> Digest {
334-
self.checksum
335-
}
336-
}
337-
338-
impl<'data> Deref for HashedDifMatch<'data> {
339-
type Target = DifMatch<'data>;
340-
341-
fn deref(&self) -> &Self::Target {
342-
&self.inner
343-
}
344-
}
345-
346-
impl ItemSize for HashedDifMatch<'_> {
347-
fn size(&self) -> u64 {
348-
self.deref().size()
349-
}
350-
}
351-
352306
type ZipFileArchive = ZipArchive<BufReader<File>>;
353307

354308
/// A handle to the source of a potential `DifMatch` used inside `search_difs`.
@@ -1250,130 +1204,6 @@ fn upload_difs_chunked(
12501204
chunks::upload_chunked_objects(&chunked, options)
12511205
}
12521206

1253-
/// Returns debug files missing on the server.
1254-
fn get_missing_difs<'data>(
1255-
objects: Vec<HashedDifMatch<'data>>,
1256-
options: &DifUpload,
1257-
) -> Result<Vec<HashedDifMatch<'data>>> {
1258-
info!(
1259-
"Checking for missing debug information files: {:#?}",
1260-
&objects
1261-
);
1262-
1263-
let api = Api::current();
1264-
let missing_checksums = {
1265-
let checksums = objects.iter().map(HashedDifMatch::checksum);
1266-
api.authenticated()?
1267-
.find_missing_dif_checksums(options.org, options.project, checksums)?
1268-
};
1269-
1270-
let missing = objects
1271-
.into_iter()
1272-
.filter(|sym| missing_checksums.contains(&sym.checksum()))
1273-
.collect();
1274-
1275-
info!("Missing debug information files: {:#?}", &missing);
1276-
Ok(missing)
1277-
}
1278-
1279-
/// Compresses the given batch into a ZIP archive.
1280-
fn create_batch_archive(difs: &[HashedDifMatch<'_>]) -> Result<TempFile> {
1281-
let total_bytes = difs.iter().map(ItemSize::size).sum();
1282-
let pb = make_byte_progress_bar(total_bytes);
1283-
let tf = TempFile::create()?;
1284-
1285-
{
1286-
let mut zip = ZipWriter::new(tf.open()?);
1287-
1288-
for symbol in difs {
1289-
zip.start_file(symbol.file_name(), SimpleFileOptions::default())?;
1290-
copy_with_progress(&pb, &mut symbol.data(), &mut zip)?;
1291-
}
1292-
}
1293-
1294-
pb.finish_and_clear();
1295-
Ok(tf)
1296-
}
1297-
1298-
/// Uploads the given DIFs to the server in batched ZIP archives.
1299-
fn upload_in_batches(
1300-
objects: &[HashedDifMatch<'_>],
1301-
options: &DifUpload,
1302-
) -> Result<Vec<DebugInfoFile>> {
1303-
let api = Api::current();
1304-
let max_size = Config::current().get_max_dif_archive_size();
1305-
let mut dsyms = Vec::new();
1306-
1307-
for (i, (batch, _)) in objects.batches(max_size, MAX_CHUNKS).enumerate() {
1308-
println!("\n{}", style(format!("Batch {}", i + 1)).bold());
1309-
1310-
println!(
1311-
"{} Compressing {} debug symbol files",
1312-
style(">").dim(),
1313-
style(batch.len()).yellow()
1314-
);
1315-
let archive = create_batch_archive(batch)?;
1316-
1317-
println!("{} Uploading debug symbol files", style(">").dim());
1318-
dsyms.extend(
1319-
api.authenticated()?
1320-
.region_specific(options.org)
1321-
.upload_dif_archive(options.project, archive.path())?,
1322-
);
1323-
}
1324-
1325-
Ok(dsyms)
1326-
}
1327-
1328-
/// Uploads debug info files using the legacy endpoint.
1329-
#[deprecated = "this non-chunked upload mechanism is deprecated in favor of upload_difs_chunked"]
1330-
fn upload_difs_batched(options: &DifUpload) -> Result<Vec<DebugInfoFile>> {
1331-
// Search for debug files in the file system and ZIPs
1332-
let found = search_difs(options)?;
1333-
if found.is_empty() {
1334-
println!("{} No debug information files found", style(">").dim());
1335-
return Ok(Default::default());
1336-
}
1337-
1338-
// Try to resolve BCSymbolMaps
1339-
let symbol_map = options.symbol_map.as_deref();
1340-
let processed = process_symbol_maps(found, symbol_map)?;
1341-
1342-
// Calculate checksums
1343-
let hashed = prepare_difs(processed, HashedDifMatch::from)?;
1344-
1345-
// Check which files are missing on the server
1346-
let missing = get_missing_difs(hashed, options)?;
1347-
if missing.is_empty() {
1348-
println!(
1349-
"{} Nothing to upload, all files are on the server",
1350-
style(">").dim()
1351-
);
1352-
println!("{} Nothing to upload", style(">").dim());
1353-
return Ok(Default::default());
1354-
}
1355-
if options.no_upload {
1356-
println!("{} skipping upload.", style(">").dim());
1357-
return Ok(Default::default());
1358-
}
1359-
1360-
// Upload missing DIFs in batches
1361-
let uploaded = upload_in_batches(&missing, options)?;
1362-
if !uploaded.is_empty() {
1363-
println!("{} File upload complete:\n", style(">").dim());
1364-
for dif in &uploaded {
1365-
println!(
1366-
" {} ({}; {})",
1367-
style(&dif.id()).dim(),
1368-
&dif.object_name,
1369-
dif.cpu_name
1370-
);
1371-
}
1372-
}
1373-
1374-
Ok(uploaded)
1375-
}
1376-
13771207
/// The format of a Debug Information File (DIF).
13781208
///
13791209
/// Most DIFs are also object files, but we also know of some auxiliary DIF formats.
@@ -1641,23 +1471,16 @@ impl<'a> DifUpload<'a> {
16411471
self.bcsymbolmaps_allowed = chunk_options.supports(ChunkUploadCapability::BcSymbolmap);
16421472
self.il2cpp_mappings_allowed = chunk_options.supports(ChunkUploadCapability::Il2Cpp);
16431473

1644-
if chunk_options.supports(ChunkUploadCapability::DebugFiles) {
1645-
self.validate_capabilities();
1646-
return upload_difs_chunked(self, chunk_options);
1474+
if !chunk_options.supports(ChunkUploadCapability::DebugFiles) {
1475+
anyhow::bail!(
1476+
"Your Sentry server does not support chunked uploads for debug files. Please upgrade \
1477+
your Sentry server, or if you cannot upgrade your server, downgrade your Sentry \
1478+
CLI version to 2.x."
1479+
);
16471480
}
16481481

16491482
self.validate_capabilities();
1650-
1651-
log::warn!(
1652-
"[DEPRECATION NOTICE] Your Sentry server does not support chunked uploads for debug \
1653-
files. Falling back to deprecated upload method. Support for this deprecated upload \
1654-
method will be removed in Sentry CLI 3.0.0. Please upgrade your Sentry server, or if \
1655-
you cannot upgrade, pin your Sentry CLI version to 2.x, so you don't get upgraded \
1656-
to 3.x when it is released."
1657-
);
1658-
1659-
#[expect(deprecated, reason = "fallback to legacy upload")]
1660-
Ok((upload_difs_batched(&self)?, false))
1483+
upload_difs_chunked(self, chunk_options)
16611484
}
16621485

16631486
/// Validate that the server supports all requested capabilities.

0 commit comments

Comments
 (0)