Skip to content

ref(vsts): Remove legacy pipeline views#113336

Open
evanpurkhiser wants to merge 1 commit intomasterfrom
evanpurkhiser/ref-vsts-remove-legacy-pipeline-views
Open

ref(vsts): Remove legacy pipeline views#113336
evanpurkhiser wants to merge 1 commit intomasterfrom
evanpurkhiser/ref-vsts-remove-legacy-pipeline-views

Conversation

@evanpurkhiser
Copy link
Copy Markdown
Member

@evanpurkhiser evanpurkhiser commented Apr 17, 2026

The VSTS (Azure DevOps) integration now uses the API-driven pipeline
exclusively. Remove the old view-based pipeline steps (AccountConfigView,
AccountForm, NestedPipelineView), the vsts-config.html template, and
legacy test helpers. Update vsts_extension to return views directly
instead of filtering the parent's views.

Refs VDY-60

The VSTS (Azure DevOps) integration now uses the API-driven pipeline
exclusively. Remove the old view-based pipeline steps (AccountConfigView,
AccountForm, NestedPipelineView), the vsts-config.html template, and
legacy test helpers. Update vsts_extension to return views directly
instead of filtering the parent's views.

Refs [VDY-32](https://linear.app/getsentry/issue/VDY-32/migrate-integration-setup-pipelines-to-api-driven-flows)
@evanpurkhiser evanpurkhiser requested review from a team as code owners April 17, 2026 18:18
@linear-code
Copy link
Copy Markdown

linear-code bot commented Apr 17, 2026

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Apr 17, 2026
Comment on lines 588 to 590
if "oauth_data" in state:
data = state["oauth_data"]
else:
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: The fallback else branch in build_integration accesses state["identity"], but the code that populates this key has been removed, creating a latent KeyError crash risk.
Severity: LOW

Suggested Fix

The else block is dead code and should be removed. Alternatively, if it's intended as a safeguard, it should be updated to raise a more informative error, like IntegrationError("Missing oauth_data from pipeline state"), instead of crashing with a KeyError.

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/integrations/vsts/integration.py#L588-L590

Potential issue: The `build_integration` function contains a fallback `else` branch that
attempts to access `state["identity"]["data"]`. This code path was intended for a legacy
pipeline flow that is being removed in this pull request. The new API-driven flow
exclusively populates `state["oauth_data"]`. If an unexpected edge case, such as
pipeline state corruption, causes `state["oauth_data"]` to be missing and the `else`
branch is executed, the application will crash with a `KeyError` because
`state["identity"]` is no longer populated by any code path.

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

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 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit cab95e9. Configure here.

views = [view for view in views if not isinstance(view, AccountConfigView)]
views.append(VstsExtensionFinishedView())
return views
return [VstsExtensionFinishedView()]
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.

Extension view pipeline missing OAuth step breaks installation

High Severity

VstsExtensionIntegrationProvider.get_pipeline_views() now returns only [VstsExtensionFinishedView()], dropping the OAuth step that was previously inherited from the parent's NestedPipelineView. The extension's view-based flow (triggered via VstsExtensionConfigurationView) jumps directly to VstsExtensionFinishedView.dispatch(), which calls pipeline.finish_pipeline()build_integration(). Since no OAuth step ran, neither oauth_data nor identity exists in pipeline state, causing a KeyError when accessing token data. The removed test_builds_integration_with_vsts_key test was covering exactly this flow.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cab95e9. Configure here.

config=identity_pipeline_config,
),
AccountConfigView(),
]
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.

Dead legacy fallback branch after removing views

Low Severity

The TODO comment that said "Remove the legacy path once the old views are retired" was deleted, but the else branch (state["identity"]["data"]) it referred to was kept. Since the NestedPipelineView that populated state["identity"] was removed in this PR, nothing in production sets this key anymore. For the normal VSTS flow, oauth_data is always present (via API steps), making the else branch unreachable dead code.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cab95e9. Configure here.

@github-actions
Copy link
Copy Markdown
Contributor

Backend Test Failures

Failures on fc57c0f in this run:

tests/sentry/web/frontend/test_vsts_extension_configuration.py::VstsExtensionConfigurationTest::test_choose_orglog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/web/frontend/test_vsts_extension_configuration.py:47: in test_choose_org
    resp = self.client.get(
.venv/lib/python3.13/site-packages/django/test/client.py:1124: in get
    response = super().get(
.venv/lib/python3.13/site-packages/django/test/client.py:475: in get
    return self.generic(
.venv/lib/python3.13/site-packages/django/test/client.py:671: in generic
    return self.request(**r)
.venv/lib/python3.13/site-packages/django/test/client.py:1087: in request
    self.check_exception(response)
.venv/lib/python3.13/site-packages/django/test/client.py:802: in check_exception
    raise exc_value
.venv/lib/python3.13/site-packages/django/core/handlers/exception.py:55: in inner
    response = get_response(request)
.venv/lib/python3.13/site-packages/django/core/handlers/base.py:197: in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
.venv/lib/python3.13/site-packages/django/views/generic/base.py:105: in view
    return self.dispatch(request, *args, **kwargs)
src/sentry/silo/base.py:157: in override
    return original_method(*args, **kwargs)
.venv/lib/python3.13/site-packages/django/utils/decorators.py:48: in _wrapper
    return bound_method(*args, **kwargs)
.venv/lib/python3.13/site-packages/django/views/decorators/csrf.py:65: in _view_wrapper
    return view_func(request, *args, **kwargs)
src/sentry/web/frontend/base.py:475: in dispatch
    return self.handle(request, *args, **kwargs)
src/sentry/web/frontend/base.py:490: in handle
    return super().dispatch(request, *args, **kwargs)
.venv/lib/python3.13/site-packages/django/views/generic/base.py:144: in dispatch
    return handler(request, *args, **kwargs)
src/sentry/integrations/web/integration_extension_configuration.py:95: in get
    return pipeline.current_step()
src/sentry/pipeline/base.py:192: in current_step
    return step.dispatch(self.request, pipeline=self)
src/sentry/integrations/vsts_extension/integration.py:42: in dispatch
    response = pipeline.finish_pipeline()
src/sentry/integrations/pipeline.py:234: in finish_pipeline
    data = self.provider.build_integration(self.state.data)
src/sentry/integrations/vsts_extension/integration.py:29: in build_integration
    return super().build_integration(
src/sentry/integrations/vsts/integration.py:591: in build_integration
    data = state["identity"]["data"]
E   KeyError: 'identity'
tests/sentry/web/frontend/test_vsts_extension_configuration.py::VstsExtensionConfigurationTest::test_goes_to_setup_unregisted_featurelog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/web/frontend/test_vsts_extension_configuration.py:73: in test_goes_to_setup_unregisted_feature
    resp = self.client.get(self.path, {"targetId": "1", "targetName": "foo"})
.venv/lib/python3.13/site-packages/django/test/client.py:1124: in get
    response = super().get(
.venv/lib/python3.13/site-packages/django/test/client.py:475: in get
    return self.generic(
.venv/lib/python3.13/site-packages/django/test/client.py:671: in generic
    return self.request(**r)
.venv/lib/python3.13/site-packages/django/test/client.py:1087: in request
    self.check_exception(response)
.venv/lib/python3.13/site-packages/django/test/client.py:802: in check_exception
    raise exc_value
.venv/lib/python3.13/site-packages/django/core/handlers/exception.py:55: in inner
    response = get_response(request)
.venv/lib/python3.13/site-packages/django/core/handlers/base.py:197: in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
.venv/lib/python3.13/site-packages/django/views/generic/base.py:105: in view
    return self.dispatch(request, *args, **kwargs)
src/sentry/silo/base.py:157: in override
    return original_method(*args, **kwargs)
.venv/lib/python3.13/site-packages/django/utils/decorators.py:48: in _wrapper
    return bound_method(*args, **kwargs)
.venv/lib/python3.13/site-packages/django/views/decorators/csrf.py:65: in _view_wrapper
    return view_func(request, *args, **kwargs)
src/sentry/web/frontend/base.py:475: in dispatch
    return self.handle(request, *args, **kwargs)
src/sentry/web/frontend/base.py:490: in handle
    return super().dispatch(request, *args, **kwargs)
.venv/lib/python3.13/site-packages/django/views/generic/base.py:144: in dispatch
    return handler(request, *args, **kwargs)
src/sentry/integrations/web/integration_extension_configuration.py:95: in get
    return pipeline.current_step()
src/sentry/pipeline/base.py:192: in current_step
    return step.dispatch(self.request, pipeline=self)
src/sentry/integrations/vsts_extension/integration.py:42: in dispatch
    response = pipeline.finish_pipeline()
src/sentry/integrations/pipeline.py:234: in finish_pipeline
    data = self.provider.build_integration(self.state.data)
src/sentry/integrations/vsts_extension/integration.py:29: in build_integration
    return super().build_integration(
src/sentry/integrations/vsts/integration.py:591: in build_integration
    data = state["identity"]["data"]
E   KeyError: 'identity'
tests/sentry/web/frontend/test_vsts_extension_configuration.py::VstsExtensionConfigurationTest::test_logged_in_one_orglog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/web/frontend/test_vsts_extension_configuration.py:25: in test_logged_in_one_org
    resp = self.client.get(self.path, {"targetId": "1", "targetName": "foo"})
.venv/lib/python3.13/site-packages/django/test/client.py:1124: in get
    response = super().get(
.venv/lib/python3.13/site-packages/django/test/client.py:475: in get
    return self.generic(
.venv/lib/python3.13/site-packages/django/test/client.py:671: in generic
    return self.request(**r)
.venv/lib/python3.13/site-packages/django/test/client.py:1087: in request
    self.check_exception(response)
.venv/lib/python3.13/site-packages/django/test/client.py:802: in check_exception
    raise exc_value
.venv/lib/python3.13/site-packages/django/core/handlers/exception.py:55: in inner
    response = get_response(request)
.venv/lib/python3.13/site-packages/django/core/handlers/base.py:197: in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
.venv/lib/python3.13/site-packages/django/views/generic/base.py:105: in view
    return self.dispatch(request, *args, **kwargs)
src/sentry/silo/base.py:157: in override
    return original_method(*args, **kwargs)
.venv/lib/python3.13/site-packages/django/utils/decorators.py:48: in _wrapper
    return bound_method(*args, **kwargs)
.venv/lib/python3.13/site-packages/django/views/decorators/csrf.py:65: in _view_wrapper
    return view_func(request, *args, **kwargs)
src/sentry/web/frontend/base.py:475: in dispatch
    return self.handle(request, *args, **kwargs)
src/sentry/web/frontend/base.py:490: in handle
    return super().dispatch(request, *args, **kwargs)
.venv/lib/python3.13/site-packages/django/views/generic/base.py:144: in dispatch
    return handler(request, *args, **kwargs)
src/sentry/integrations/web/integration_extension_configuration.py:95: in get
    return pipeline.current_step()
src/sentry/pipeline/base.py:192: in current_step
    return step.dispatch(self.request, pipeline=self)
src/sentry/integrations/vsts_extension/integration.py:42: in dispatch
    response = pipeline.finish_pipeline()
src/sentry/integrations/pipeline.py:234: in finish_pipeline
    data = self.provider.build_integration(self.state.data)
src/sentry/integrations/vsts_extension/integration.py:29: in build_integration
    return super().build_integration(
src/sentry/integrations/vsts/integration.py:591: in build_integration
    data = state["identity"]["data"]
E   KeyError: 'identity'

@evanpurkhiser evanpurkhiser requested review from a team and removed request for a team April 17, 2026 18:29
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants