diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ed6d65a09..ee6f90b371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Features + +- Add `--install-group` parameter to `sentry-cli build upload` for controlling update visibility between builds ([#3094](https://github.com/getsentry/sentry-cli/pull/3094)) + ## 3.1.0 ### New Features diff --git a/src/api/data_types/chunking/build.rs b/src/api/data_types/chunking/build.rs index cc94ab2fa1..b76a9fcb38 100644 --- a/src/api/data_types/chunking/build.rs +++ b/src/api/data_types/chunking/build.rs @@ -13,6 +13,8 @@ pub struct ChunkedBuildRequest<'a> { pub build_configuration: Option<&'a str>, #[serde(skip_serializing_if = "Option::is_none")] pub release_notes: Option<&'a str>, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + pub install_groups: &'a [String], #[serde(flatten)] pub vcs_info: &'a VcsInfo<'a>, } diff --git a/src/commands/build/upload.rs b/src/commands/build/upload.rs index 611a8ebd56..d55b2388ad 100644 --- a/src/commands/build/upload.rs +++ b/src/commands/build/upload.rs @@ -106,6 +106,16 @@ pub fn make_command(command: Command) -> Command { .long("release-notes") .help("The release notes to use for the upload.") ) + .arg( + Arg::new("install_group") + .long("install-group") + .action(ArgAction::Append) + .help( + "The install group(s) for this build. Can be specified multiple times. \ + Builds with at least one matching install group will be shown updates \ + for each other.", + ) + ) .arg( Arg::new("force_git_metadata") .long("force-git-metadata") @@ -175,6 +185,10 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { let build_configuration = matches.get_one("build_configuration").map(String::as_str); let release_notes = matches.get_one("release_notes").map(String::as_str); + let install_groups: Vec = matches + .get_many::("install_group") + .map(|vals| vals.cloned().collect()) + .unwrap_or_default(); let (plugin_name, plugin_version) = parse_plugin_from_pipeline(config.get_pipeline_env()); @@ -237,15 +251,13 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { for (path, zip) in normalized_zips { info!("Uploading file: {}", path.display()); let bytes = ByteView::open(zip.path())?; - match upload_file( - &authenticated_api, - &bytes, - &org, - &project, + let metadata = BuildMetadata { build_configuration, release_notes, - &vcs_info, - ) { + install_groups: &install_groups, + vcs_info: &vcs_info, + }; + match upload_file(&authenticated_api, &bytes, &org, &project, &metadata) { Ok(artifact_url) => { info!("Successfully uploaded file: {}", path.display()); uploaded_paths_and_urls.push((path.to_path_buf(), artifact_url)); @@ -588,19 +600,27 @@ fn handle_directory( normalize_directory(path, temp_dir.path(), plugin_name, plugin_version) } +/// Metadata for a build upload. +struct BuildMetadata<'a> { + build_configuration: Option<&'a str>, + release_notes: Option<&'a str>, + install_groups: &'a [String], + vcs_info: &'a VcsInfo<'a>, +} + /// Returns artifact url if upload was successful. fn upload_file( api: &AuthenticatedApi, bytes: &[u8], org: &str, project: &str, - build_configuration: Option<&str>, - release_notes: Option<&str>, - vcs_info: &VcsInfo<'_>, + metadata: &BuildMetadata<'_>, ) -> Result { debug!( - "Uploading file to organization: {org}, project: {project}, build_configuration: {}, vcs_info: {vcs_info:?}", - build_configuration.unwrap_or("unknown"), + "Uploading file to organization: {org}, project: {project}, build_configuration: {}, install_groups: {:?}, vcs_info: {:?}", + metadata.build_configuration.unwrap_or("unknown"), + metadata.install_groups, + metadata.vcs_info, ); let chunk_upload_options = api.get_chunk_upload_options(org)?; @@ -641,9 +661,10 @@ fn upload_file( &ChunkedBuildRequest { checksum, chunks: &checksums, - build_configuration, - release_notes, - vcs_info, + build_configuration: metadata.build_configuration, + release_notes: metadata.release_notes, + install_groups: metadata.install_groups, + vcs_info: metadata.vcs_info, }, )?; chunks.retain(|Chunk((digest, _))| response.missing_chunks.contains(digest)); diff --git a/tests/integration/_cases/build/build-upload-help-macos.trycmd b/tests/integration/_cases/build/build-upload-help-macos.trycmd index d1a0a51c4e..2b5d6e35f7 100644 --- a/tests/integration/_cases/build/build-upload-help-macos.trycmd +++ b/tests/integration/_cases/build/build-upload-help-macos.trycmd @@ -74,6 +74,10 @@ Options: --release-notes The release notes to use for the upload. + --install-group + The install group(s) for this build. Can be specified multiple times. Builds with at least + one matching install group will be shown updates for each other. + --force-git-metadata Force collection and sending of git metadata (branch, commit, etc.). If neither this nor --no-git-metadata is specified, git metadata is automatically collected when running in diff --git a/tests/integration/_cases/build/build-upload-help-not-macos.trycmd b/tests/integration/_cases/build/build-upload-help-not-macos.trycmd index ab4df7233e..e9d9461e89 100644 --- a/tests/integration/_cases/build/build-upload-help-not-macos.trycmd +++ b/tests/integration/_cases/build/build-upload-help-not-macos.trycmd @@ -73,6 +73,10 @@ Options: --release-notes The release notes to use for the upload. + --install-group + The install group(s) for this build. Can be specified multiple times. Builds with at least + one matching install group will be shown updates for each other. + --force-git-metadata Force collection and sending of git metadata (branch, commit, etc.). If neither this nor --no-git-metadata is specified, git metadata is automatically collected when running in