Skip to content

feat: allow pass hostname in docker env#157

Open
tomerqodo wants to merge 2 commits intoqodo_claude_vs_qodo_base_feat_allow_pass_hostname_in_docker_env_pr6from
qodo_claude_vs_qodo_head_feat_allow_pass_hostname_in_docker_env_pr6
Open

feat: allow pass hostname in docker env#157
tomerqodo wants to merge 2 commits intoqodo_claude_vs_qodo_base_feat_allow_pass_hostname_in_docker_env_pr6from
qodo_claude_vs_qodo_head_feat_allow_pass_hostname_in_docker_env_pr6

Conversation

@tomerqodo
Copy link

Benchmark PR from agentic-review-benchmarks#6

@qodo-code-review
Copy link

Review Summary by Qodo

Add SMTP local hostname override configuration

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add SMTP_LOCAL_HOSTNAME configuration to override local hostname in SMTP HELO/EHLO
• Refactor SMTP client initialization to use ternary operator for cleaner code
• Update all SMTP tests to accept local_hostname parameter with ANY matcher
• Add configuration to Docker environment files and example configs
Diagram
flowchart LR
  A["MailConfig"] -->|"adds SMTP_LOCAL_HOSTNAME field"| B["Feature Configuration"]
  B -->|"read by"| C["SMTPClient"]
  C -->|"passes to"| D["smtplib.SMTP/SMTP_SSL"]
  D -->|"uses in"| E["HELO/EHLO commands"]
  F["Environment Files"] -->|"define"| B
Loading

Grey Divider

File Changes

1. api/configs/feature/__init__.py ⚙️ Configuration changes +6/-0

Add SMTP local hostname configuration field

• Add new SMTP_LOCAL_HOSTNAME optional configuration field to MailConfig
• Field allows overriding the local hostname used in SMTP HELO/EHLO commands
• Useful for deployments behind NAT or when default hostname causes rejections

api/configs/feature/init.py


2. api/libs/smtp.py ✨ Enhancement +14/-13

Refactor SMTP client to support local hostname override

• Import dify_config to access SMTP_LOCAL_HOSTNAME configuration
• Refactor SMTP connection initialization using ternary operator for TLS mode selection
• Pass local_hostname parameter to both smtplib.SMTP and smtplib.SMTP_SSL constructors
• Update ehlo() calls to use configured local hostname instead of server address

api/libs/smtp.py


3. api/tests/unit_tests/libs/test_smtp_client.py 🧪 Tests +3/-3

Update SMTP client unit tests for local hostname

• Import ANY matcher from unittest.mock for flexible assertion matching
• Update SMTP client instantiation assertions to accept local_hostname parameter
• Use ANY matcher to allow any value for local_hostname in test assertions

api/tests/unit_tests/libs/test_smtp_client.py


View more (5)
4. api/tests/unit_tests/tasks/test_mail_send_task.py 🧪 Tests +4/-4

Update mail send task tests for local hostname parameter

• Import ANY matcher from unittest.mock
• Update three test cases to accept local_hostname parameter in SMTP assertions
• Tests cover TLS/SSL, opportunistic TLS, and plain SMTP modes

api/tests/unit_tests/tasks/test_mail_send_task.py


5. api/.env.example 📝 Documentation +3/-0

Add SMTP local hostname to environment example

• Add SMTP_LOCAL_HOSTNAME configuration variable with comment explaining its purpose
• Document that it's optional and used to override HELO/EHLO hostname

api/.env.example


6. api/tests/integration_tests/.env.example 📝 Documentation +2/-0

Add SMTP local hostname to integration test environment

• Add SMTP_LOCAL_HOSTNAME configuration variable with explanatory comment
• Maintain consistency with main environment example file

api/tests/integration_tests/.env.example


7. docker/.env.example 📝 Documentation +2/-0

Add SMTP local hostname to Docker environment example

• Add SMTP_LOCAL_HOSTNAME configuration variable with comment
• Document optional nature and purpose of the setting

docker/.env.example


8. docker/docker-compose.yaml ⚙️ Configuration changes +1/-0

Add SMTP local hostname to Docker Compose configuration

• Add SMTP_LOCAL_HOSTNAME environment variable to shared API worker configuration
• Use environment variable substitution with empty string as default value

docker/docker-compose.yaml


Grey Divider

Qodo Logo

@qodo-code-review
Copy link

qodo-code-review bot commented Mar 10, 2026

Code Review by Qodo

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

Grey Divider


Action required

1. feature/__init__.py exceeds 800 📘 Rule violation ⛯ Reliability
Description
api/configs/feature/__init__.py is well over the 800-line limit and this PR adds additional lines
to it, further exceeding the maximum. This reduces maintainability and violates the required file
size constraint for backend Python files under api/.
Code

api/configs/feature/init.py[R952-956]

+    SMTP_LOCAL_HOSTNAME: str | None = Field(
+        description="Override the local hostname used in SMTP HELO/EHLO. "
+        "Useful behind NAT or when the default hostname causes rejections.",
+        default=None,
+    )
Evidence
Compliance ID 4 requires all Python files under api/ to be under 800 lines. The file contains code
at/after line 1100 (proving it exceeds 800 lines), and the PR adds new lines at 952-956, increasing
the already-over-limit file size.

AGENTS.md
api/configs/feature/init.py[952-956]
api/configs/feature/init.py[1098-1104]

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

## Issue description
`api/configs/feature/__init__.py` exceeds the 800-line maximum for backend files under `api/`, and this PR adds more content to the file.

## Issue Context
The file already contains content beyond line 1100, which is over the 800-line limit. The new `SMTP_LOCAL_HOSTNAME` field was added into this already oversized module.

## Fix Focus Areas
- api/configs/feature/__init__.py[952-956]
- api/configs/feature/__init__.py[890-980]
- api/configs/feature/__init__.py[1095-1105]

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



Remediation recommended

2. SMTPClient config side effects 🐞 Bug ⛯ Reliability
Description
libs.smtp.SMTPClient now depends on the global dify_config to determine HELO/EHLO hostname, which
forces config initialization when importing the SMTP utility and makes the local hostname
process-global rather than instance-level. This increases coupling and can break callers that want
to use SMTPClient without the full app config stack or need different hostnames per client instance.
Code

api/libs/smtp.py[R24-36]

+        smtp: smtplib.SMTP | None = None
+        local_host = dify_config.SMTP_LOCAL_HOSTNAME or ""
        try:
-            if self.use_tls:
-                if self.opportunistic_tls:
-                    smtp = smtplib.SMTP(self.server, self.port, timeout=10)
-                    # Send EHLO command with the HELO domain name as the server address
-                    smtp.ehlo(self.server)
-                    smtp.starttls()
-                    # Resend EHLO command to identify the TLS session
-                    smtp.ehlo(self.server)
-                else:
-                    smtp = smtplib.SMTP_SSL(self.server, self.port, timeout=10)
-            else:
-                smtp = smtplib.SMTP(self.server, self.port, timeout=10)
+            # Use ternary to select SMTP class based on TLS mode
+            smtp = (smtplib.SMTP_SSL if (self.use_tls and not self.opportunistic_tls) else smtplib.SMTP)(
+                self.server, self.port, timeout=10, local_hostname=local_host or None
+            )
+
+            assert smtp is not None
+            if self.use_tls and self.opportunistic_tls:
+                smtp.ehlo(local_host)
+                smtp.starttls()
+                smtp.ehlo(local_host)
Evidence
SMTPClient reads SMTP_LOCAL_HOSTNAME from the global dify_config during send(), and importing
dify_config constructs DifyConfig at import time. ext_mail already has dify_config in scope when
constructing SMTPClient, so this configuration can be passed at the composition boundary instead of
hard-coding a global dependency inside the library.

api/libs/smtp.py[6-36]
api/configs/init.py[1-3]
api/extensions/ext_mail.py[5-57]

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

### Issue description
`SMTPClient` currently imports and reads global `dify_config` inside `libs/smtp.py`, introducing import-time side effects and preventing per-instance configuration of the HELO/EHLO hostname.

### Issue Context
The mail extension (`extensions/ext_mail.py`) already has access to `dify_config` when it constructs `SMTPClient`, so it can provide the hostname override explicitly without coupling the SMTP utility module to global config.

### Fix Focus Areas
- api/libs/smtp.py[6-36]
- api/extensions/ext_mail.py[42-57]
- api/configs/feature/__init__.py[952-957]

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



Advisory comments

3. Hostname override untested 🐞 Bug ⛯ Reliability
Description
Unit tests were updated to accept any value for the new local_hostname argument, but they do not
assert that SMTP_LOCAL_HOSTNAME is actually propagated into the SMTP handshake behavior. This can
allow future regressions where the env/config value is ignored while tests still pass.
Code

api/tests/unit_tests/libs/test_smtp_client.py[R20-21]

+    mock_smtp_cls.assert_called_once_with("smtp.example.com", 25, timeout=10, local_hostname=ANY)
    mock_smtp.sendmail.assert_called_once()
Evidence
Tests now use ANY for the local_hostname argument rather than validating it equals the configured
override, so they no longer verify the core feature behavior (env-driven override).

api/tests/unit_tests/libs/test_smtp_client.py[12-22]
api/tests/unit_tests/tasks/test_mail_send_task.py[129-219]

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

### Issue description
Current unit tests accept `local_hostname=ANY` and do not verify the new env/config-driven hostname override is actually used.

### Issue Context
The PR’s feature is specifically about propagating `SMTP_LOCAL_HOSTNAME` into the SMTP client behavior; tests should pin this behavior to prevent regressions.

### Fix Focus Areas
- api/tests/unit_tests/libs/test_smtp_client.py[1-100]
- api/libs/smtp.py[23-36]

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


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +952 to +956
SMTP_LOCAL_HOSTNAME: str | None = Field(
description="Override the local hostname used in SMTP HELO/EHLO. "
"Useful behind NAT or when the default hostname causes rejections.",
default=None,
)

Choose a reason for hiding this comment

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

Action required

1. feature/init.py exceeds 800 📘 Rule violation ⛯ Reliability

api/configs/feature/__init__.py is well over the 800-line limit and this PR adds additional lines
to it, further exceeding the maximum. This reduces maintainability and violates the required file
size constraint for backend Python files under api/.
Agent Prompt
## Issue description
`api/configs/feature/__init__.py` exceeds the 800-line maximum for backend files under `api/`, and this PR adds more content to the file.

## Issue Context
The file already contains content beyond line 1100, which is over the 800-line limit. The new `SMTP_LOCAL_HOSTNAME` field was added into this already oversized module.

## Fix Focus Areas
- api/configs/feature/__init__.py[952-956]
- api/configs/feature/__init__.py[890-980]
- api/configs/feature/__init__.py[1095-1105]

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

Comment on lines +24 to +36
smtp: smtplib.SMTP | None = None
local_host = dify_config.SMTP_LOCAL_HOSTNAME or ""
try:
if self.use_tls:
if self.opportunistic_tls:
smtp = smtplib.SMTP(self.server, self.port, timeout=10)
# Send EHLO command with the HELO domain name as the server address
smtp.ehlo(self.server)
smtp.starttls()
# Resend EHLO command to identify the TLS session
smtp.ehlo(self.server)
else:
smtp = smtplib.SMTP_SSL(self.server, self.port, timeout=10)
else:
smtp = smtplib.SMTP(self.server, self.port, timeout=10)
# Use ternary to select SMTP class based on TLS mode
smtp = (smtplib.SMTP_SSL if (self.use_tls and not self.opportunistic_tls) else smtplib.SMTP)(
self.server, self.port, timeout=10, local_hostname=local_host or None
)

assert smtp is not None
if self.use_tls and self.opportunistic_tls:
smtp.ehlo(local_host)
smtp.starttls()
smtp.ehlo(local_host)

Choose a reason for hiding this comment

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

Remediation recommended

2. Smtpclient config side effects 🐞 Bug ⛯ Reliability

libs.smtp.SMTPClient now depends on the global dify_config to determine HELO/EHLO hostname, which
forces config initialization when importing the SMTP utility and makes the local hostname
process-global rather than instance-level. This increases coupling and can break callers that want
to use SMTPClient without the full app config stack or need different hostnames per client instance.
Agent Prompt
### Issue description
`SMTPClient` currently imports and reads global `dify_config` inside `libs/smtp.py`, introducing import-time side effects and preventing per-instance configuration of the HELO/EHLO hostname.

### Issue Context
The mail extension (`extensions/ext_mail.py`) already has access to `dify_config` when it constructs `SMTPClient`, so it can provide the hostname override explicitly without coupling the SMTP utility module to global config.

### Fix Focus Areas
- api/libs/smtp.py[6-36]
- api/extensions/ext_mail.py[42-57]
- api/configs/feature/__init__.py[952-957]

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

Comment on lines +20 to 21
mock_smtp_cls.assert_called_once_with("smtp.example.com", 25, timeout=10, local_hostname=ANY)
mock_smtp.sendmail.assert_called_once()

Choose a reason for hiding this comment

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

Advisory comments

3. Hostname override untested 🐞 Bug ⛯ Reliability

Unit tests were updated to accept any value for the new local_hostname argument, but they do not
assert that SMTP_LOCAL_HOSTNAME is actually propagated into the SMTP handshake behavior. This can
allow future regressions where the env/config value is ignored while tests still pass.
Agent Prompt
### Issue description
Current unit tests accept `local_hostname=ANY` and do not verify the new env/config-driven hostname override is actually used.

### Issue Context
The PR’s feature is specifically about propagating `SMTP_LOCAL_HOSTNAME` into the SMTP client behavior; tests should pin this behavior to prevent regressions.

### Fix Focus Areas
- api/tests/unit_tests/libs/test_smtp_client.py[1-100]
- api/libs/smtp.py[23-36]

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

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants