Skip to content

[Integration][GitHub] Add skipWebhookManagement config#3156

Open
jasonnwakaeze-debug wants to merge 9 commits intomainfrom
PORT-17738-Add-an-env-to-skip-patching-webhooks
Open

[Integration][GitHub] Add skipWebhookManagement config#3156
jasonnwakaeze-debug wants to merge 9 commits intomainfrom
PORT-17738-Add-an-env-to-skip-patching-webhooks

Conversation

@jasonnwakaeze-debug
Copy link
Copy Markdown
Contributor

Description

What -
New boolean config skipWebhookManagement (default false). When enabled, the integration skips all webhook creation and patching on GitHub during startup. This lets customers remove webhookSecret (so the integration skips HMAC verification) without the side effect of the integration stripping the secret from the org webhook.

Why -

How -

Type of change

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

  • 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

Include screenshots from your environment showing how the resources of the integration will look.

API Documentation

Provide links to the API documentation used for this integration.

Copilot AI review requested due to automatic review settings April 27, 2026 23:22
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add skipWebhookManagement config to GitHub integration

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add skipWebhookManagement config to disable automatic webhook creation
• Implement early return in on_start() when flag is enabled
• Add comprehensive test coverage for webhook management skip logic
• Update changelog and integration spec documentation
Diagram
flowchart LR
  A["Integration startup"] --> B{"skipWebhookManagement<br/>enabled?"}
  B -->|Yes| C["Skip webhook creation<br/>and patching"]
  B -->|No| D["Proceed with webhook<br/>management"]
  C --> E["Return early"]
  D --> F["Create/update webhooks"]
Loading

Grey Divider

File Changes

1. integrations/github/main.py ✨ Enhancement +7/-0

Add webhook management skip logic to startup

• Add conditional check for skip_webhook_management config in on_start() function
• Log informational message when webhook management is skipped
• Return early to prevent webhook creation when flag is enabled

integrations/github/main.py


2. integrations/github/tests/github/webhook/test_skip_webhook_management.py 🧪 Tests +45/-0

Add tests for webhook management skip feature

• Create new test file with comprehensive test coverage
• Test that webhook creation is skipped when skip_webhook_management is True
• Test that webhook creation proceeds normally when flag is not set
• Implement helper function to extract registered on_start() function

integrations/github/tests/github/webhook/test_skip_webhook_management.py


3. integrations/github/.port/spec.yaml ⚙️ Configuration changes +5/-0

Add skipWebhookManagement config to spec

• Add new skipWebhookManagement boolean configuration option
• Set default value to false for backward compatibility
• Include description explaining external webhook management use case

integrations/github/.port/spec.yaml


View more (1)
4. integrations/github/CHANGELOG.md 📝 Documentation +3/-3

Update changelog with new feature entry

• Update release date from 2026-04-23 to 2026-04-27
• Change section header from "Improvements" to "Features"
• Replace generic ocean version bump entry with new feature description

integrations/github/CHANGELOG.md


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Apr 27, 2026

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Remediation recommended

1. Brittle on_start test hook🐞 Bug ☼ Reliability
Description
The new test retrieves the startup handler via ocean.app.integration.on_start.call_args, which
only works because ocean.app.integration is an implicit MagicMock in the current fixture; if the
fixture starts using a real integration object or multiple @ocean.on_start() handlers are
registered, the test can fail or call the wrong function.
Code

integrations/github/tests/github/webhook/test_skip_webhook_management.py[R7-13]

+def _get_on_start_fn() -> Any:
+    """Extract the real on_start async function registered via @ocean.on_start().
+
+    The decorator calls ocean.app.integration.on_start(fn) which, in test context,
+    is a MagicMock. The original function is captured in the mock's call history.
+    """
+    return ocean.app.integration.on_start.call_args.args[0]  # type: ignore[attr-defined]
Evidence
_get_on_start_fn() depends on MagicMock internals (call_args) to find the registered function.
In production code, @ocean.on_start() delegates to self.integration.on_start(function) (not a
mock API), and the test fixture doesn’t explicitly define an integration object—so the behavior is
an implementation detail of using MagicMock rather than a stable contract.

integrations/github/tests/github/webhook/test_skip_webhook_management.py[7-13]
port_ocean/context/ocean.py[96-100]
integrations/github/tests/conftest.py[38-60]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`test_skip_webhook_management.py` relies on `ocean.app.integration.on_start.call_args` to recover the function registered by `@ocean.on_start()`. This is brittle because it depends on MagicMock internals and on there being exactly one registration.

### Issue Context
`PortOceanContext.on_start()` returns `self.integration.on_start(function)` (production path), so `call_args` is not a stable interface. The current test fixture happens to make `ocean.app.integration` a MagicMock, which is why the call-history approach works.

### Fix Focus Areas
- integrations/github/tests/github/webhook/test_skip_webhook_management.py[7-13]
- integrations/github/tests/conftest.py[38-60]
- port_ocean/context/ocean.py[96-100]

### Suggested fix
Make the mocked `integration.on_start` behave like a real decorator registration API by returning the passed function (and optionally storing it).

Example approach (fixture-level):
1. In `integrations/github/tests/conftest.py`, explicitly set:
  - `mock_ocean_app.integration = MagicMock()`
  - `mock_ocean_app.integration.on_start = MagicMock(side_effect=lambda fn: fn)`
2. In the test, after importing `main`, call `await main.on_start()` directly (no `call_args` extraction needed).
3. If you need access to the registered function separately, store it in a list/attribute within the `side_effect`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new configuration flag to allow the GitHub integration to skip webhook creation/patching during startup, preventing unintended webhook secret updates when customers opt out of webhook secret verification.

Changes:

  • Added skipWebhookManagement config option to the integration spec (default false).
  • Updated startup flow (on_start) to return early when webhook management is skipped.
  • Added tests covering the new skip behavior and updated changelog entry.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
integrations/github/main.py Adds an early-exit in on_start() when webhook management is disabled via config.
integrations/github/.port/spec.yaml Introduces skipWebhookManagement boolean configuration option.
integrations/github/tests/github/webhook/test_skip_webhook_management.py Adds unit tests to verify webhook creation is skipped/enabled based on the flag.
integrations/github/CHANGELOG.md Documents the new configuration option in release notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +45
self, mock_ocean_context: Any
) -> None:
ocean.integration_config["skip_webhook_management"] = True
ocean.app.config.event_listener.should_process_webhooks = True

mock_create = AsyncMock()
with patch("main._create_webhooks_for_organization", mock_create):
import main # noqa: F401 — triggers @ocean.on_start() registration

await _get_on_start_fn()()

mock_create.assert_not_called()
ocean.integration_config.pop("skip_webhook_management", None)

async def test_on_start_proceeds_when_flag_not_set(
self, mock_ocean_context: Any
) -> None:
ocean.integration_config.pop("skip_webhook_management", None)
ocean.app.config.event_listener.should_process_webhooks = True

mock_create = AsyncMock()
with patch("main._create_webhooks_for_organization", mock_create):
import main # noqa: F401

await _get_on_start_fn()()

mock_create.assert_called_once()
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The test mutates global ocean.integration_config and cleans it up only at the end of the test body. If an assertion fails (or an exception occurs before cleanup), skip_webhook_management can leak into subsequent tests and change their behavior. Use monkeypatch/try...finally (or a fixture) to guarantee cleanup, and consider resetting ocean.app.integration.on_start's mock call history to keep the test isolated.

Suggested change
self, mock_ocean_context: Any
) -> None:
ocean.integration_config["skip_webhook_management"] = True
ocean.app.config.event_listener.should_process_webhooks = True
mock_create = AsyncMock()
with patch("main._create_webhooks_for_organization", mock_create):
import main # noqa: F401 — triggers @ocean.on_start() registration
await _get_on_start_fn()()
mock_create.assert_not_called()
ocean.integration_config.pop("skip_webhook_management", None)
async def test_on_start_proceeds_when_flag_not_set(
self, mock_ocean_context: Any
) -> None:
ocean.integration_config.pop("skip_webhook_management", None)
ocean.app.config.event_listener.should_process_webhooks = True
mock_create = AsyncMock()
with patch("main._create_webhooks_for_organization", mock_create):
import main # noqa: F401
await _get_on_start_fn()()
mock_create.assert_called_once()
self, mock_ocean_context: Any, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setitem(ocean.integration_config, "skip_webhook_management", True)
monkeypatch.setattr(
ocean.app.config.event_listener, "should_process_webhooks", True
)
mock_create = AsyncMock()
try:
with patch("main._create_webhooks_for_organization", mock_create):
import main # noqa: F401 — triggers @ocean.on_start() registration
await _get_on_start_fn()()
mock_create.assert_not_called()
finally:
ocean.app.integration.on_start.reset_mock() # type: ignore[attr-defined]
async def test_on_start_proceeds_when_flag_not_set(
self, mock_ocean_context: Any, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.delitem(
ocean.integration_config, "skip_webhook_management", raising=False
)
monkeypatch.setattr(
ocean.app.config.event_listener, "should_process_webhooks", True
)
mock_create = AsyncMock()
try:
with patch("main._create_webhooks_for_organization", mock_create):
import main # noqa: F401
await _get_on_start_fn()()
mock_create.assert_called_once()
finally:
ocean.app.integration.on_start.reset_mock() # type: ignore[attr-defined]

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +45
ocean.integration_config.pop("skip_webhook_management", None)
ocean.app.config.event_listener.should_process_webhooks = True

mock_create = AsyncMock()
with patch("main._create_webhooks_for_organization", mock_create):
import main # noqa: F401

await _get_on_start_fn()()

mock_create.assert_called_once()
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

This test relies on a previously-registered @ocean.on_start() function via ocean.app.integration.on_start.call_args. Since main is imported only once per test worker, the registration and mock call history can be influenced by earlier tests/imports. To make this deterministic, reset the on_start mock and/or importlib.reload(main) before extracting the registered function.

Copilot uses AI. Check for mistakes.
Comment thread integrations/github/CHANGELOG.md Outdated
### Features

- Bumped ocean version to ^0.41.4
- Added `skipWebhookManagement` configuration option to disable automatic webhook creation and patching.
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

Trailing whitespace at the end of this changelog bullet can cause noisy diffs and may trip markdown/style tooling. Remove the extra space after the period.

Suggested change
- Added `skipWebhookManagement` configuration option to disable automatic webhook creation and patching.
- Added `skipWebhookManagement` configuration option to disable automatic webhook creation and patching.

Copilot uses AI. Check for mistakes.
@github-actions github-actions Bot added size/XS and removed size/M labels Apr 28, 2026
Copy link
Copy Markdown
Member

@mk-armah mk-armah left a comment

Choose a reason for hiding this comment

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

The goal is to skip webhook patching ...

@github-actions github-actions Bot added size/S and removed size/XS labels Apr 29, 2026
Copy link
Copy Markdown
Member

@mk-armah mk-armah left a comment

Choose a reason for hiding this comment

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

Left comments

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.

  • Its overwritting existing
  • Its an improvement not a feature

Comment thread integrations/github/main.py Outdated
Comment thread integrations/github/main.py Outdated
Co-authored-by: Michael Kofi Armah <mikeyarmah@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants