Skip to content

Conversation

dennis-bilson-port
Copy link
Member

@dennis-bilson-port dennis-bilson-port commented Oct 17, 2025

User description

Added support for real-time webhook events for GitLab releases and tags, enabling immediate synchronization when releases are created or tags are pushed.

Type of change

Please leave one option from the following and delete the rest:

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • New Integration (non-breaking change which adds a new integration)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Non-breaking change (fix of existing functionality that will not change current behavior)
  • Documentation (added/updated documentation)

All tests should be run against the port production environment(using a testing org).

Core testing checklist

  • Integration able to create all default resources from scratch
  • Resync finishes successfully
  • Resync able to create entities
  • Resync able to update entities
  • Resync able to detect and delete entities
  • Scheduled resync able to abort existing resync and start a new one
  • Tested with at least 2 integrations from scratch
  • Tested with Kafka and Polling event listeners
  • Tested deletion of entities that don't pass the selector

Integration testing checklist

  • Integration able to create all default resources from scratch
  • Completed a full resync from a freshly installed integration and it completed successfully
  • Resync able to create entities
  • Resync able to update entities
  • Resync able to detect and delete entities
  • Resync finishes successfully
  • If new resource kind is added or updated in the integration, add example raw data, mapping and expected result to the examples folder in the integration directory.
  • If resource kind is updated, run the integration with the example data and check if the expected result is achieved
  • If new resource kind is added or updated, validate that live-events for that resource are working as expected
  • Docs PR link here

Preflight checklist

  • Handled rate limiting
  • Handled pagination
  • Implemented the code in async
  • Support Multi account

Screenshots

image image image

API Documentation

Provide links to the API documentation used for this integration.


PR Type

Enhancement


Description

  • Added webhook processors for GitLab tag and release events

  • Implemented client methods to fetch individual tags and releases

  • Added resync handlers for tags and releases resource kinds

  • Extended integration configuration to support tag and release resources


Diagram Walkthrough

flowchart LR
  A["GitLab Webhook Events"] -->|tag_push| B["TagWebhookProcessor"]
  A -->|release| C["ReleaseWebhookProcessor"]
  B -->|get_tag| D["GitLabClient"]
  C -->|get_release| D
  D -->|enrich with project| E["Updated Raw Results"]
  B -->|resync| F["on_resync_tags"]
  C -->|resync| G["on_resync_releases"]
  F -->|get_tags| D
  G -->|get_releases| D
Loading

File Walkthrough

Relevant files
Enhancement
6 files
gitlab_client.py
Add tag and release fetching methods                                         
+84/-0   
utils.py
Add TAG and RELEASE object kinds                                                 
+2/-0     
tag_webhook_processor.py
Implement tag webhook event processor                                       
+38/-0   
release_webhook_processor.py
Implement release webhook event processor                               
+39/-0   
integration.py
Add tag and release resource configurations                           
+12/-0   
main.py
Register tag and release webhook processors                           
+42/-0   
Tests
3 files
test_gitlab_client.py
Add tests for tag and release client methods                         
+103/-0 
test_tag_webhook_processor.py
Add tag webhook processor unit tests                                         
+93/-0   
test_release_webhook_processor.py
Add release webhook processor unit tests                                 
+93/-0   
Configuration changes
2 files
spec.yaml
Add releases and tags to spec resources                                   
+2/-0     
pyproject.toml
Bump version to 0.3.1                                                                       
+1/-1     
Documentation
1 files
CHANGELOG.md
Document version 0.3.1 and 0.3.0 releases                               
+18/-0   

@dennis-bilson-port dennis-bilson-port self-assigned this Oct 17, 2025
@dennis-bilson-port dennis-bilson-port marked this pull request as ready for review October 17, 2025 00:53
Copy link
Contributor

qodo-merge-pro bot commented Oct 17, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
- [ ] Create ticket/issue <!-- /create_ticket --create_ticket=true -->

</details></td></tr>
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
No custom compliance provided

Follow the guide to enable custom compliance check.

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Copy link
Contributor

qodo-merge-pro bot commented Oct 17, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Missing deletion handling for webhook events

The webhook processors for tags and releases currently only handle creation and
update events. They should be updated to also process deletion events to prevent
stale data.

Examples:

integrations/gitlab-v2/gitlab/webhook/webhook_processors/release_webhook_processor.py [21-39]
    async def handle_event(
        self, payload: EventPayload, resource_config: ResourceConfig
    ) -> WebhookEventRawResults:
        project_id = payload["project"]["id"]
        tag_name = payload["tag"]
        logger.info(
            f"Handling release webhook event for project with ID '{project_id} and tag name '{tag_name}'"
        )
        release = await self._gitlab_webhook_client.get_release(
            project_id=project_id,

 ... (clipped 9 lines)
integrations/gitlab-v2/gitlab/webhook/webhook_processors/tag_webhook_processor.py [21-38]
    async def handle_event(
        self, payload: EventPayload, resource_config: ResourceConfig
    ) -> WebhookEventRawResults:
        project_id = payload["project"]["id"]
        tag_name = payload["ref"].split("/")[-1]
        logger.info(
            f"Handling tag webhook event for project with ID '{project_id}' and tag name '{tag_name}'"
        )
        tag = await self._gitlab_webhook_client.get_tag(
            project_id=project_id,

 ... (clipped 8 lines)

Solution Walkthrough:

Before:

# In ReleaseWebhookProcessor.handle_event
async def handle_event(self, payload, ...):
    project_id = payload["project"]["id"]
    tag_name = payload["tag"]
    release = await self._gitlab_webhook_client.get_release(
        project_id=project_id,
        tag_name=tag_name,
    )
    # ... enrichment logic ...
    return WebhookEventRawResults(
        updated_raw_results=[release], 
        deleted_raw_results=[]
    )

After:

# In ReleaseWebhookProcessor.handle_event
async def handle_event(self, payload, ...):
    if payload.get("action") == "delete":
        # Construct a minimal payload for deletion
        deleted_release = {"tag": payload["tag"], "project": payload["project"]}
        return WebhookEventRawResults(
            updated_raw_results=[], 
            deleted_raw_results=[deleted_release]
        )

    project_id = payload["project"]["id"]
    tag_name = payload["tag"]
    release = await self._gitlab_webhook_client.get_release(...)
    # ... enrichment logic ...
    return WebhookEventRawResults(
        updated_raw_results=[release], 
        deleted_raw_results=[]
    )
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a significant functional gap in the new webhook processors, as they fail to handle resource deletions, which would lead to stale data in Port.

Medium
Possible issue
Handle null tag response
Suggestion Impact:The commit added an early return with the populated tag and changed the fallback to return an empty updated_raw_results list when no tag is found, preventing [None] from being returned.

code diff:

         if tag:
             project_path = payload["project"]["path_with_namespace"]
             tag = {**tag, "__project": {"path_with_namespace": project_path}}
+            return WebhookEventRawResults(
+                updated_raw_results=[tag], deleted_raw_results=[]
+            )
 
-        return WebhookEventRawResults(updated_raw_results=[tag], deleted_raw_results=[])
+        return WebhookEventRawResults(updated_raw_results=[], deleted_raw_results=[])

Modify the handle_event method to avoid returning [None] in updated_raw_results
when the get_tag call returns no tag. Instead, return an empty list.

integrations/gitlab-v2/gitlab/webhook/webhook_processors/tag_webhook_processor.py [29-38]

 tag = await self._gitlab_webhook_client.get_tag(
     project_id=project_id,
     tag_name=tag_name,
 )
 
 if tag:
     project_path = payload["project"]["path_with_namespace"]
     tag = {**tag, "__project": {"path_with_namespace": project_path}}
+    return WebhookEventRawResults(updated_raw_results=[tag], deleted_raw_results=[])
 
-return WebhookEventRawResults(updated_raw_results=[tag], deleted_raw_results=[])
+return WebhookEventRawResults(updated_raw_results=[], deleted_raw_results=[])

[Suggestion processed]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that if get_tag returns None, [None] will be passed to updated_raw_results, which can cause downstream errors. The proposed fix prevents this by returning an empty list instead, improving the code's robustness.

Low
Handle null release response
Suggestion Impact:The commit adds a fall-through return that provides empty lists when no release is found and adjusts logic to handle delete actions, thereby preventing [None] from being placed in updated_raw_results as suggested.

code diff:

+            if release["action"] == "delete":
+                return WebhookEventRawResults(
+                    updated_raw_results=[], deleted_raw_results=[release]
+                )
+            else:
+                return WebhookEventRawResults(
+                    updated_raw_results=[release], deleted_raw_results=[]
+                )
+
+        return WebhookEventRawResults(updated_raw_results=[], deleted_raw_results=[])

Modify the handle_event method to avoid returning [None] in updated_raw_results
when the get_release call returns no release. Instead, return an empty list.

integrations/gitlab-v2/gitlab/webhook/webhook_processors/release_webhook_processor.py [29-39]

 release = await self._gitlab_webhook_client.get_release(
     project_id=project_id,
     tag_name=tag_name,
 )
 if release:
     project_path = payload["project"]["path_with_namespace"]
     release = {**release, "__project": {"path_with_namespace": project_path}}
+    return WebhookEventRawResults(
+        updated_raw_results=[release], deleted_raw_results=[]
+    )
 
 return WebhookEventRawResults(
-    updated_raw_results=[release], deleted_raw_results=[]
+    updated_raw_results=[], deleted_raw_results=[]
 )

[Suggestion processed]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that if get_release returns None, [None] will be passed to updated_raw_results, which can cause downstream errors. The proposed fix prevents this by returning an empty list instead, improving the code's robustness.

Low
  • Update

Comment on lines +30 to +34
release = {
**payload,
"__project": {
"path_with_namespace": payload["project"]["path_with_namespace"]
},
Copy link
Member

Choose a reason for hiding this comment

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

Don't repeat yourself, create a function (enrich_resource_with_project) for this call wherever its needed


if tag:
project_path = payload["project"]["path_with_namespace"]
tag = {**tag, "__project": {"path_with_namespace": project_path}}
Copy link
Member

Choose a reason for hiding this comment

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

same here,

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants