Skip to content

Commit f566dcc

Browse files
committed
feat(perforce): Add backend support for Perforce integration
This commit adds backend support for Perforce version control integration: - New Perforce integration with P4 client support - Repository and code mapping functionality - Stacktrace linking for Perforce depot paths - Tests for integration, code mapping, and stacktrace linking - Updated dependencies in pyproject.toml The integration supports: - Authentication via P4PORT, P4USER, P4PASSWD - Code mapping between depot paths and project structure - Source URL generation for stacktrace frames - Integration with Sentry's repository and code mapping systems
1 parent 753774b commit f566dcc

File tree

17 files changed

+2609
-1
lines changed

17 files changed

+2609
-1
lines changed

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ dependencies = [
5353
"objectstore-client>=0.0.5",
5454
"openai>=1.3.5",
5555
"orjson>=3.10.10",
56+
"p4python>=2025.1.2767466",
5657
"packaging>=24.1",
5758
"parsimonious>=0.10.0",
5859
"petname>=2.6",
@@ -295,6 +296,7 @@ module = [
295296
"onelogin.saml2.auth.*",
296297
"onelogin.saml2.constants.*",
297298
"onelogin.saml2.idp_metadata_parser.*",
299+
"P4",
298300
"rb.*",
299301
"statsd.*",
300302
"tokenizers.*",

src/sentry/conf/server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2149,6 +2149,7 @@ def custom_parameter_sort(parameter: dict) -> tuple[str, int]:
21492149
"sentry.integrations.discord.DiscordIntegrationProvider",
21502150
"sentry.integrations.opsgenie.OpsgenieIntegrationProvider",
21512151
"sentry.integrations.cursor.integration.CursorAgentIntegrationProvider",
2152+
"sentry.integrations.perforce.integration.PerforceIntegrationProvider",
21522153
)
21532154

21542155

src/sentry/integrations/api/bases/external_actor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
ExternalProviders.SLACK,
3535
ExternalProviders.MSTEAMS,
3636
ExternalProviders.JIRA_SERVER,
37+
ExternalProviders.PERFORCE,
3738
ExternalProviders.CUSTOM,
3839
}
3940

src/sentry/integrations/api/endpoints/organization_code_mappings.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ class RepositoryProjectPathConfigSerializer(CamelSnakeModelSerializer):
4242
source_root = gen_path_regex_field()
4343
default_branch = serializers.RegexField(
4444
r"^(^(?![\/]))([\w\.\/-]+)(?<![\/])$",
45-
required=True,
45+
required=False, # Validated in validate_default_branch based on integration type
46+
allow_blank=True, # Perforce allows empty streams
4647
error_messages={"invalid": _(BRANCH_NAME_ERROR_MESSAGE)},
4748
)
4849
instance: RepositoryProjectPathConfig | None
@@ -98,6 +99,24 @@ def validate_project_id(self, project_id):
9899
raise serializers.ValidationError("Project does not exist")
99100
return project_id
100101

102+
def validate_default_branch(self, default_branch):
103+
# Get the integration to check if it's Perforce
104+
integration = integration_service.get_integration(
105+
integration_id=self.org_integration.integration_id
106+
)
107+
108+
# For Perforce, allow empty branch (streams are part of depot path)
109+
if integration and integration.provider == "perforce":
110+
# Allow empty string for Perforce
111+
if not default_branch:
112+
return default_branch
113+
else:
114+
# For other integrations, branch is required
115+
if not default_branch:
116+
raise serializers.ValidationError("This field is required.")
117+
118+
return default_branch
119+
101120
def create(self, validated_data):
102121
return RepositoryProjectPathConfig.objects.create(
103122
organization_integration_id=self.org_integration.id,

src/sentry/integrations/models/external_actor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class ExternalActor(ReplicatedRegionModel):
4141
(ExternalProviders.GITHUB_ENTERPRISE, IntegrationProviderSlug.GITHUB_ENTERPRISE.value),
4242
(ExternalProviders.GITLAB, IntegrationProviderSlug.GITLAB.value),
4343
(ExternalProviders.JIRA_SERVER, IntegrationProviderSlug.JIRA_SERVER.value),
44+
(ExternalProviders.PERFORCE, IntegrationProviderSlug.PERFORCE.value),
4445
# TODO: do migration to delete this from database
4546
(ExternalProviders.CUSTOM, "custom_scm"),
4647
),
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from __future__ import annotations
2+
3+
from .integration import PerforceIntegration, PerforceIntegrationProvider
4+
from .repository import PerforceRepositoryProvider
5+
6+
__all__ = ["PerforceIntegration", "PerforceIntegrationProvider", "PerforceRepositoryProvider"]

0 commit comments

Comments
 (0)