Match semantic IDs (e.g. KS-10) without requiring OP# prefix

Bare numeric IDs like #10 still require OP# to avoid collision with
Gitea issue numbers. Semantic identifiers (PROJ-42 style) are
unambiguous and matched with or without the prefix, as well as via
full OpenProject URL.

Update the tab empty-state message accordingly.
This commit is contained in:
quafzi committed 2026-08-29 16:16:49 +02:00
1 parent 29b922b708
commit f704afec51
2 files changed
+16 -6

No files matched your search

+1 -1
View File
@@ -46,7 +46,7 @@ en:
title: Quick snippets for Git title: Quick snippets for Git
title: "Pull requests" title: "Pull requests"
tab_prs: tab_prs:
empty: There are no pull requests linked yet. Link an existing PR by using the code <code>OP#%{wp_id}</code> in the PR title/description or create a new PR. empty: There are no pull requests linked yet. Reference this work package in the PR title or description using its ID (e.g. <code>%{wp_id}</code>) or a full OpenProject URL.
updated_on: Updated on updated_on: Updated on
work_packages: work_packages:
tab_name: "Gitea" tab_name: "Gitea"
@@ -30,14 +30,24 @@ module OpenProject::GiteaIntegration
module NotificationHandler module NotificationHandler
module Helper module Helper
def extract_work_package_ids(text) def extract_work_package_ids(text)
host_name = Regexp.escape(Setting.host_name) host_name = Regexp.escape(Setting.host_name)
wp_id = WorkPackage::SemanticIdentifier::ID_ROUTE_CONSTRAINT numeric_id = /\d+/
url_part = /http(?:s?):\/\/#{host_name}\/(?:\S+?\/)*(?:work_packages|wp)\/(#{wp_id})/ semantic_id = WorkPackage::SemanticIdentifier::SEMANTIC_ID_PATTERN
any_id = WorkPackage::SemanticIdentifier::ID_ROUTE_CONSTRAINT
wp_regex = /OP#(#{wp_id})|#{url_part}/ # Full OpenProject URL — matches both numeric and semantic IDs
url_part = /http(?:s?):\/\/#{host_name}\/(?:\S+?\/)*(?:work_packages|wp)\/(#{any_id})/
# OP# prefix — required for bare numeric IDs to avoid collision with
# Gitea issue numbers (e.g. "Fix #10" refers to a Gitea issue, not an OP WP)
op_prefix_numeric = /OP#(#{numeric_id})/
# Semantic identifiers (e.g. KS-10, PROJ-42) are unambiguous enough to
# match without any prefix, both with and without OP#
op_prefix_semantic = /(?:OP#)?(#{semantic_id})/
String(text) String(text)
.scan(wp_regex) .scan(/#{url_part}|#{op_prefix_numeric}|#{op_prefix_semantic}/)
.filter_map { |groups| groups.compact.first } .filter_map { |groups| groups.compact.first }
.uniq .uniq
end end