Skip to content

Conversation

@cameroncooke
Copy link
Contributor

@cameroncooke cameroncooke commented Jan 26, 2026

Add comparison support for insights:

  • ImageOptimizationInsightResult
  • StripBinaryInsightResult

Fixes bug that prevented LocalizedStringInsightResult results from showing.

These Apple-specific insights now display file-level diffs on the Compare page, showing which optimizable images or strippable binaries were added, removed, or changed between builds.

Changes:

  • Add _compare_optimizable_images and _compare_strip_binary_files functions to handle the different file structures of these insight types
  • Update _compare_insights to route these insight types to their specialized comparison functions
  • Add platform discriminator field to AndroidInsightResults and AppleInsightResults to fix union type parsing - without this, Pydantic uses left-to-right validation order which caused iOS insights to be incorrectly parsed as AndroidInsightResults, silently dropping iOS-specific fields like strip_binary and image_optimization
  • Add image_optimization to the frontend insight type union

Requires: getsentry/launchpad#544

Backwards Compatibility

These changes are backwards compatible with existing cached comparison results. When the platform discriminator field is missing from cached data (generated before the launchpad service changes), Pydantic falls back to the default value on each model. Since both AndroidInsightResults and AppleInsightResults have defaults for their platform field, Pydantic reverts to left-to-right union validation - preserving the existing (buggy) behavior where iOS results map to AndroidInsightResults.

This means iOS-only insights (image_optimization, strip_binary, 'minify_localized_strings`) will only appear for new comparisons processed after getsentry/launchpad#544 is deployed.

Before

Screenshot 2026-01-26 at 15 06 28

After

Screenshot 2026-01-26 at 15 06 40

Refs EME-678

@linear
Copy link

linear bot commented Jan 26, 2026

@github-actions github-actions bot added Scope: Frontend Automatically applied to PRs that change frontend components Scope: Backend Automatically applied to PRs that change backend components labels Jan 26, 2026
@github-actions
Copy link
Contributor

🚨 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 #discuss-dev-infra channel.

@cameroncooke cameroncooke marked this pull request as ready for review January 26, 2026 15:05
@cameroncooke cameroncooke requested a review from a team as a code owner January 26, 2026 15:05
Comment on lines +140 to +143
insights: (
Annotated[AndroidInsightResults | AppleInsightResults, Field(discriminator="platform")]
| None
) = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The SizeAnalysisResults model now requires a platform field. Parsing old analysis data lacking this field via parse_raw() will raise an unhandled ValidationError, crashing the task.
Severity: HIGH

Suggested Fix

To ensure backward compatibility, add try/except ValidationError blocks around the parse_raw() calls to gracefully handle parsing failures for old data. The exception handler could attempt to re-parse the data with a default platform or log the failure without crashing the entire task. Alternatively, adjust the Pydantic model to handle the absence of the platform field in old data.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: src/sentry/preprod/size_analysis/models.py#L140-L143

Potential issue: The introduction of a Pydantic discriminated union on the `insights`
field in `SizeAnalysisResults` makes the `platform` field a required discriminator.
However, historical size analysis data stored in the database lacks this field. When a
size comparison task is run against an old baseline, the call to
`SizeAnalysisResults.parse_raw()` will fail with a `ValidationError`. Since these calls
are not wrapped in error handling blocks, the task will crash, leading to failed
analysis comparisons and incomplete results.

Did we get this right? 👍 / 👎 to inform future reviews.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a valid issue. The platform field has a default value on both models:

class AndroidInsightResults(BaseModel):
    platform: Literal["android"] = "android"

class AppleInsightResults(BaseModel):
    platform: Literal["apple"] = "apple"

When the discriminator field is missing from historical data, Pydantic does not raise a ValidationError. Instead, it falls back to left-to-right union validation and uses the default value.

Verified in Django shell:

>>> old_data = '{"analysis_duration": 0.5, "insights": {"image_optimization": {"total_savings": 1200, "optimizable_files": []}}}'
>>> result = SizeAnalysisResults.parse_raw(old_data)
>>> result.insights
AndroidInsightResults(platform='android', ...)  # Parses successfully, no crash

The behavior is:

  • Old data (no platform field): Parses successfully, maps to AndroidInsightResults, iOS-specific fields silently dropped (existing behavior)
  • New data (with platform field): Parses successfully, correctly maps to the right model type

This is the intended backwards-compatible behavior documented in the PR.

Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

'multiple_native_library_archs',
'video_compression',
'image_optimization',
'strip_binary',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing frontend entry for alternate_icons_optimization insight

Medium Severity

The backend now generates file-level diffs for ImageOptimizationInsightResult, which is used by both image_optimization and alternate_icons_optimization fields in AppleInsightResults. However, only 'strip_binary' was added to FILE_DIFF_INSIGHT_TYPES in the frontend - 'alternate_icons_optimization' is missing. When comparison data for alternate icons optimization exists, the frontend will silently drop it (returning null in the map), and users won't see the insight despite the backend generating valid comparison data.

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ticket only talks to LocalizedStringInsightResult, ImageOptimizationInsightResult and StripBinaryInsightResult

base_files = base_insight.files if isinstance(base_insight, FilesInsightResult) else []
head_groups = head_insight.groups if isinstance(head_insight, GroupsInsightResult) else []
base_groups = base_insight.groups if isinstance(base_insight, GroupsInsightResult) else []
head_optimizable_images = (
Copy link
Member

@rbro112 rbro112 Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better as a follow up, but all these assignments might be better suited if we instead just check the instance type of the overall insight where our current file/group checks are (line 761). So something more like

if isinstance(head_insight, FilesInsightResults) and isinstance(base_insight, FilesInsightResults): 
  # Handle files insight
elif isinstance(head_insight, GroupsInsightsResults) ... :
  ...
elif ... 

I know you're just following existing patterns here, so no worries, but could be a nice cleanup now that there's more branching logic

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I saw that the existing pattern wasn't scaling well. Good call.

def _compare_optimizable_images(
head_files: list[OptimizableImageFile], base_files: list[OptimizableImageFile]
) -> list[DiffItem]:
"""Compare optimizable image files between head and base builds."""
Copy link
Member

@rbro112 rbro112 Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Would recommend removing these type of comments, usually they're not adding anything since the function is pretty self-explanatory

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point.

@cameroncooke cameroncooke merged commit 2bb3d40 into master Jan 26, 2026
75 checks passed
@cameroncooke cameroncooke deleted the cameroncooke/01-26-feat_preprod_add_support_for_apple_insight_types_on_compare branch January 26, 2026 19:39
dashed pushed a commit that referenced this pull request Jan 26, 2026
Add comparison support for insights:

- `ImageOptimizationInsightResult`
- `StripBinaryInsightResult`

Fixes bug that prevented `LocalizedStringInsightResult` results from
showing.

These Apple-specific insights now display file-level diffs on the
Compare page, showing which optimizable images or strippable binaries
were added, removed, or changed between builds.

**Changes:**
- Add `_compare_optimizable_images` and `_compare_strip_binary_files`
functions to handle the different file structures of these insight types
- Update `_compare_insights` to route these insight types to their
specialized comparison functions
- Add `platform` discriminator field to `AndroidInsightResults` and
`AppleInsightResults` to fix union type parsing - without this, Pydantic
uses left-to-right validation order which caused iOS insights to be
incorrectly parsed as `AndroidInsightResults`, silently dropping
iOS-specific fields like `strip_binary` and `image_optimization`
- Add `image_optimization` to the frontend insight type union

Requires: getsentry/launchpad#544

## Backwards Compatibility

These changes are backwards compatible with existing cached comparison
results. When the `platform` discriminator field is missing from cached
data (generated before the launchpad service changes), Pydantic falls
back to the default value on each model. Since both
`AndroidInsightResults` and `AppleInsightResults` have defaults for
their `platform` field, Pydantic reverts to left-to-right union
validation - preserving the existing (buggy) behavior where iOS results
map to `AndroidInsightResults`.

This means iOS-only insights (`image_optimization`, `strip_binary`,
'minify_localized_strings`) will only appear for new comparisons
processed after getsentry/launchpad#544 is
deployed.

## Before

<img width="917" height="683" alt="Screenshot 2026-01-26 at 15 06 28"
src="https://github.com/user-attachments/assets/69d680d8-0b6a-4d61-a059-8e0bfbc57252"
/>

## After 

<img width="895" height="801" alt="Screenshot 2026-01-26 at 15 06 40"
src="https://github.com/user-attachments/assets/9f7a2f89-0327-42e7-9ca5-d36a58db7b12"
/>

Refs EME-678
JonasBa pushed a commit that referenced this pull request Jan 27, 2026
Add comparison support for insights:

- `ImageOptimizationInsightResult`
- `StripBinaryInsightResult`

Fixes bug that prevented `LocalizedStringInsightResult` results from
showing.

These Apple-specific insights now display file-level diffs on the
Compare page, showing which optimizable images or strippable binaries
were added, removed, or changed between builds.

**Changes:**
- Add `_compare_optimizable_images` and `_compare_strip_binary_files`
functions to handle the different file structures of these insight types
- Update `_compare_insights` to route these insight types to their
specialized comparison functions
- Add `platform` discriminator field to `AndroidInsightResults` and
`AppleInsightResults` to fix union type parsing - without this, Pydantic
uses left-to-right validation order which caused iOS insights to be
incorrectly parsed as `AndroidInsightResults`, silently dropping
iOS-specific fields like `strip_binary` and `image_optimization`
- Add `image_optimization` to the frontend insight type union

Requires: getsentry/launchpad#544

## Backwards Compatibility

These changes are backwards compatible with existing cached comparison
results. When the `platform` discriminator field is missing from cached
data (generated before the launchpad service changes), Pydantic falls
back to the default value on each model. Since both
`AndroidInsightResults` and `AppleInsightResults` have defaults for
their `platform` field, Pydantic reverts to left-to-right union
validation - preserving the existing (buggy) behavior where iOS results
map to `AndroidInsightResults`.

This means iOS-only insights (`image_optimization`, `strip_binary`,
'minify_localized_strings`) will only appear for new comparisons
processed after getsentry/launchpad#544 is
deployed.

## Before

<img width="917" height="683" alt="Screenshot 2026-01-26 at 15 06 28"
src="https://github.com/user-attachments/assets/69d680d8-0b6a-4d61-a059-8e0bfbc57252"
/>

## After 

<img width="895" height="801" alt="Screenshot 2026-01-26 at 15 06 40"
src="https://github.com/user-attachments/assets/9f7a2f89-0327-42e7-9ca5-d36a58db7b12"
/>

Refs EME-678
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants