Skip to content

Commit d43c2a5

Browse files
runningcodeclaude
andcommitted
feat(build): Add CLI parameters to track plugin versions (EME-XXX)
Add support for tracking Gradle and Fastlane plugin versions in build uploads by accepting them as CLI parameters and writing them to the metadata file in the uploaded zip archive. This enables the backend to store plugin version information in the PreprodArtifact database table for size analysis and build distribution tracking. Changes: - Add --gradle-plugin-version and --fastlane-plugin-version CLI parameters - Write plugin versions to .sentry-cli-metadata.txt in uploaded zips - Update write_version_metadata() to accept optional plugin versions - Add test coverage for metadata file generation with plugin versions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 80eba90 commit d43c2a5

File tree

5 files changed

+124
-20
lines changed

5 files changed

+124
-20
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
## Unreleased
44

55
### Improvements
6+
67
- For the `sentry-cli build upload` command, we now only auto-detect Git metadata when we detect we are running in a CI environment, unless the user manually overrides this behavior ([#2974](https://github.com/getsentry/sentry-cli/pull/2974)). This change prevents local development builds from triggiering GitHub status checks for size analysis.
78
- We can detect most common CI environments based on the environment variables these set.
89
- We introduced two new arguments, `--force-git-metadata` and `--no-git-metadata`, which force-enable and force-disable automatic Git data collection, respectively, overriding the default behavior.
10+
- The `sentry-cli build upload` command now accepts `--gradle-plugin-version` and `--fastlane-plugin-version` parameters to track plugin versions ([#2994](https://github.com/getsentry/sentry-cli/pull/2994)). These versions are written to the `.sentry-cli-metadata.txt` file in uploaded build archives, enabling the backend to store plugin version information for size analysis and build distribution tracking.
911
- The `sentry-cli build upload` command now automatically detects the correct branch or tag reference in non-PR GitHub Actions workflows ([#2976](https://github.com/getsentry/sentry-cli/pull/2976)). Previously, `--head-ref` was only auto-detected for pull request workflows. Now it works for push, release, and other workflow types by using the `GITHUB_REF_NAME` environment variable.
1012

1113
## 2.58.2

src/commands/build/upload.rs

Lines changed: 99 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,16 @@ pub fn make_command(command: Command) -> Command {
123123
.conflicts_with("force_git_metadata")
124124
.help("Disable collection and sending of git metadata.")
125125
)
126+
.arg(
127+
Arg::new("gradle_plugin_version")
128+
.long("gradle-plugin-version")
129+
.help("The version of the Sentry Gradle plugin used to build the artifact.")
130+
)
131+
.arg(
132+
Arg::new("fastlane_plugin_version")
133+
.long("fastlane-plugin-version")
134+
.help("The version of the Sentry Fastlane plugin used to build the artifact.")
135+
)
126136
}
127137

128138
pub fn execute(matches: &ArgMatches) -> Result<()> {
@@ -149,6 +159,10 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
149159

150160
let build_configuration = matches.get_one("build_configuration").map(String::as_str);
151161
let release_notes = matches.get_one("release_notes").map(String::as_str);
162+
let gradle_plugin_version = matches.get_one("gradle_plugin_version").map(String::as_str);
163+
let fastlane_plugin_version = matches
164+
.get_one("fastlane_plugin_version")
165+
.map(String::as_str);
152166

153167
let api = Api::current();
154168
let authenticated_api = api.authenticated()?;
@@ -171,15 +185,22 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
171185

172186
let normalized_zip = if path.is_file() {
173187
debug!("Normalizing file: {}", path.display());
174-
handle_file(path, &byteview)?
188+
handle_file(
189+
path,
190+
&byteview,
191+
gradle_plugin_version,
192+
fastlane_plugin_version,
193+
)?
175194
} else if path.is_dir() {
176195
debug!("Normalizing directory: {}", path.display());
177-
handle_directory(path).with_context(|| {
178-
format!(
179-
"Failed to generate uploadable bundle for directory {}",
180-
path.display()
181-
)
182-
})?
196+
handle_directory(path, gradle_plugin_version, fastlane_plugin_version).with_context(
197+
|| {
198+
format!(
199+
"Failed to generate uploadable bundle for directory {}",
200+
path.display()
201+
)
202+
},
203+
)?
183204
} else {
184205
Err(anyhow!(
185206
"Path {} is neither a file nor a directory, cannot upload",
@@ -259,6 +280,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
259280
Ok(())
260281
}
261282

283+
<<<<<<< HEAD
262284
/// Collects git metadata from arguments and VCS introspection.
263285
///
264286
/// When `auto_collect` is false, only explicitly provided values are collected;
@@ -434,18 +456,31 @@ fn collect_git_metadata(
434456
}
435457
}
436458

437-
fn handle_file(path: &Path, byteview: &ByteView) -> Result<TempFile> {
459+
fn handle_file(
460+
path: &Path,
461+
byteview: &ByteView,
462+
gradle_plugin_version: Option<&str>,
463+
fastlane_plugin_version: Option<&str>,
464+
) -> Result<TempFile> {
438465
// Handle IPA files by converting them to XCArchive
439466
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
440467
if is_zip_file(byteview) && is_ipa_file(byteview)? {
441468
debug!("Converting IPA file to XCArchive structure");
442469
let archive_temp_dir = TempDir::create()?;
443470
return ipa_to_xcarchive(path, byteview, &archive_temp_dir)
444-
.and_then(|path| handle_directory(&path))
471+
.and_then(|path| {
472+
handle_directory(&path, gradle_plugin_version, fastlane_plugin_version)
473+
})
445474
.with_context(|| format!("Failed to process IPA file {}", path.display()));
446475
}
447476

448-
normalize_file(path, byteview).with_context(|| {
477+
normalize_file(
478+
path,
479+
byteview,
480+
gradle_plugin_version,
481+
fastlane_plugin_version,
482+
)
483+
.with_context(|| {
449484
format!(
450485
"Failed to generate uploadable bundle for file {}",
451486
path.display()
@@ -499,7 +534,12 @@ fn validate_is_supported_build(path: &Path, bytes: &[u8]) -> Result<()> {
499534
}
500535

501536
// For APK and AAB files, we'll copy them directly into the zip
502-
fn normalize_file(path: &Path, bytes: &[u8]) -> Result<TempFile> {
537+
fn normalize_file(
538+
path: &Path,
539+
bytes: &[u8],
540+
gradle_plugin_version: Option<&str>,
541+
fastlane_plugin_version: Option<&str>,
542+
) -> Result<TempFile> {
503543
debug!("Creating normalized zip for file: {}", path.display());
504544

505545
let temp_file = TempFile::create()?;
@@ -523,20 +563,29 @@ fn normalize_file(path: &Path, bytes: &[u8]) -> Result<TempFile> {
523563
zip.start_file(file_name, options)?;
524564
zip.write_all(bytes)?;
525565

526-
write_version_metadata(&mut zip)?;
566+
write_version_metadata(&mut zip, gradle_plugin_version, fastlane_plugin_version)?;
527567

528568
zip.finish()?;
529569
debug!("Successfully created normalized zip for file");
530570
Ok(temp_file)
531571
}
532572

533-
fn handle_directory(path: &Path) -> Result<TempFile> {
573+
fn handle_directory(
574+
path: &Path,
575+
gradle_plugin_version: Option<&str>,
576+
fastlane_plugin_version: Option<&str>,
577+
) -> Result<TempFile> {
534578
let temp_dir = TempDir::create()?;
535579
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
536580
if is_apple_app(path)? {
537581
handle_asset_catalogs(path, temp_dir.path());
538582
}
539-
normalize_directory(path, temp_dir.path())
583+
normalize_directory(
584+
path,
585+
temp_dir.path(),
586+
gradle_plugin_version,
587+
fastlane_plugin_version,
588+
)
540589
}
541590

542591
/// Returns artifact url if upload was successful.
@@ -674,7 +723,7 @@ mod tests {
674723
fs::create_dir_all(test_dir.join("Products"))?;
675724
fs::write(test_dir.join("Products").join("app.txt"), "test content")?;
676725

677-
let result_zip = normalize_directory(&test_dir, temp_dir.path())?;
726+
let result_zip = normalize_directory(&test_dir, temp_dir.path(), None, None)?;
678727
let zip_file = fs::File::open(result_zip.path())?;
679728
let mut archive = ZipArchive::new(zip_file)?;
680729
let file = archive.by_index(0)?;
@@ -690,7 +739,7 @@ mod tests {
690739
let xcarchive_path = Path::new("tests/integration/_fixtures/build/archive.xcarchive");
691740

692741
// Process the XCArchive directory
693-
let result = handle_directory(xcarchive_path)?;
742+
let result = handle_directory(xcarchive_path, None, None)?;
694743

695744
// Verify the resulting zip contains parsed assets
696745
let zip_file = fs::File::open(result.path())?;
@@ -723,7 +772,7 @@ mod tests {
723772
let byteview = ByteView::open(ipa_path)?;
724773

725774
// Process the IPA file - this should work even without asset catalogs
726-
let result = handle_file(ipa_path, &byteview)?;
775+
let result = handle_file(ipa_path, &byteview, None, None)?;
727776

728777
let zip_file = fs::File::open(result.path())?;
729778
let mut archive = ZipArchive::new(zip_file)?;
@@ -760,7 +809,7 @@ mod tests {
760809
let symlink_path = test_dir.join("Products").join("app_link.txt");
761810
symlink("app.txt", &symlink_path)?;
762811

763-
let result_zip = normalize_directory(&test_dir, temp_dir.path())?;
812+
let result_zip = normalize_directory(&test_dir, temp_dir.path(), None, None)?;
764813
let zip_file = fs::File::open(result_zip.path())?;
765814
let mut archive = ZipArchive::new(zip_file)?;
766815

@@ -795,6 +844,7 @@ mod tests {
795844
}
796845

797846
#[test]
847+
<<<<<<< HEAD
798848
fn test_collect_git_metadata_respects_explicit_values_when_auto_collect_disabled() {
799849
// Create a mock ArgMatches with explicit --head-sha and --vcs-provider values
800850
let app = make_command(Command::new("test"));
@@ -877,4 +927,35 @@ mod tests {
877927
"head_ref should be empty with auto_collect=false and no explicit value"
878928
);
879929
}
930+
931+
#[test]
932+
fn test_metadata_includes_plugin_versions() -> Result<()> {
933+
let temp_dir = crate::utils::fs::TempDir::create()?;
934+
let test_dir = temp_dir.path().join("TestApp.xcarchive");
935+
fs::create_dir_all(test_dir.join("Products"))?;
936+
fs::write(test_dir.join("Products").join("app.txt"), "test content")?;
937+
938+
let result_zip =
939+
normalize_directory(&test_dir, temp_dir.path(), Some("4.12.0"), Some("1.2.3"))?;
940+
let zip_file = fs::File::open(result_zip.path())?;
941+
let mut archive = ZipArchive::new(zip_file)?;
942+
943+
// Find and read the metadata file
944+
let metadata_file = archive.by_name(".sentry-cli-metadata.txt")?;
945+
let metadata_content = std::io::read_to_string(metadata_file)?;
946+
947+
assert!(
948+
metadata_content.contains("sentry-cli-version:"),
949+
"Metadata should contain sentry-cli-version"
950+
);
951+
assert!(
952+
metadata_content.contains("gradle-plugin-version: 4.12.0"),
953+
"Metadata should contain gradle-plugin-version"
954+
);
955+
assert!(
956+
metadata_content.contains("fastlane-plugin-version: 1.2.3"),
957+
"Metadata should contain fastlane-plugin-version"
958+
);
959+
Ok(())
960+
}
880961
}

src/utils/build/normalize.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,17 +94,30 @@ fn metadata_file_options() -> SimpleFileOptions {
9494

9595
pub fn write_version_metadata<W: std::io::Write + std::io::Seek>(
9696
zip: &mut ZipWriter<W>,
97+
gradle_plugin_version: Option<&str>,
98+
fastlane_plugin_version: Option<&str>,
9799
) -> Result<()> {
98100
let version = get_version();
99101
zip.start_file(".sentry-cli-metadata.txt", metadata_file_options())?;
100102
writeln!(zip, "sentry-cli-version: {version}")?;
103+
if let Some(gradle_version) = gradle_plugin_version {
104+
writeln!(zip, "gradle-plugin-version: {gradle_version}")?;
105+
}
106+
if let Some(fastlane_version) = fastlane_plugin_version {
107+
writeln!(zip, "fastlane-plugin-version: {fastlane_version}")?;
108+
}
101109
Ok(())
102110
}
103111

104112
// For XCArchive directories, we'll zip the entire directory
105113
// It's important to not change the contents of the directory or the size
106114
// analysis will be wrong and the code signature will break.
107-
pub fn normalize_directory(path: &Path, parsed_assets_path: &Path) -> Result<TempFile> {
115+
pub fn normalize_directory(
116+
path: &Path,
117+
parsed_assets_path: &Path,
118+
gradle_plugin_version: Option<&str>,
119+
fastlane_plugin_version: Option<&str>,
120+
) -> Result<TempFile> {
108121
debug!("Creating normalized zip for directory: {}", path.display());
109122

110123
let temp_file = TempFile::create()?;
@@ -133,7 +146,7 @@ pub fn normalize_directory(path: &Path, parsed_assets_path: &Path) -> Result<Tem
133146
)?;
134147
}
135148

136-
write_version_metadata(&mut zip)?;
149+
write_version_metadata(&mut zip, gradle_plugin_version, fastlane_plugin_version)?;
137150

138151
zip.finish()?;
139152
debug!("Successfully created normalized zip for directory with {file_count} files");

tests/integration/_cases/build/build-upload-help-macos.trycmd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ Options:
5959
most CI environments.
6060
--no-git-metadata
6161
Disable collection and sending of git metadata.
62+
--gradle-plugin-version <gradle_plugin_version>
63+
The version of the Sentry Gradle plugin used to build the artifact.
64+
--fastlane-plugin-version <fastlane_plugin_version>
65+
The version of the Sentry Fastlane plugin used to build the artifact.
6266
-h, --help
6367
Print help
6468

tests/integration/_cases/build/build-upload-help-not-macos.trycmd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ Options:
5858
most CI environments.
5959
--no-git-metadata
6060
Disable collection and sending of git metadata.
61+
--gradle-plugin-version <gradle_plugin_version>
62+
The version of the Sentry Gradle plugin used to build the artifact.
63+
--fastlane-plugin-version <fastlane_plugin_version>
64+
The version of the Sentry Fastlane plugin used to build the artifact.
6165
-h, --help
6266
Print help
6367

0 commit comments

Comments
 (0)