Skip to content

Conversation

@mujacica
Copy link
Contributor

@mujacica mujacica commented Nov 11, 2025

This commit adds backend support for Perforce version control integration:

  • New Perforce integration with P4 client support
  • Installation logic
  • Tests for integration

Requires: #103287

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Nov 11, 2025
@cathteng
Copy link
Member

This PR is 2500 lines -- would it possible to split it up for review? There are aspects that should be reviewed by different teams like dependencies, specific features, etc

@mujacica mujacica force-pushed the feat/perforce-backend branch from f566dcc to ab41785 Compare November 13, 2025 12:05
@mujacica mujacica changed the base branch from master to feat/perforce-integration November 13, 2025 12:05
@mujacica mujacica force-pushed the feat/perforce-backend branch 2 times, most recently from 2172a79 to 09e3105 Compare November 13, 2025 12:10
@mujacica
Copy link
Contributor Author

Hey @cathteng, I extracted the stubs/generic code/dependencies into another PR: #103287

This one now contains only perforce-related logic and tests (note that 70% of the lines in the PR now are coming from the tests themselves).

@codecov
Copy link

codecov bot commented Nov 13, 2025

Codecov Report

❌ Patch coverage is 10.88710% with 221 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/sentry/integrations/perforce/integration.py 13.38% 123 Missing ⚠️
src/sentry/integrations/perforce/client.py 7.54% 98 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           master   #103171      +/-   ##
===========================================
- Coverage   80.59%    80.54%   -0.06%     
===========================================
  Files        9297      9297              
  Lines      396850    397078     +228     
  Branches    25281     25281              
===========================================
- Hits       319839    319809      -30     
- Misses      76551     76809     +258     
  Partials      460       460              

@mujacica mujacica force-pushed the feat/perforce-integration branch from a978a06 to 1197dcd Compare November 13, 2025 12:35
@mujacica mujacica force-pushed the feat/perforce-backend branch from 2a52e60 to ecfa2ce Compare November 13, 2025 12:37
@mujacica mujacica marked this pull request as ready for review November 13, 2025 13:00
@mujacica mujacica requested review from a team as code owners November 13, 2025 13:00
Copy link
Contributor

@Christinarlong Christinarlong left a comment

Choose a reason for hiding this comment

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

Could we break this PR down more to be on the feature level? This PR is still pretty insane to review and push out at once since there's like 4-5 features here. Maybe something like

  • integration installation & authentication ?
  • stacktrace linking
  • repository management
  • releases/commit tracking
  • suspect commits

Copy link
Member

@cathteng cathteng left a comment

Choose a reason for hiding this comment

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

Generally looks good, I haven't looked at the tests too thoroughly because I'm not familiar with how Perforce constructs its URLs 😅

Comment on lines 49 to 50
self.P4 = P4
self.P4Exception = P4Exception
Copy link
Member

Choose a reason for hiding this comment

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

nit: does this need to be in self if it's always the same thing?

path_without_rev, revision = path.rsplit("#", 1)

# If already absolute depot path, use as-is
if path_without_rev.startswith("//"):
Copy link
Member

Choose a reason for hiding this comment

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

Is it guaranteed that anything that starts with // will point to the absolute path?

)

# Apply pagination limit if specified
if page_number_limit and len(repositories) >= page_number_limit:
Copy link
Member

Choose a reason for hiding this comment

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

How does this apply the page number limit? For GitHub this is the max number of pages we will process because the API has pagination, I'm not sure it's the same thing as the total number of repos.

The page_number_limit is in the function signature because GH uses it, there are some source code integrations that don't use it so I think you can consider it optional

"""
return self.model.metadata

def update_organization_config(self, data: Mapping[str, Any]) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

I'm unsure if this function is necessary because we don't surface anywhere for you to be able to update the integration details after you install the integration (besides if you hit the OrganizationIntegrationDetails endpoint directly) and we don't actually store information on the OrganizationIntegration

Like Gitlab, you could leave get_organization_config and update_organization_config unimplemented in the PerforceIntegration class (it will return [] and be fine)

external_id = f"perforce-org-{organization_id}-{p4port_hash}"

# Store credentials in Integration.metadata
metadata = {
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice to type this

pipeline.bind_state("p4port", form_data.get("p4port"))
pipeline.bind_state("name", f"Perforce ({form_data.get('p4port')})")
# Include organization_id to create unique external_id per org
pipeline.bind_state("organization_id", pipeline.organization.id)
Copy link
Member

Choose a reason for hiding this comment

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

You can also use determine_active_organization(request) when you need to pull the organization in the next step

Comment on lines +721 to +722
pipeline.bind_state("p4port", form_data.get("p4port"))
pipeline.bind_state("name", f"Perforce ({form_data.get('p4port')})")
Copy link
Member

Choose a reason for hiding this comment

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

nit: do you need to bind these if they're already in the form data?

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.

6 participants