Skip to content

chore(ACI): Normalize sentry app data in Actions#107091

Merged
ceorourke merged 6 commits intomasterfrom
ceorourke/ISWF-783
Jan 28, 2026
Merged

chore(ACI): Normalize sentry app data in Actions#107091
ceorourke merged 6 commits intomasterfrom
ceorourke/ISWF-783

Conversation

@ceorourke
Copy link
Copy Markdown
Member

@ceorourke ceorourke commented Jan 27, 2026

Sentry app Action config data currently has 2 different types of data stored - sometimes we store the sentry app installation uuid, and sometimes we store the sentry app id. Going forward we are only creating actions with the sentry app id and need to migrate the data with the installation uuid to lookup the sentry app id and store that instead. Currently we handle both pieces of data, but once we run this migration we can go back and only support one.

Example of a config we do not want:

{
    "target_type":3,
    "target_display":null,
    "target_identifier":"abc123-def456",
    "sentry_app_identifier":"sentry_app_installation_uuid"
}

Example of a config we do want:

{
    "target_type":3,
    "target_display":"My WebHook",
    "target_identifier":"12345",
    "sentry_app_identifier":"sentry_app_id"
}

Fixes https://linear.app/getsentry/issue/ISWF-783/

@linear
Copy link
Copy Markdown

linear Bot commented Jan 27, 2026

@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Jan 27, 2026
Comment thread src/sentry/workflow_engine/migrations/0106_migrate_actions_sentry_app_data.py Outdated
Comment thread src/sentry/workflow_engine/migrations/0106_migrate_actions_sentry_app_data.py Outdated
@github-actions
Copy link
Copy Markdown
Contributor

This PR has a migration; here is the generated SQL for src/sentry/workflow_engine/migrations/0106_migrate_actions_sentry_app_data.py

for 0106_migrate_actions_sentry_app_data in workflow_engine

--
-- Raw Python operation
--
-- THIS OPERATION CANNOT BE WRITTEN AS SQL

Comment thread src/sentry/workflow_engine/service/action/impl.py Outdated
Comment thread src/sentry/workflow_engine/migrations/0106_migrate_actions_sentry_app_data.py Outdated
Comment thread src/sentry/hybridcloud/outbox/category.py Outdated
Comment thread src/sentry/workflow_engine/migrations/0106_migrate_actions_sentry_app_data.py Outdated
Comment thread src/sentry/receivers/outbox/control.py Outdated
Comment thread src/sentry/workflow_engine/migrations/0106_migrate_actions_sentry_app_data.py Outdated
@ceorourke ceorourke marked this pull request as ready for review January 28, 2026 00:20
@ceorourke ceorourke requested review from a team as code owners January 28, 2026 00:20
Comment thread src/sentry/receivers/outbox/region.py
Comment thread src/sentry/receivers/outbox/region.py
Comment thread src/sentry/receivers/outbox/region.py Outdated
Comment thread src/sentry/receivers/outbox/region.py
)
return

if installs:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there something else we should do to the action if no valid sentry app installation is found? I remember Cathy doing some cleanup logic for enabling/disabling actions based on sentry app status.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ooh I'll ask her tomorrow, I could see disabling being a reasonable outcome if we can't find it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There's logic today to disable actions when the corresponding sentry app or install is deleted, e.g. #101492 and #100813

It already handles both types of sentry app action configs, and I've done a backfill migration to disable too

}
)
if len(installs) > 1:
logger.info(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TIL these UUIDs are not unique 😱 Do we even have duplicates in our DB?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

oh this was a "just in case" since I used get_many(), I don't actually expect it to happen.

Comment on lines +41 to +47
assert (
self.installation_uuid_action.config.get("sentry_app_identifier")
== SentryAppIdentifier.SENTRY_APP_ID
)
assert self.installation_uuid_action.config.get("target_identifier") == str(
self.sentry_app.id
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm shocked this works. I would have expected this to fail because we're not explicitly flushing these outboxes, so there must be some setup flushing these coincidentally?

Comment thread src/sentry/receivers/outbox/region.py
Comment on lines +80 to +82
action.config["target_identifier"] = str(installs[0].sentry_app.id)
action.config["sentry_app_identifier"] = SentryAppIdentifier.SENTRY_APP_ID
action.save()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: In-place modification of the action.config JSONField may not be persisted to the database upon calling action.save() because Django does not detect the change.
Severity: MEDIUM

Suggested Fix

To ensure the changes are saved, create a copy of the action.config dictionary, apply the modifications to the copy, and then reassign the entire modified dictionary back to action.config before calling action.save(). For example: new_config = action.config.copy(); new_config['key'] = 'value'; action.config = new_config; action.save().

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/receivers/outbox/region.py#L80-L82

Potential issue: The code in the outbox receiver at
`src/sentry/receivers/outbox/region.py` modifies the `action.config` dictionary
in-place. Specifically, it sets `target_identifier` and `sentry_app_identifier` on the
existing dictionary object. According to Django's ORM behavior, in-place mutations to a
`JSONField` are not automatically detected. As a result, the subsequent call to
`action.save()` may not persist these changes to the database, leaving the action's
configuration in an incorrect state after the migration logic runs.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Gah this is why I did the copy and update but now I am not sure which is best, going to check Django docs

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I can't find anything directly on the Django docs but I checked a handful of pages and they all say the way I have it now is the way to do it.

Copy link
Copy Markdown
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.

if installs:
action.config["target_identifier"] = str(installs[0].sentry_app.id)
action.config["sentry_app_identifier"] = SentryAppIdentifier.SENTRY_APP_ID
action.save()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Redundant conditional checks on the same variable

Low Severity

The handler checks if not installs: (line 70) without returning, then immediately checks if installs: (line 79). These conditions are mutually exclusive, making the second check redundant. The if installs: block would be clearer as an else: clause, since it's logically the opposite of if not installs:.

Fix in Cursor Fix in Web

@ceorourke ceorourke merged commit 0af0f2a into master Jan 28, 2026
90 of 91 checks passed
@ceorourke ceorourke deleted the ceorourke/ISWF-783 branch January 28, 2026 17:41
@ceorourke ceorourke added the Trigger: Revert Add to a merged PR to revert it (skips CI) label Jan 28, 2026
@getsentry-bot
Copy link
Copy Markdown
Contributor

PR reverted: 75500c1

getsentry-bot added a commit that referenced this pull request Jan 28, 2026
This reverts commit 0af0f2a.

Co-authored-by: ceorourke <29959063+ceorourke@users.noreply.github.com>
ceorourke added a commit that referenced this pull request Jan 28, 2026
Reverting #107091 caused some
messages to get stuck because the outbox category wasn't there anymore.
This adds it back and adds a noop receiver to they can be processed.

Fixes https://sentry.sentry.io/issues/7225310852/?project=1
ceorourke added a commit that referenced this pull request Jan 30, 2026
A redo of #107091

Sentry app `Action` config data currently has 2 different types of data
stored - sometimes we store the sentry app installation uuid, and
sometimes we store the sentry app id. Going forward we are only creating
actions with the sentry app id and need to migrate the data with the
installation uuid to lookup the sentry app id and store that instead.
[Currently](#103790) we handle
both pieces of data, but once we run this migration we can go back and
only support one.

Example of a `config` we do not want:
```
{
    "target_type":3,
    "target_display":null,
    "target_identifier":"abc123-def456",
    "sentry_app_identifier":"sentry_app_installation_uuid"
}
```

Example of a `config` we do want:
```
{
    "target_type":3,
    "target_display":"My WebHook",
    "target_identifier":"12345",
    "sentry_app_identifier":"sentry_app_id"
}
```

Fixes https://linear.app/getsentry/issue/ISWF-783/
priscilawebdev pushed a commit that referenced this pull request Feb 2, 2026
Sentry app `Action` config data currently has 2 different types of data
stored - sometimes we store the sentry app installation uuid, and
sometimes we store the sentry app id. Going forward we are only creating
actions with the sentry app id and need to migrate the data with the
installation uuid to lookup the sentry app id and store that instead.
[Currently](#103790) we handle
both pieces of data, but once we run this migration we can go back and
only support one.

Example of a `config` we do not want:
```
{
    "target_type":3,
    "target_display":null,
    "target_identifier":"abc123-def456",
    "sentry_app_identifier":"sentry_app_installation_uuid"
}
```

Example of a `config` we do want:
```
{
    "target_type":3,
    "target_display":"My WebHook",
    "target_identifier":"12345",
    "sentry_app_identifier":"sentry_app_id"
}
```

Fixes https://linear.app/getsentry/issue/ISWF-783/
priscilawebdev pushed a commit that referenced this pull request Feb 2, 2026
This reverts commit 0af0f2a.

Co-authored-by: ceorourke <29959063+ceorourke@users.noreply.github.com>
priscilawebdev pushed a commit that referenced this pull request Feb 2, 2026
Reverting #107091 caused some
messages to get stuck because the outbox category wasn't there anymore.
This adds it back and adds a noop receiver to they can be processed.

Fixes https://sentry.sentry.io/issues/7225310852/?project=1
priscilawebdev pushed a commit that referenced this pull request Feb 2, 2026
A redo of #107091

Sentry app `Action` config data currently has 2 different types of data
stored - sometimes we store the sentry app installation uuid, and
sometimes we store the sentry app id. Going forward we are only creating
actions with the sentry app id and need to migrate the data with the
installation uuid to lookup the sentry app id and store that instead.
[Currently](#103790) we handle
both pieces of data, but once we run this migration we can go back and
only support one.

Example of a `config` we do not want:
```
{
    "target_type":3,
    "target_display":null,
    "target_identifier":"abc123-def456",
    "sentry_app_identifier":"sentry_app_installation_uuid"
}
```

Example of a `config` we do want:
```
{
    "target_type":3,
    "target_display":"My WebHook",
    "target_identifier":"12345",
    "sentry_app_identifier":"sentry_app_id"
}
```

Fixes https://linear.app/getsentry/issue/ISWF-783/
@github-actions github-actions Bot locked and limited conversation to collaborators Feb 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Scope: Backend Automatically applied to PRs that change backend components Trigger: Revert Add to a merged PR to revert it (skips CI)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants