refactor(publish): extract GitLab MR write-back into dedicated helper module#1026
Conversation
Greptile SummaryThis PR extracts the GitLab MR write-back logic from Confidence Score: 5/5Safe to merge — pure refactoring with no functional changes and good test coverage. All findings are P2 (style/naming/test-quality suggestions). The extraction is mechanically correct, the env-flag guard and failure-suppression semantics are unchanged, and the new tests exercise the important paths. tests/nodes/publish_findings/test_gitlab_writeback.py — the failure-propagation test has no assertions. Important Files Changed
Sequence DiagramsequenceDiagram
participant N as node.py::generate_report
participant GW as gitlab_writeback.py::post_gitlab_mr_writeback
participant ENV as os.getenv
participant GL as app.integrations.gitlab
N->>GW: post_gitlab_mr_writeback(state, slack_message)
GW->>ENV: getenv("GITLAB_MR_WRITEBACK")
alt flag not truthy
GW-->>N: return (no-op)
else flag truthy
GW->>GW: extract merge_request_iid, project_id from state
alt missing iid or project_id
GW-->>N: return (no-op)
else both present
GW->>GL: build_gitlab_config(...)
GW->>GL: post_gitlab_mr_note(config, project_id, mr_iid, body)
alt success
GL-->>GW: note dict
GW->>GW: logger.info(...)
else exception
GW->>GW: logger.warning(...)
end
GW-->>N: return
end
end
Reviews (1): Last reviewed commit: "refactor(publish): extract GitLab MR wri..." | Re-trigger Greptile |
| import logging | ||
| import os | ||
|
|
||
| from app.integrations.gitlab import build_gitlab_config, post_gitlab_mr_note |
There was a problem hiding this comment.
Eager top-level import replaces formerly lazy in-try import
In the original node.py, the build_gitlab_config / post_gitlab_mr_note imports lived inside the try block and would therefore be caught by the except Exception handler if the module or its transitive dependencies failed to load. Now they are a top-level import of gitlab_writeback.py, which in turn is imported at the top of node.py. Any import-time failure in app.integrations.gitlab will now surface as an unhandled ImportError at process startup rather than a logged warning at call-time.
In practice app.integrations.gitlab only uses httpx and pydantic (both core deps), so this is unlikely to bite today, but it's a subtle erosion of the defensive isolation the original pattern provided.
| return f"### RCA Finding\n\n<details>\n<summary>Investigation summary</summary>\n\n{body}\n\n</details>" | ||
|
|
||
|
|
||
| def post_gitlab_mr_writeback(state: InvestigationState, slack_message: str) -> None: |
There was a problem hiding this comment.
slack_message parameter name is misleading inside a GitLab module
The parameter carries the Slack-formatted report string and is named slack_message throughout the call chain. Naming it report or report_body in this module would make the public API of the helper self-explanatory without requiring callers to know that the GitLab note body originates from the Slack formatter.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| def test_failure_does_not_propagate(state_with_gitlab): | ||
| with ( | ||
| patch("app.nodes.publish_findings.gitlab_writeback.os.getenv", return_value="true"), | ||
| patch( | ||
| "app.nodes.publish_findings.gitlab_writeback.post_gitlab_mr_note", | ||
| side_effect=RuntimeError("network error"), | ||
| ), | ||
| patch( | ||
| "app.nodes.publish_findings.gitlab_writeback.build_gitlab_config", | ||
| return_value=MagicMock(), | ||
| ), | ||
| ): | ||
| post_gitlab_mr_writeback(state_with_gitlab, "report") |
There was a problem hiding this comment.
Failure test has no assertions
test_failure_does_not_propagate verifies only that no exception escapes — it doesn't check that the warning is actually emitted. A future refactor could accidentally silence the log (e.g. change logger.warning → pass) and the test would still pass. Consider adding:
with patch("app.nodes.publish_findings.gitlab_writeback.logger") as mock_logger:
post_gitlab_mr_writeback(state_with_gitlab, "report")
mock_logger.warning.assert_called_once()| _gl = (state.get("available_sources") or {}).get("gitlab", {}) | ||
| _mr_iid = _gl.get("merge_request_iid", "") | ||
| _project_id = _gl.get("project_id", "") | ||
|
|
There was a problem hiding this comment.
Underscore prefix on local variables (_gl, _mr_iid, _project_id) is noise — that convention is for module-level private names, not locals. Drop the underscores.
| assert "x" * 3997 + "..." in note | ||
| assert "x" * 3998 not in note | ||
|
|
||
|
|
There was a problem hiding this comment.
Patching os.getenv globally replaces every getenv call in the module for the duration of the test. Use patch.dict(os.environ, {"GITLAB_MR_WRITEBACK": "false"}) instead — it only touches the one key and is safer if more env-var reads get added later.
| state = {"available_sources": {"gitlab": {"project_id": "99"}}} | ||
| with ( | ||
| patch("app.nodes.publish_findings.gitlab_writeback.os.getenv", return_value="true"), | ||
| patch("app.nodes.publish_findings.gitlab_writeback.post_gitlab_mr_note") as mock_post, |
There was a problem hiding this comment.
Same os.getenv patching concern as the first test. Applies to all five test functions — swap all of them to patch.dict for consistency.
…atch.dict for env, assert logger.warning on failure
|
@muddlebee all review comments addressed — dropped underscore locals, renamed param to report, switched all env patches to patch.dict, and added the logger.warning assertion to the failure test. Happy to make any further adjustments if needed. |
|
@udit-rawat thank you |
Problem
app/nodes/publish_findings/node.pywas doing too many things in one file — rendering, ingest, Slack, Discord, Telegram, and GitLab MR write-back all inline. The GitLab branch was 20+ lines including a_build_mr_note()helper, making the node hard to test and maintain.Solution
Extracted
_build_mr_note()and the entire GitLab write-back branch intoapp/nodes/publish_findings/gitlab_writeback.pybehind a singlepost_gitlab_mr_writeback()function. The publish node now calls that one function instead of inlining the logic.GITLAB_MR_WRITEBACKenv-flag behavior and failure handling are unchanged.Tests
Added
tests/nodes/publish_findings/test_gitlab_writeback.pycovering:post_gitlab_mr_notecalled with correct argsCloses #869