Skip to content

Commit 3670f47

Browse files
committed
Show mobile-app upload show analysis URL
1 parent c5d840d commit 3670f47

File tree

7 files changed

+68
-98
lines changed

7 files changed

+68
-98
lines changed

src/api/data_types/chunking/mobile_app.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub struct ChunkedMobileAppRequest<'a> {
99
pub checksum: Digest,
1010
pub chunks: &'a [Digest],
1111
#[serde(skip_serializing_if = "Option::is_none")]
12-
pub git_sha: Option<&'a str>,
12+
pub head_sha: Option<&'a str>,
1313
#[serde(skip_serializing_if = "Option::is_none")]
1414
pub build_configuration: Option<&'a str>,
1515
}
@@ -20,4 +20,5 @@ pub struct AssembleMobileAppResponse {
2020
pub state: ChunkedFileState,
2121
pub missing_chunks: Vec<Digest>,
2222
pub detail: Option<String>,
23+
pub artifact_id: Option<String>,
2324
}

src/api/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,7 +1036,7 @@ impl<'a> AuthenticatedApi<'a> {
10361036
project: &str,
10371037
checksum: Digest,
10381038
chunks: &[Digest],
1039-
git_sha: Option<&str>,
1039+
head_sha: Option<&str>,
10401040
build_configuration: Option<&str>,
10411041
) -> ApiResult<AssembleMobileAppResponse> {
10421042
let url = format!(
@@ -1049,7 +1049,7 @@ impl<'a> AuthenticatedApi<'a> {
10491049
.with_json_body(&ChunkedMobileAppRequest {
10501050
checksum,
10511051
chunks,
1052-
git_sha,
1052+
head_sha,
10531053
build_configuration,
10541054
})?
10551055
.send()?

src/commands/mobile_app/upload.rs

Lines changed: 56 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@ use clap::{Arg, ArgAction, ArgMatches, Command};
1111
use indicatif::ProgressStyle;
1212
use itertools::Itertools as _;
1313
use log::{debug, info, warn};
14-
use sha1_smol::Digest;
1514
use symbolic::common::ByteView;
1615
use zip::write::SimpleFileOptions;
1716
use zip::{DateTime, ZipWriter};
1817

1918
use crate::api::{Api, AuthenticatedApi, ChunkUploadCapability};
2019
use crate::config::Config;
2120
use crate::utils::args::ArgExt as _;
22-
use crate::utils::chunks::{upload_chunks, Chunk, ASSEMBLE_POLL_INTERVAL};
21+
use crate::utils::chunks::{upload_chunks, Chunk};
2322
use crate::utils::fs::get_sha1_checksums;
2423
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
2524
use crate::utils::fs::TempDir;
@@ -129,9 +128,10 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
129128

130129
let config = Config::current();
131130
let (org, project) = config.get_org_and_project(matches)?;
131+
let base_url = config.get_base_url()?;
132132

133-
let mut uploaded_paths = vec![];
134-
let mut errored_paths = vec![];
133+
let mut uploaded_paths_and_ids = vec![];
134+
let mut errored_paths_and_reasons = vec![];
135135
for (path, zip) in normalized_zips {
136136
info!("Uploading file: {}", path.display());
137137
let bytes = ByteView::open(zip.path())?;
@@ -143,41 +143,50 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
143143
sha.as_deref(),
144144
build_configuration,
145145
) {
146-
Ok(_) => {
146+
Ok(artifact_id) => {
147147
info!("Successfully uploaded file: {}", path.display());
148-
uploaded_paths.push(path.to_path_buf());
148+
uploaded_paths_and_ids.push((path.to_path_buf(), artifact_id));
149149
}
150150
Err(e) => {
151151
debug!("Failed to upload file at path {}: {}", path.display(), e);
152-
errored_paths.push(path.to_path_buf());
152+
errored_paths_and_reasons.push((path.to_path_buf(), e));
153153
}
154154
}
155155
}
156156

157-
if !errored_paths.is_empty() {
157+
if !errored_paths_and_reasons.is_empty() {
158158
warn!(
159159
"Failed to upload {} file{}:",
160-
errored_paths.len(),
161-
if errored_paths.len() == 1 { "" } else { "s" }
160+
errored_paths_and_reasons.len(),
161+
if errored_paths_and_reasons.len() == 1 {
162+
""
163+
} else {
164+
"s"
165+
}
162166
);
163-
for path in errored_paths {
164-
warn!(" - {}", path.display());
165-
}
166-
}
167-
168-
println!(
169-
"Successfully uploaded {} file{} to Sentry",
170-
uploaded_paths.len(),
171-
if uploaded_paths.len() == 1 { "" } else { "s" }
172-
);
173-
if uploaded_paths.len() < 3 {
174-
for path in &uploaded_paths {
175-
println!(" - {}", path.display());
167+
for (path, reason) in errored_paths_and_reasons {
168+
warn!(" - {} ({})", path.display(), reason);
176169
}
177170
}
178171

179-
if uploaded_paths.is_empty() {
172+
if uploaded_paths_and_ids.is_empty() {
180173
bail!("Failed to upload any files");
174+
} else {
175+
println!(
176+
"Successfully uploaded {} file{} to Sentry",
177+
uploaded_paths_and_ids.len(),
178+
if uploaded_paths_and_ids.len() == 1 {
179+
""
180+
} else {
181+
"s"
182+
}
183+
);
184+
if uploaded_paths_and_ids.len() < 3 {
185+
for (path, artifact_id) in &uploaded_paths_and_ids {
186+
let url = format!("{base_url}/{org}/preprod/{project}/{artifact_id}");
187+
println!(" - {} {url}", path.display());
188+
}
189+
}
181190
}
182191
Ok(())
183192
}
@@ -337,14 +346,15 @@ fn normalize_directory(path: &Path) -> Result<TempFile> {
337346
Ok(temp_file)
338347
}
339348

349+
/// Returns artifact id if upload was successful.
340350
fn upload_file(
341351
api: &AuthenticatedApi,
342352
bytes: &[u8],
343353
org: &str,
344354
project: &str,
345355
sha: Option<&str>,
346356
build_configuration: Option<&str>,
347-
) -> Result<()> {
357+
) -> Result<String> {
348358
const SELF_HOSTED_ERROR_HINT: &str = "If you are using a self-hosted Sentry server, \
349359
update to the latest version of Sentry to use the mobile-app upload command.";
350360

@@ -371,7 +381,7 @@ fn upload_file(
371381
}
372382

373383
let progress_style =
374-
ProgressStyle::default_spinner().template("{spinner} Optimizing bundle for upload...");
384+
ProgressStyle::default_spinner().template("{spinner} Preparing for upload...");
375385
let pb = ProgressBar::new_spinner();
376386
pb.enable_steady_tick(100);
377387
pb.set_style(progress_style);
@@ -384,76 +394,33 @@ fn upload_file(
384394
.map(|(data, checksum)| Chunk((*checksum, data)))
385395
.collect::<Vec<_>>();
386396

387-
pb.finish_with_duration("Finishing upload");
388-
389-
let response =
390-
api.assemble_mobile_app(org, project, checksum, &checksums, sha, build_configuration)?;
391-
chunks.retain(|Chunk((digest, _))| response.missing_chunks.contains(digest));
392-
393-
if !chunks.is_empty() {
394-
let upload_progress_style = ProgressStyle::default_bar().template(
395-
"{prefix:.dim} Uploading files...\
396-
\n{wide_bar} {bytes}/{total_bytes} ({eta})",
397-
);
398-
upload_chunks(&chunks, &chunk_upload_options, upload_progress_style)?;
399-
} else {
400-
println!("Nothing to upload, all files are on the server");
401-
}
402-
403-
poll_assemble(
404-
api,
405-
checksum,
406-
&checksums,
407-
org,
408-
project,
409-
sha,
410-
build_configuration,
411-
)?;
412-
Ok(())
413-
}
414-
415-
fn poll_assemble(
416-
api: &AuthenticatedApi,
417-
checksum: Digest,
418-
chunks: &[Digest],
419-
org: &str,
420-
project: &str,
421-
sha: Option<&str>,
422-
build_configuration: Option<&str>,
423-
) -> Result<()> {
424-
debug!("Polling assemble for checksum: {}", checksum);
425-
426-
let progress_style = ProgressStyle::default_spinner().template("{spinner} Processing files...");
427-
let pb = ProgressBar::new_spinner();
428-
429-
pb.enable_steady_tick(100);
430-
pb.set_style(progress_style);
397+
pb.finish_with_duration("Preparing for upload");
431398

432-
let response = loop {
399+
let result = loop {
433400
let response =
434-
api.assemble_mobile_app(org, project, checksum, chunks, sha, build_configuration)?;
401+
api.assemble_mobile_app(org, project, checksum, &checksums, sha, build_configuration)?;
402+
chunks.retain(|Chunk((digest, _))| response.missing_chunks.contains(digest));
403+
404+
if !chunks.is_empty() {
405+
let upload_progress_style = ProgressStyle::default_bar().template(
406+
"{prefix:.dim} Uploading files...\
407+
\n{wide_bar} {bytes}/{total_bytes} ({eta})",
408+
);
409+
upload_chunks(&chunks, &chunk_upload_options, upload_progress_style)?;
410+
}
435411

436-
if response.state.is_finished() {
437-
break response;
412+
// is_err() is not_found or error and is_finished() is ok or error, the both is only error
413+
if response.state.is_finished() && response.state.is_err() {
414+
let message = response.detail.as_deref().unwrap_or("unknown error");
415+
bail!("Failed to process uploaded files: {}", message);
438416
}
439417

440-
std::thread::sleep(ASSEMBLE_POLL_INTERVAL);
418+
if let Some(artifact_id) = response.artifact_id {
419+
break Ok(artifact_id);
420+
}
441421
};
442422

443-
pb.finish_with_duration("Processing");
444-
445-
if response.state.is_err() {
446-
let message = response.detail.as_deref().unwrap_or("unknown error");
447-
bail!("Failed to process uploaded files: {}", message);
448-
}
449-
450-
if response.state.is_pending() {
451-
info!("File upload complete (processing pending on server)");
452-
} else {
453-
info!("File processing complete");
454-
}
455-
456-
Ok(())
423+
result
457424
}
458425

459426
#[cfg(not(windows))]

tests/integration/_cases/mobile_app/mobile_app-upload-apk-all-uploaded.trycmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ $ sentry-cli mobile-app upload tests/integration/_fixtures/mobile_app/apk.apk
44
[..]WARN[..]EXPERIMENTAL: The mobile-app subcommand is experimental. The command is subject to breaking changes and may be removed without notice in any release.
55
Nothing to upload, all files are on the server
66
Successfully uploaded 1 file to Sentry
7-
- tests/integration/_fixtures/mobile_app/apk.apk
7+
- tests/integration/_fixtures/mobile_app/apk.apk http[..]/wat-org/preprod/wat-project/42
88

99
```

tests/integration/_cases/mobile_app/mobile_app-upload-apk.trycmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ $ sentry-cli mobile-app upload tests/integration/_fixtures/mobile_app/apk.apk --
33
? success
44
[..]WARN[..]EXPERIMENTAL: The mobile-app subcommand is experimental. The command is subject to breaking changes and may be removed without notice in any release.
55
Successfully uploaded 1 file to Sentry
6-
- tests/integration/_fixtures/mobile_app/apk.apk
6+
- tests/integration/_fixtures/mobile_app/apk.apk http[..]/wat-org/preprod/wat-project/42
77

88
```

tests/integration/_cases/mobile_app/mobile_app-upload-ipa.trycmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ $ sentry-cli mobile-app upload tests/integration/_fixtures/mobile_app/ipa.ipa --
33
? success
44
[..]WARN[..]EXPERIMENTAL: The mobile-app subcommand is experimental. The command is subject to breaking changes and may be removed without notice in any release.
55
Successfully uploaded 1 file to Sentry
6-
- tests/integration/_fixtures/mobile_app/ipa.ipa
6+
- tests/integration/_fixtures/mobile_app/ipa.ipa http[..]/wat-org/preprod/wat-project/some-text-id
77

88
```

tests/integration/mobile_app/upload.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ fn command_mobile_app_upload_apk_all_uploaded() {
8686
"POST",
8787
"/api/0/projects/wat-org/wat-project/files/preprodartifacts/assemble/",
8888
)
89-
.with_response_body(r#"{"state":"ok","missingChunks":[]}"#),
89+
.with_response_body(r#"{"state":"ok","missingChunks":[],"artifactId":"42"}"#),
9090
)
9191
.register_trycmd_test("mobile_app/mobile_app-upload-apk-all-uploaded.trycmd")
9292
.with_default_token();
@@ -169,7 +169,8 @@ fn command_mobile_app_upload_apk_chunked() {
169169
} else {
170170
r#"{
171171
"state": "ok",
172-
"missingChunks": []
172+
"missingChunks": [],
173+
"artifactId": "42"
173174
}"#
174175
}
175176
.into()
@@ -223,7 +224,8 @@ fn command_mobile_app_upload_ipa_chunked() {
223224
} else {
224225
r#"{
225226
"state": "ok",
226-
"missingChunks": []
227+
"missingChunks": [],
228+
"artifactId": "some-text-id"
227229
}"#
228230
}
229231
.into()

0 commit comments

Comments
 (0)