feat(preprod): Add public build-distribution/latest endpoint#109584
feat(preprod): Add public build-distribution/latest endpoint#109584
Conversation
- Add organization-scoped public install-details endpoint - Add platform property to PreprodArtifact model - Add ArtifactInstallInfo and get_artifact_install_info utility - Refactor existing install-details endpoint to use shared utility - Refactor size_analysis tasks to use model platform property - Add install info response models and OpenAPI examples
|
🚨 Warning: This pull request contains Frontend and Backend changes! It's discouraged to make changes to Sentry's Frontend and Backend in a single pull request. The Frontend and Backend are not atomically deployed. If the changes are interdependent of each other, they must be separated into two pull requests and be made forward or backwards compatible, such that the Backend or Frontend can be safely deployed independently. Have questions? Please ask in the |
3786bbf to
62e6912
Compare
8ee58aa to
ce905bd
Compare
ce905bd to
7149f64
Compare
8d99752 to
12b6f00
Compare
Compare against Platform.APPLE and Platform.ANDROID enum members instead of raw string literals for consistency and type safety. Co-Authored-By: Claude <noreply@anthropic.com>
Uppercase the platform enum value in public endpoint responses for consistency with other enum fields like artifactType. Internal endpoints are unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
Consolidate public build distribution response models into a single file, consistent with how size_analysis.py is organized.
…tails get_artifact_install_info() already computes install_url and download_count. The endpoint was calling the underlying functions a second time, creating an orphaned InstallablePreprodArtifact DB row on every GET request.
Add project-scoped endpoint to list the latest installable builds with filtering by platform, appId, branch, buildVersion, buildConfiguration, prNumber, and installGroup.
e165283 to
ba4f67d
Compare
src/sentry/preprod/api/endpoints/public/project_preprod_build_distribution_latest.py
Show resolved
Hide resolved
- Rename response fields to `latestArtifact`/`currentArtifact` for clarity - Rename query param to `installGroups` (plural) and read via getlist - Remove ListField from validator; read installGroups directly from request - Add logging when current artifact not found in check-for-updates mode Co-Authored-By: Claude <noreply@anthropic.com>
Reorganize tests into separate classes by flow: - LatestBuildValidationTest: feature flags and param validation - LatestBuildModeTest: latest-only mode behavior - LatestBuildFilteringTest: explicit filter parameters - CheckForUpdatesTest: check-for-updates mode with filter inheritance Adds coverage for combined filter inheritance, multi-group install groups, and query param arrays to match legacy checkForUpdates endpoint. Co-Authored-By: Claude <noreply@anthropic.com>
src/sentry/preprod/api/endpoints/public/project_preprod_build_distribution_latest.py
Outdated
Show resolved
Hide resolved
Backend Test FailuresFailures on
|
Match the legacy checkForUpdates endpoint by requiring project:distribution scope and adding a 100 req/min org rate limit. Co-Authored-By: Claude <noreply@anthropic.com>
src/sentry/preprod/api/endpoints/public/project_preprod_build_distribution_latest.py
Show resolved
Hide resolved
src/sentry/preprod/api/endpoints/project_preprod_artifact_install_details.py
Outdated
Show resolved
Hide resolved
- Move logger after all imports to avoid splitting import blocks - Use consistent truthiness check for build_version parameter - Revert stricter installability check on deprecated endpoint to preserve backward compatibility Co-Authored-By: Claude <noreply@anthropic.com>
Add org-level rate limiting (100 req/min) to the public install details endpoint to match the latest build endpoint. Co-Authored-By: Claude <noreply@anthropic.com>
| publish_status = { | ||
| "GET": ApiPublishStatus.PUBLIC, | ||
| } | ||
| rate_limits = RateLimitConfig( |
| if highest_version is None or parsed_version > parse_version(highest_version): | ||
| highest_version = version | ||
| except Exception: | ||
| continue |
There was a problem hiding this comment.
Redundant re-parsing of highest_version each loop iteration
Low Severity
In find_latest_installable_artifact, parse_version(highest_version) is called on every loop iteration when comparing against the current best version. The parsed result of highest_version could be cached in a local variable and updated only when highest_version changes, avoiding redundant reparsing for every version string in the queryset.
The DB column stores an integer, so validate it as IntegerField in the serializer rather than silently returning None on non-numeric input. Also updates the function signature and OpenAPI param type to match. Co-Authored-By: Claude <noreply@anthropic.com>
src/sentry/preprod/api/endpoints/public/project_preprod_build_distribution_latest.py
Outdated
Show resolved
Hide resolved
…test build lookup find_latest_installable_artifact() could return an XCARCHIVE with an invalid code signature as the "latest installable build". Now iterates through candidates and skips Apple artifacts where is_code_signature_valid is not set in extras. Co-Authored-By: Claude <noreply@anthropic.com>
Backend Test FailuresFailures on
|
Filter out empty strings from installGroups query param so that ?installGroups= is treated the same as omitting the param. Use `build_number is None` instead of `not build_number` in the cross-field validator so that buildNumber=0 is accepted as valid. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| try: | ||
| return ( | ||
| PreprodArtifact.objects.select_related( | ||
| "project", "build_configuration", "commit_comparison", "mobile_app_info" |
There was a problem hiding this comment.
Missing project__organization in select_related causes extra queries
Medium Severity
Both find_current_artifact and find_latest_installable_artifact use select_related("project", ...) but omit "project__organization". The returned artifacts are passed to create_install_info_dict, which calls get_download_url_for_artifact, which accesses artifact.project.organization.slug (line 130). This triggers an additional lazy-loaded DB query per installable artifact. The existing OrganizationPreprodArtifactPublicInstallDetailsEndpoint correctly includes "project__organization" in its select_related for the same reason.
Additional Locations (1)
Backend Test FailuresFailures on
|


Summary
Split from #109377, stacked on #109583.
Replaces the paginated list endpoint at
GET /api/0/projects/{org}/{project}/preprodartifacts/build-distribution/latest/with a single-result endpoint that returns the latest installable build, with optional check-for-updates support.Two modes:
appId+platform, gets back the newest installable build (highest semver version, highest build number as tiebreaker).buildVersion+ (buildNumberormainBinaryIdentifier), gets back both the current and latest builds plus anupdateAvailableboolean.Key behaviors:
buildConfiguration,codesigningType, andinstallGroup(repeatable).buildConfiguration,codesigningType, andinstallGroupsfrom the current artifact when not explicitly provided.PROCESSEDartifacts with an installable app file and build number.installGroupsfield to the build response shape.Response shape:
{ "latest": { <build> } | null, "current": { <build> } | null, "updateAvailable": true | false | null }Extracted shared helpers (
find_current_artifact,find_latest_installable_artifact,build_install_groups_q,get_platform_artifact_type_filter) intobuild_distribution_utils.pyfor reuse. Added deprecation comment to the experimentalcheck-for-updatesendpoint.Test plan
check-for-updatesendpoint tests still pass (29/29)