From 9eb17b36c14b00aa2b250a836782f35438308f18 Mon Sep 17 00:00:00 2001 From: Evans Castonguay Date: Fri, 9 Jan 2026 23:09:33 -0500 Subject: [PATCH 1/4] fix: allow PORT override for GitLab webhook --- pr_agent/servers/gitlab_webhook.py | 4 +++- tests/unittest/test_gitlab_webhook_port.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/unittest/test_gitlab_webhook_port.py diff --git a/pr_agent/servers/gitlab_webhook.py b/pr_agent/servers/gitlab_webhook.py index 868756d74a..38cd02311e 100644 --- a/pr_agent/servers/gitlab_webhook.py +++ b/pr_agent/servers/gitlab_webhook.py @@ -1,5 +1,6 @@ import copy import json +import os import re from datetime import datetime @@ -309,7 +310,8 @@ async def root(): def start(): - uvicorn.run(app, host="0.0.0.0", port=3000) + port = int(os.environ.get("PORT", "3000")) + uvicorn.run(app, host="0.0.0.0", port=port) if __name__ == '__main__': diff --git a/tests/unittest/test_gitlab_webhook_port.py b/tests/unittest/test_gitlab_webhook_port.py new file mode 100644 index 0000000000..97fe6bc90c --- /dev/null +++ b/tests/unittest/test_gitlab_webhook_port.py @@ -0,0 +1,22 @@ +import importlib +import os +from unittest import mock + + +def _load_module(): + import pr_agent.servers.gitlab_webhook as gitlab_webhook + + return importlib.reload(gitlab_webhook) + + +def test_start_uses_port_env(monkeypatch): + monkeypatch.setenv("GITLAB__URL", "https://gitlab.example.com") + monkeypatch.setenv("PORT", "4567") + module = _load_module() + + with mock.patch.object(module.uvicorn, "run") as mock_run: + module.start() + + args, kwargs = mock_run.call_args + assert kwargs["port"] == 4567 + assert kwargs["host"] == "0.0.0.0" From a06120ade62656455455e07283b8adaf729ac15b Mon Sep 17 00:00:00 2001 From: Evans Castonguay Date: Fri, 9 Jan 2026 23:23:43 -0500 Subject: [PATCH 2/4] fix: guard invalid PORT env in GitLab webhook --- pr_agent/servers/gitlab_webhook.py | 6 +++++- tests/unittest/test_gitlab_webhook_port.py | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/pr_agent/servers/gitlab_webhook.py b/pr_agent/servers/gitlab_webhook.py index 38cd02311e..8e803e7117 100644 --- a/pr_agent/servers/gitlab_webhook.py +++ b/pr_agent/servers/gitlab_webhook.py @@ -310,7 +310,11 @@ async def root(): def start(): - port = int(os.environ.get("PORT", "3000")) + try: + port = int(os.environ.get("PORT", "3000")) + except ValueError: + get_logger().warning("Invalid PORT environment variable, using default port 3000") + port = 3000 uvicorn.run(app, host="0.0.0.0", port=port) diff --git a/tests/unittest/test_gitlab_webhook_port.py b/tests/unittest/test_gitlab_webhook_port.py index 97fe6bc90c..fc55d4ecc2 100644 --- a/tests/unittest/test_gitlab_webhook_port.py +++ b/tests/unittest/test_gitlab_webhook_port.py @@ -20,3 +20,15 @@ def test_start_uses_port_env(monkeypatch): args, kwargs = mock_run.call_args assert kwargs["port"] == 4567 assert kwargs["host"] == "0.0.0.0" + + +def test_start_invalid_port_env(monkeypatch): + monkeypatch.setenv("GITLAB__URL", "https://gitlab.example.com") + monkeypatch.setenv("PORT", "not-a-number") + module = _load_module() + + with mock.patch.object(module.uvicorn, "run") as mock_run: + module.start() + + _, kwargs = mock_run.call_args + assert kwargs["port"] == 3000 From 2a4d939b830b7e437838c267b62aa0bb09c81517 Mon Sep 17 00:00:00 2001 From: Evans Castonguay Date: Fri, 9 Jan 2026 23:28:37 -0500 Subject: [PATCH 3/4] chore: validate PORT env for GitLab webhook --- docs/docs/installation/gitlab.md | 3 +- pr_agent/servers/gitlab_webhook.py | 17 ++++++-- tests/unittest/test_gitlab_webhook_port.py | 45 +++++++++++++++------- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/docs/docs/installation/gitlab.md b/docs/docs/installation/gitlab.md index 33ac595df1..d411abf375 100644 --- a/docs/docs/installation/gitlab.md +++ b/docs/docs/installation/gitlab.md @@ -88,6 +88,7 @@ GITLAB__SHARED_SECRET= GITLAB__URL=https://gitlab.com GITLAB__AUTH_TYPE=oauth_token # Use "private_token" for older GitLab versions OPENAI__KEY= +PORT=3000 # Optional: override the webhook server port ``` 8. Create a webhook in your GitLab project. Set the URL to `http[s]:///webhook`, the secret token to the generated secret from step 3, and enable the triggers `push`, `comments` and `merge request events`. @@ -152,4 +153,4 @@ AWS_SECRETS_MANAGER__SECRET_ARN=arn:aws:secretsmanager:us-east-1:123456789012:se **Important**: When using Secrets Manager, GitLab's webhook secret must be the Secrets Manager secret name. -5. Add IAM permission `secretsmanager:GetSecretValue` to your Lambda execution role \ No newline at end of file +5. Add IAM permission `secretsmanager:GetSecretValue` to your Lambda execution role diff --git a/pr_agent/servers/gitlab_webhook.py b/pr_agent/servers/gitlab_webhook.py index 8e803e7117..6647a41b11 100644 --- a/pr_agent/servers/gitlab_webhook.py +++ b/pr_agent/servers/gitlab_webhook.py @@ -310,10 +310,21 @@ async def root(): def start(): + """ + Start the GitLab webhook server. + + The server port can be configured via the PORT environment variable. + Defaults to 3000 if PORT is not set or invalid. + """ + raw_port = os.environ.get("PORT") try: - port = int(os.environ.get("PORT", "3000")) - except ValueError: - get_logger().warning("Invalid PORT environment variable, using default port 3000") + port = int(raw_port) if raw_port else 3000 + if not (1 <= port <= 65535): + raise ValueError(f"Port {port} is out of valid range") + if raw_port: + get_logger().info(f"Using custom PORT from environment: {port}") + except ValueError as e: + get_logger().warning(f"Invalid PORT environment variable ({e}), using default port 3000") port = 3000 uvicorn.run(app, host="0.0.0.0", port=port) diff --git a/tests/unittest/test_gitlab_webhook_port.py b/tests/unittest/test_gitlab_webhook_port.py index fc55d4ecc2..d2d312cd83 100644 --- a/tests/unittest/test_gitlab_webhook_port.py +++ b/tests/unittest/test_gitlab_webhook_port.py @@ -1,34 +1,53 @@ import importlib -import os from unittest import mock +import pytest -def _load_module(): + +@pytest.fixture() +def gitlab_webhook_module(monkeypatch): + monkeypatch.setenv("GITLAB__URL", "https://gitlab.example.com") import pr_agent.servers.gitlab_webhook as gitlab_webhook return importlib.reload(gitlab_webhook) -def test_start_uses_port_env(monkeypatch): - monkeypatch.setenv("GITLAB__URL", "https://gitlab.example.com") +def test_start_uses_port_env(monkeypatch, gitlab_webhook_module): monkeypatch.setenv("PORT", "4567") - module = _load_module() - with mock.patch.object(module.uvicorn, "run") as mock_run: - module.start() + with mock.patch.object(gitlab_webhook_module.uvicorn, "run") as mock_run: + gitlab_webhook_module.start() - args, kwargs = mock_run.call_args + _, kwargs = mock_run.call_args assert kwargs["port"] == 4567 assert kwargs["host"] == "0.0.0.0" -def test_start_invalid_port_env(monkeypatch): - monkeypatch.setenv("GITLAB__URL", "https://gitlab.example.com") +def test_start_invalid_port_env(monkeypatch, gitlab_webhook_module): monkeypatch.setenv("PORT", "not-a-number") - module = _load_module() - with mock.patch.object(module.uvicorn, "run") as mock_run: - module.start() + with mock.patch.object(gitlab_webhook_module.uvicorn, "run") as mock_run: + gitlab_webhook_module.start() + + _, kwargs = mock_run.call_args + assert kwargs["port"] == 3000 + + +def test_start_default_port(monkeypatch, gitlab_webhook_module): + monkeypatch.delenv("PORT", raising=False) + + with mock.patch.object(gitlab_webhook_module.uvicorn, "run") as mock_run: + gitlab_webhook_module.start() + + _, kwargs = mock_run.call_args + assert kwargs["port"] == 3000 + + +def test_start_invalid_port_range(monkeypatch, gitlab_webhook_module): + monkeypatch.setenv("PORT", "70000") + + with mock.patch.object(gitlab_webhook_module.uvicorn, "run") as mock_run: + gitlab_webhook_module.start() _, kwargs = mock_run.call_args assert kwargs["port"] == 3000 From 1b2268a41dcca8d5377a0d5cf6ff778c50f19a16 Mon Sep 17 00:00:00 2001 From: Evans Castonguay Date: Fri, 9 Jan 2026 23:30:05 -0500 Subject: [PATCH 4/4] test: avoid reload in GitLab webhook port tests --- tests/unittest/test_gitlab_webhook_port.py | 37 +++++++++------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/tests/unittest/test_gitlab_webhook_port.py b/tests/unittest/test_gitlab_webhook_port.py index d2d312cd83..b85daaf699 100644 --- a/tests/unittest/test_gitlab_webhook_port.py +++ b/tests/unittest/test_gitlab_webhook_port.py @@ -1,53 +1,46 @@ -import importlib +import os from unittest import mock -import pytest +os.environ.setdefault("GITLAB__URL", "https://gitlab.example.com") +import pr_agent.servers.gitlab_webhook as gitlab_webhook -@pytest.fixture() -def gitlab_webhook_module(monkeypatch): - monkeypatch.setenv("GITLAB__URL", "https://gitlab.example.com") - import pr_agent.servers.gitlab_webhook as gitlab_webhook - - return importlib.reload(gitlab_webhook) - - -def test_start_uses_port_env(monkeypatch, gitlab_webhook_module): +def test_start_uses_port_env(monkeypatch): monkeypatch.setenv("PORT", "4567") - with mock.patch.object(gitlab_webhook_module.uvicorn, "run") as mock_run: - gitlab_webhook_module.start() + with mock.patch.object(gitlab_webhook.uvicorn, "run") as mock_run: + gitlab_webhook.start() _, kwargs = mock_run.call_args assert kwargs["port"] == 4567 assert kwargs["host"] == "0.0.0.0" -def test_start_invalid_port_env(monkeypatch, gitlab_webhook_module): +def test_start_invalid_port_env(monkeypatch): monkeypatch.setenv("PORT", "not-a-number") - with mock.patch.object(gitlab_webhook_module.uvicorn, "run") as mock_run: - gitlab_webhook_module.start() + with mock.patch.object(gitlab_webhook.uvicorn, "run") as mock_run: + gitlab_webhook.start() _, kwargs = mock_run.call_args assert kwargs["port"] == 3000 -def test_start_default_port(monkeypatch, gitlab_webhook_module): +def test_start_default_port(monkeypatch): monkeypatch.delenv("PORT", raising=False) - with mock.patch.object(gitlab_webhook_module.uvicorn, "run") as mock_run: - gitlab_webhook_module.start() + with mock.patch.object(gitlab_webhook.uvicorn, "run") as mock_run: + gitlab_webhook.start() _, kwargs = mock_run.call_args assert kwargs["port"] == 3000 -def test_start_invalid_port_range(monkeypatch, gitlab_webhook_module): +def test_start_invalid_port_range(monkeypatch): monkeypatch.setenv("PORT", "70000") - with mock.patch.object(gitlab_webhook_module.uvicorn, "run") as mock_run: - gitlab_webhook_module.start() + with mock.patch.object(gitlab_webhook.uvicorn, "run") as mock_run: + gitlab_webhook.start() _, kwargs = mock_run.call_args assert kwargs["port"] == 3000