Skip to content

Commit 0a030d4

Browse files
runningcodeclaudeszokeasaurusrex
authored
feat(build): Add CLI version metadata to build upload archives (#2890)
## Summary Adds CLI version metadata to all zip archives created during build uploads for debugging purposes. Resolves EME-471 ## Changes - Adds a `.sentry-cli-metadata.txt` file at the root of each build upload zip archive - The file contains: `sentry-cli-version: <version>` - Applied to both single-file uploads (APK, AAB) and directory uploads (XCArchive) - Uses the same compression and timestamp settings as other files to maintain consistent checksums - Adds support for `SENTRY_CLI_INTEGRATION_TEST_VERSION_OVERRIDE` environment variable to override the version in tests and allow for deterministic chunk hashes - The version override is checked at runtime (not just during `cfg(test)`), allowing integration tests that spawn the CLI as a subprocess to use the override ## Testing - Integration tests use `SENTRY_CLI_INTEGRATION_TEST_VERSION_OVERRIDE=0.0.0-test` to ensure stable chunk hashes across releases - The environment variable is properly passed to CLI subprocesses in integration tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com>
1 parent ccb71af commit 0a030d4

File tree

6 files changed

+38
-3
lines changed

6 files changed

+38
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### New Features
66

77
- Promote `sentry-cli build upload` command to non-experimental ([#2899](https://github.com/getsentry/sentry-cli/pull/2899), [#2905](https://github.com/getsentry/sentry-cli/pull/2905)).
8+
- Build uploads now include CLI version metadata in the uploaded archive ([#2890](https://github.com/getsentry/sentry-cli/pull/2890))
89

910
### Deprecations
1011

src/commands/build/upload.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ use crate::config::Config;
1717
use crate::utils::args::ArgExt as _;
1818
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
1919
use crate::utils::build::{handle_asset_catalogs, ipa_to_xcarchive, is_apple_app, is_ipa_file};
20-
use crate::utils::build::{is_aab_file, is_apk_file, is_zip_file, normalize_directory};
20+
use crate::utils::build::{
21+
is_aab_file, is_apk_file, is_zip_file, normalize_directory, write_version_metadata,
22+
};
2123
use crate::utils::chunks::{upload_chunks, Chunk};
2224
use crate::utils::fs::get_sha1_checksums;
2325
use crate::utils::fs::TempDir;
@@ -455,6 +457,8 @@ fn normalize_file(path: &Path, bytes: &[u8]) -> Result<TempFile> {
455457
zip.start_file(file_name, options)?;
456458
zip.write_all(bytes)?;
457459

460+
write_version_metadata(&mut zip)?;
461+
458462
zip.finish()?;
459463
debug!("Successfully created normalized zip for file");
460464
Ok(temp_file)

src/utils/build/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ mod validation;
55

66
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
77
pub use self::apple::{handle_asset_catalogs, ipa_to_xcarchive};
8-
pub use self::normalize::normalize_directory;
8+
pub use self::normalize::{normalize_directory, write_version_metadata};
99
pub use self::validation::{is_aab_file, is_apk_file, is_zip_file};
1010
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
1111
pub use self::validation::{is_apple_app, is_ipa_file};

src/utils/build/normalize.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::borrow::Cow;
12
#[cfg(not(windows))]
23
use std::fs;
34
use std::fs::File;
@@ -6,6 +7,7 @@ use std::io::Write as _;
67
use std::os::unix::fs::PermissionsExt as _;
78
use std::path::{Path, PathBuf};
89

10+
use crate::constants::VERSION;
911
use crate::utils::fs::TempFile;
1012
use anyhow::{Context as _, Result};
1113
use itertools::Itertools as _;
@@ -15,6 +17,16 @@ use walkdir::WalkDir;
1517
use zip::write::SimpleFileOptions;
1618
use zip::{DateTime, ZipWriter};
1719

20+
fn get_version() -> Cow<'static, str> {
21+
let version = Cow::Borrowed(VERSION);
22+
23+
// Integration tests can override the version for consistent test results.
24+
// This ensures deterministic checksums in test fixtures by using a fixed version.
25+
std::env::var("SENTRY_CLI_INTEGRATION_TEST_VERSION_OVERRIDE")
26+
.map(Cow::Owned)
27+
.unwrap_or(version)
28+
}
29+
1830
fn sort_entries(path: &Path) -> Result<impl Iterator<Item = (PathBuf, PathBuf)>> {
1931
Ok(WalkDir::new(path)
2032
.into_iter()
@@ -76,6 +88,21 @@ fn add_entries_to_zip(
7688
Ok(file_count)
7789
}
7890

91+
fn metadata_file_options() -> SimpleFileOptions {
92+
SimpleFileOptions::default()
93+
.compression_method(zip::CompressionMethod::Deflated)
94+
.last_modified_time(DateTime::default())
95+
}
96+
97+
pub fn write_version_metadata<W: std::io::Write + std::io::Seek>(
98+
zip: &mut ZipWriter<W>,
99+
) -> Result<()> {
100+
let version = get_version();
101+
zip.start_file(".sentry-cli-metadata.txt", metadata_file_options())?;
102+
writeln!(zip, "sentry-cli-version: {version}")?;
103+
Ok(())
104+
}
105+
79106
// For XCArchive directories, we'll zip the entire directory
80107
// It's important to not change the contents of the directory or the size
81108
// analysis will be wrong and the code signature will break.
@@ -108,6 +135,8 @@ pub fn normalize_directory(path: &Path, parsed_assets_path: &Path) -> Result<Tem
108135
)?;
109136
}
110137

138+
write_version_metadata(&mut zip)?;
139+
111140
zip.finish()?;
112141
debug!("Successfully created normalized zip for directory with {file_count} files");
113142
Ok(temp_file)
87 Bytes
Binary file not shown.

tests/integration/build/upload.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ fn command_build_upload_apk_chunked() {
162162
if is_first_assemble_call.swap(false, Ordering::Relaxed) {
163163
r#"{
164164
"state": "created",
165-
"missingChunks": ["18e40e6e932d0b622d631e887be454cc2003dbb5"]
165+
"missingChunks": ["60863d91bb673a1b1b92dbbe91b1de5cc0dde146"]
166166
}"#
167167
} else {
168168
r#"{
@@ -176,6 +176,7 @@ fn command_build_upload_apk_chunked() {
176176
.expect(2),
177177
)
178178
.register_trycmd_test("build/build-upload-apk.trycmd")
179+
.env("SENTRY_CLI_INTEGRATION_TEST_VERSION_OVERRIDE", "0.0.0-test")
179180
.with_default_token();
180181
}
181182

0 commit comments

Comments
 (0)