Fix duplicate Jira comments from re-processed cross-references
What does this MR do and why?
Fixes the long-standing bug where the Jira integration posts duplicate Jira comments for the same commit and "reopens" resolved Jira issues (resets a remote link's resolved status and spams the issue history) whenever the same commit SHA is re-processed: fast-forward merges, source-branch deletion, rebases, or the same SHA pushed to a second project.
Root cause
GitLab keeps no record of cross-references sent to external trackers (ExternalIssue#notes returns Note.none, so the cross_reference_exists? dedup in Mentionable#create_cross_references! never filters anything). Every re-push re-runs Integrations::CreateExternalCrossReferenceWorker, and deduplication falls entirely on two fragile Jira-side checks in Integrations::Jira:
comment_exists?matched the exact comment body, which embeds volatile context: the branch name resolved from the SHA at processing time (first_ref_by_oid) and the author link. After a fast-forward merge deletes the source branch, the body no longer matches and a duplicate comment is posted.send_messageunconditionally re-saved an existing remote link withresolved: false, which visibly reopens resolved Jira issues and adds a history entry on every re-push.- Both lookups failed open:
jira_requestrescues all errors and returnsnil, which was indistinguishable from "nothing exists", so a transient API error produced a duplicate comment and a duplicate remote link.
The fix
- Dedup mention comments on the stable entity URL (
comment.body.include?("|#{entity_url}]"), matching the Jira wiki-markup link every mention comment contains) instead of the exact body. This also dedups against comments posted by older GitLab versions. - The mention path no longer touches an existing remote link at all (
update_existing_remote_link:kwarg, defaultfalse). Only the close-issue path passestrue, so marking a linkresolved: truestill works. - Lookups now fail closed: a remote-link lookup error aborts the whole send (raises into the surrounding
jira_request), and a failed comment lookup is treated as "comment exists". A transient error can now skip a notification, but never duplicate one.
Verification against a real Jira Cloud instance
Beyond the specs (written first; they reproduced both defects on the unfixed code), the fix was verified empirically against a Jira Cloud dev site:
- End-to-end repro: a commit mentioning a Jira issue pushed on a branch produced 1 comment + 1 remote link; after a fast-forward merge to the default branch plus source branch deletion,
ProcessCommitWorker(default=true) andCreateExternalCrossReferenceWorkerboth re-ran, and no duplicate comment was posted and the remote link was untouched. Pre-fix, this exact sequence produced the duplicate. - Remote-link semantics: Jira Cloud ignores the
GlobalIDkey GitLab sends (the API field is the case-sensitiveglobalId), so remote links are never upserted server-side; they accumulate. Jira also does not dedup remote links by URL, so every fail-open lookup miss permanently added a duplicate link. Both findings shaped the fix (URL-keyed dedup + fail-closed lookups).
Trade-offs (deliberate)
- A remote link's title is no longer refreshed by re-mentions (for example, after an MR title edit). Accepted: it was an accidental side effect of the buggy re-save, and refreshing it is what reopened resolved issues.
- A prior "Issue solved with [sha|url]." comment suppresses a later mention comment for the same commit, which is arguably correct, since Jira was already told about that commit.
- No feature flag: the behavior removed (re-saving existing remote links with
resolved: false, duplicate comments) is the defect itself, and the empirical verification above refuted the one scenario where skipping updates could have masked legitimate behavior. Happy to add one if reviewers prefer for the stable backports.
Known residual limitations (follow-up material)
- Dedup is still check-then-act against remote Jira state: concurrent or retried Sidekiq jobs can race between the lookup and the post. The proper fix is persistent GitLab-side tracking of sent external cross-references (parity with internal-issue dedup).
- The comment container in Jira's issue response can be truncated on very busy issues; the URL-keyed remote-link guard (returned unpaginated by Jira) backstops that case.
References
- Resolves #233159 (closed)
- Investigation context: https://gitlab.com/gitlab-com/dev-sub-department/section-dev-request-for-help/-/issues/179
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist.