##// END OF EJS Templates
issues: support generic regex replacements in issue_url and issue_prefix...
Thomas De Schampheleire -
r7162:d24051ce default
parent child Browse files
Show More
@@ -168,34 +168,44 b' default_encoding = utf8'
168 ## issue tracker for Kallithea (leave blank to disable, absent for default)
168 ## issue tracker for Kallithea (leave blank to disable, absent for default)
169 #bugtracker = https://bitbucket.org/conservancy/kallithea/issues
169 #bugtracker = https://bitbucket.org/conservancy/kallithea/issues
170
170
171 ## issue tracking mapping for commits messages
171 ## issue tracking mapping for commit messages, comments, PR descriptions, ...
172 ## comment out issue_pat, issue_server, issue_prefix to enable
172 ## Refer to the documentation ("Integration with issue trackers") for more details.
173
173
174 ## pattern to get the issues from commit messages
174 ## regular expression to match issue references
175 ## default one used here is #<numbers> with a regex passive group for `#`
175 ## This pattern may/should contain parenthesized groups, that can
176 ## {id} will be all groups matched from this pattern
176 ## be referred to in issue_server_link or issue_sub using Python backreferences
177 ## (e.g. \1, \2, ...). You can also create named groups with '(?P<groupname>)'.
178 ## To require mandatory whitespace before the issue pattern, use:
179 ## (?:^|(?<=\s)) before the actual pattern, and for mandatory whitespace
180 ## behind the issue pattern, use (?:$|(?=\s)) after the actual pattern.
177
181
178 issue_pat = #(\d+)
182 issue_pat = #(\d+)
179
183
180 ## server url to the issue, each {id} will be replaced with match
184 ## server url to the issue
181 ## fetched from the regex and {repo} is replaced with full repository name
185 ## This pattern may/should contain backreferences to parenthesized groups in issue_pat.
182 ## including groups {repo_name} is replaced with just name of repo
186 ## A backreference can be \1, \2, ... or \g<groupname> if you specified a named group
183
187 ## called 'groupname' in issue_pat.
184 issue_server_link = https://issues.example.com/{repo}/issue/{id}
188 ## The special token {repo} is replaced with the full repository name
189 ## including repository groups, while {repo_name} is replaced with just
190 ## the name of the repository.
185
191
186 ## prefix to add to link to indicate it's an url
192 issue_server_link = https://issues.example.com/{repo}/issue/\1
187 ## #314 will be replaced by <issue_prefix><id>
188
193
189 issue_prefix = #
194 ## substitution pattern to use as the link text
195 ## If issue_sub is empty, the text matched by issue_pat is retained verbatim
196 ## for the link text. Otherwise, the link text is that of issue_sub, with any
197 ## backreferences to groups in issue_pat replaced.
190
198
191 ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify
199 issue_sub =
200
201 ## issue_pat, issue_server_link and issue_sub can have suffixes to specify
192 ## multiple patterns, to other issues server, wiki or others
202 ## multiple patterns, to other issues server, wiki or others
193 ## below an example how to create a wiki pattern
203 ## below an example how to create a wiki pattern
194 # wiki-some-id -> https://wiki.example.com/some-id
204 # wiki-some-id -> https://wiki.example.com/some-id
195
205
196 #issue_pat_wiki = (?:wiki-)(.+)
206 #issue_pat_wiki = wiki-(\S+)
197 #issue_server_link_wiki = https://wiki.example.com/{id}
207 #issue_server_link_wiki = https://wiki.example.com/\1
198 #issue_prefix_wiki = WIKI-
208 #issue_sub_wiki = WIKI-\1
199
209
200 ## alternative return HTTP header for failed authentication. Default HTTP
210 ## alternative return HTTP header for failed authentication. Default HTTP
201 ## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with
211 ## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with
@@ -535,36 +535,69 b' Integration with issue trackers'
535
535
536 Kallithea provides a simple integration with issue trackers. It's possible
536 Kallithea provides a simple integration with issue trackers. It's possible
537 to define a regular expression that will match an issue ID in commit messages,
537 to define a regular expression that will match an issue ID in commit messages,
538 and have that replaced with a URL to the issue. To enable this simply
538 and have that replaced with a URL to the issue.
539 uncomment the following variables in the ini file::
539
540 This is achieved with following three variables in the ini file::
540
541
541 issue_pat = (?:^#|\s#)(\w+)
542 issue_pat = #(\d+)
542 issue_server_link = https://issues.example.com/{repo}/issue/{id}
543 issue_server_link = https://issues.example.com/{repo}/issue/\1
543 issue_prefix = #
544 issue_sub =
544
545
545 ``issue_pat`` is the regular expression describing which strings in
546 ``issue_pat`` is the regular expression describing which strings in
546 commit messages will be treated as issue references. A match group in
547 commit messages will be treated as issue references. The expression can/should
547 parentheses should be used to specify the actual issue id.
548 have one or more parenthesized groups that can later be referred to in
549 ``issue_server_link`` and ``issue_sub`` (see below). If you prefer, named groups
550 can be used instead of simple parenthesized groups.
548
551
549 The default expression matches issues in the format ``#<number>``, e.g., ``#300``.
552 If the pattern should only match if it is preceded by whitespace, add the
553 following string before the actual pattern: ``(?:^|(?<=\s))``.
554 If the pattern should only match if it is followed by whitespace, add the
555 following string after the actual pattern: ``(?:$|(?=\s))``.
556 These expressions use lookbehind and lookahead assertions of the Python regular
557 expression module to avoid the whitespace to be part of the actual pattern,
558 otherwise the link text will also contain that whitespace.
550
559
551 Matched issue references are replaced with the link specified in
560 Matched issue references are replaced with the link specified in
552 ``issue_server_link``. ``{id}`` is replaced with the issue ID, and
561 ``issue_server_link``, in which any backreferences are resolved. Backreferences
553 ``{repo}`` with the repository name. Since the # is stripped away,
562 can be ``\1``, ``\2``, ... or for named groups ``\g<groupname>``.
554 ``issue_prefix`` is prepended to the link text. ``issue_prefix`` doesn't
563 The special token ``{repo}`` is replaced with the full repository path
555 necessarily need to be ``#``: if you set issue prefix to ``ISSUE-`` this will
564 (including repository groups), while token ``{repo_name}`` is replaced with the
556 generate a URL in the format:
565 repository name (without repository groups).
566
567 The link text is determined by ``issue_sub``, which can be a string containing
568 backreferences to the groups specified in ``issue_pat``. If ``issue_sub`` is
569 empty, then the text matched by ``issue_pat`` is used verbatim.
570
571 The example settings shown above match issues in the format ``#<number>``.
572 This will cause the text ``#300`` to be transformed into a link:
557
573
558 .. code-block:: html
574 .. code-block:: html
559
575
560 <a href="https://issues.example.com/example_repo/issue/300">ISSUE-300</a>
576 <a href="https://issues.example.com/example_repo/issue/300">#300</a>
577
578 The following example transforms a text starting with either of 'pullrequest',
579 'pull request' or 'PR', followed by an optional space, then a pound character
580 (#) and one or more digits, into a link with the text 'PR #' followed by the
581 digits::
582
583 issue_pat = (pullrequest|pull request|PR) ?#(\d+)
584 issue_server_link = https://issues.example.com/\2
585 issue_sub = PR #\2
586
587 The following example demonstrates how to require whitespace before the issue
588 reference in order for it to be recognized, such that the text ``issue#123`` will
589 not cause a match, but ``issue #123`` will::
590
591 issue_pat = (?:^|(?<=\s))#(\d+)
592 issue_server_link = https://issues.example.com/\1
593 issue_sub =
561
594
562 If needed, more than one pattern can be specified by appending a unique suffix to
595 If needed, more than one pattern can be specified by appending a unique suffix to
563 the variables. For example::
596 the variables. For example, also demonstrating the use of named groups::
564
597
565 issue_pat_wiki = (?:wiki-)(.+)
598 issue_pat_wiki = wiki-(?P<pagename>\S+)
566 issue_server_link_wiki = https://wiki.example.com/{id}
599 issue_server_link_wiki = https://wiki.example.com/\g<pagename>
567 issue_prefix_wiki = WIKI-
600 issue_sub_wiki = WIKI-\g<pagename>
568
601
569 With these settings, wiki pages can be referenced as wiki-some-id, and every
602 With these settings, wiki pages can be referenced as wiki-some-id, and every
570 such reference will be transformed into:
603 such reference will be transformed into:
@@ -573,6 +606,9 b' such reference will be transformed into:'
573
606
574 <a href="https://wiki.example.com/some-id">WIKI-some-id</a>
607 <a href="https://wiki.example.com/some-id">WIKI-some-id</a>
575
608
609 Refer to the `Python regular expression documentation`_ for more details about
610 the supported syntax in ``issue_pat``, ``issue_server_link`` and ``issue_sub``.
611
576
612
577 Hook management
613 Hook management
578 ---------------
614 ---------------
@@ -901,6 +937,7 b' the ``init.d`` directory of the Kallithe'
901
937
902 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
938 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
903 .. _python: http://www.python.org/
939 .. _python: http://www.python.org/
940 .. _Python regular expression documentation: https://docs.python.org/2/library/re.html
904 .. _Mercurial: https://www.mercurial-scm.org/
941 .. _Mercurial: https://www.mercurial-scm.org/
905 .. _Celery: http://celeryproject.org/
942 .. _Celery: http://celeryproject.org/
906 .. _Celery documentation: http://docs.celeryproject.org/en/latest/getting-started/index.html
943 .. _Celery documentation: http://docs.celeryproject.org/en/latest/getting-started/index.html
@@ -1140,35 +1140,46 b' def urlify_issues(newtext, repo_name):'
1140 suffix = m.group(1)
1140 suffix = m.group(1)
1141 issue_pat = CONFIG.get(k)
1141 issue_pat = CONFIG.get(k)
1142 issue_server_link = CONFIG.get('issue_server_link%s' % suffix)
1142 issue_server_link = CONFIG.get('issue_server_link%s' % suffix)
1143 issue_prefix = CONFIG.get('issue_prefix%s' % suffix)
1143 issue_sub = CONFIG.get('issue_sub%s' % suffix)
1144 if not issue_pat or not issue_server_link or issue_prefix is None: # issue_prefix can be empty but should be present
1144 if not issue_pat or not issue_server_link or issue_sub is None: # issue_sub can be empty but should be present
1145 log.error('skipping incomplete issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_prefix)
1145 log.error('skipping incomplete issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_sub)
1146 continue
1146 continue
1147
1147
1148 # Wrap tmp_urlify_issues_f with substitution of this pattern, while making sure all loop variables (and compiled regexpes) are bound
1148 # Wrap tmp_urlify_issues_f with substitution of this pattern, while making sure all loop variables (and compiled regexpes) are bound
1149 try:
1149 try:
1150 issue_re = re.compile(issue_pat)
1150 issue_re = re.compile(issue_pat)
1151 except re.error as e:
1151 except re.error as e:
1152 log.error('skipping invalid issue pattern %r: %r -> %r %r. Error: %s', suffix, issue_pat, issue_server_link, issue_prefix, str(e))
1152 log.error('skipping invalid issue pattern %r: %r -> %r %r. Error: %s', suffix, issue_pat, issue_server_link, issue_sub, str(e))
1153 continue
1153 continue
1154
1154
1155 log.debug('issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_prefix)
1155 log.debug('issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_sub)
1156
1156
1157 def issues_replace(match_obj,
1157 def issues_replace(match_obj,
1158 issue_server_link=issue_server_link, issue_prefix=issue_prefix):
1158 issue_server_link=issue_server_link, issue_sub=issue_sub):
1159 issue_id = ''.join(match_obj.groups())
1159 try:
1160 issue_url = issue_server_link.replace('{id}', issue_id)
1160 issue_url = match_obj.expand(issue_server_link)
1161 except (IndexError, re.error) as e:
1162 log.error('invalid issue_url setting %r -> %r %r. Error: %s', issue_pat, issue_server_link, issue_sub, str(e))
1163 issue_url = issue_server_link
1161 issue_url = issue_url.replace('{repo}', repo_name)
1164 issue_url = issue_url.replace('{repo}', repo_name)
1162 issue_url = issue_url.replace('{repo_name}', repo_name.split(URL_SEP)[-1])
1165 issue_url = issue_url.replace('{repo_name}', repo_name.split(URL_SEP)[-1])
1166 # if issue_sub is empty use the matched issue reference verbatim
1167 if not issue_sub:
1168 issue_text = match_obj.group()
1169 else:
1170 try:
1171 issue_text = match_obj.expand(issue_sub)
1172 except (IndexError, re.error) as e:
1173 log.error('invalid issue_sub setting %r -> %r %r. Error: %s', issue_pat, issue_server_link, issue_sub, str(e))
1174 issue_text = match_obj.group()
1175
1163 return (
1176 return (
1164 '<a class="issue-tracker-link" href="%(url)s">'
1177 '<a class="issue-tracker-link" href="%(url)s">'
1165 '%(issue-prefix)s%(id-repr)s'
1178 '%(text)s'
1166 '</a>'
1179 '</a>'
1167 ) % {
1180 ) % {
1168 'url': issue_url,
1181 'url': issue_url,
1169 'id-repr': issue_id,
1182 'text': issue_text,
1170 'issue-prefix': issue_prefix,
1171 'serv': issue_server_link,
1172 }
1183 }
1173 tmp_urlify_issues_f = (lambda s,
1184 tmp_urlify_issues_f = (lambda s,
1174 issue_re=issue_re, issues_replace=issues_replace, chain_f=tmp_urlify_issues_f:
1185 issue_re=issue_re, issues_replace=issues_replace, chain_f=tmp_urlify_issues_f:
@@ -261,37 +261,44 b' default_encoding = utf8'
261 <%text>## issue tracker for Kallithea (leave blank to disable, absent for default)</%text>
261 <%text>## issue tracker for Kallithea (leave blank to disable, absent for default)</%text>
262 #bugtracker = https://bitbucket.org/conservancy/kallithea/issues
262 #bugtracker = https://bitbucket.org/conservancy/kallithea/issues
263
263
264 <%text>## issue tracking mapping for commits messages</%text>
264 <%text>## issue tracking mapping for commit messages, comments, PR descriptions, ...</%text>
265 <%text>## comment out issue_pat, issue_server, issue_prefix to enable</%text>
265 <%text>## Refer to the documentation ("Integration with issue trackers") for more details.</%text>
266
266
267 <%text>## pattern to get the issues from commit messages</%text>
267 <%text>## regular expression to match issue references</%text>
268 <%text>## default one used here is #<numbers> with a regex passive group for `#`</%text>
268 <%text>## This pattern may/should contain parenthesized groups, that can</%text>
269 <%text>## {id} will be all groups matched from this pattern</%text>
269 <%text>## be referred to in issue_server_link or issue_sub using Python backreferences</%text>
270 <%text>## (e.g. \1, \2, ...). You can also create named groups with '(?P<groupname>)'.</%text>
270 <%text>## To require mandatory whitespace before the issue pattern, use:</%text>
271 <%text>## To require mandatory whitespace before the issue pattern, use:</%text>
271 <%text>## (?:^|(?<=\s)) before the actual pattern, and for mandatory whitespace</%text>
272 <%text>## (?:^|(?<=\s)) before the actual pattern, and for mandatory whitespace</%text>
272 <%text>## behind the issue pattern, use (?:$|(?=\s)) after the actual pattern</%text>
273 <%text>## behind the issue pattern, use (?:$|(?=\s)) after the actual pattern.</%text>
273
274
274 issue_pat = #(\d+)
275 issue_pat = #(\d+)
275
276
276 <%text>## server url to the issue, each {id} will be replaced with match</%text>
277 <%text>## server url to the issue</%text>
277 <%text>## fetched from the regex and {repo} is replaced with full repository name</%text>
278 <%text>## This pattern may/should contain backreferences to parenthesized groups in issue_pat.</%text>
278 <%text>## including groups {repo_name} is replaced with just name of repo</%text>
279 <%text>## A backreference can be \1, \2, ... or \g<groupname> if you specified a named group</%text>
279
280 <%text>## called 'groupname' in issue_pat.</%text>
280 issue_server_link = https://issues.example.com/{repo}/issue/{id}
281 <%text>## The special token {repo} is replaced with the full repository name</%text>
282 <%text>## including repository groups, while {repo_name} is replaced with just</%text>
283 <%text>## the name of the repository.</%text>
281
284
282 <%text>## prefix to add to link to indicate it's an url</%text>
285 issue_server_link = https://issues.example.com/{repo}/issue/\1
283 <%text>## #314 will be replaced by <issue_prefix><id></%text>
284
286
285 issue_prefix = #
287 <%text>## substitution pattern to use as the link text</%text>
288 <%text>## If issue_sub is empty, the text matched by issue_pat is retained verbatim</%text>
289 <%text>## for the link text. Otherwise, the link text is that of issue_sub, with any</%text>
290 <%text>## backreferences to groups in issue_pat replaced.</%text>
286
291
287 <%text>## issue_pat, issue_server_link, issue_prefix can have suffixes to specify</%text>
292 issue_sub =
293
294 <%text>## issue_pat, issue_server_link and issue_sub can have suffixes to specify</%text>
288 <%text>## multiple patterns, to other issues server, wiki or others</%text>
295 <%text>## multiple patterns, to other issues server, wiki or others</%text>
289 <%text>## below an example how to create a wiki pattern</%text>
296 <%text>## below an example how to create a wiki pattern</%text>
290 # wiki-some-id -> https://wiki.example.com/some-id
297 # wiki-some-id -> https://wiki.example.com/some-id
291
298
292 #issue_pat_wiki = (?:wiki-)(.+)
299 #issue_pat_wiki = wiki-(\S+)
293 #issue_server_link_wiki = https://wiki.example.com/{id}
300 #issue_server_link_wiki = https://wiki.example.com/\1
294 #issue_prefix_wiki = WIKI-
301 #issue_sub_wiki = WIKI-\1
295
302
296 <%text>## alternative return HTTP header for failed authentication. Default HTTP</%text>
303 <%text>## alternative return HTTP header for failed authentication. Default HTTP</%text>
297 <%text>## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with</%text>
304 <%text>## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with</%text>
@@ -406,84 +406,91 b' class TestLibs(TestController):'
406 from kallithea.lib.helpers import urlify_text
406 from kallithea.lib.helpers import urlify_text
407 assert urlify_text(sample, 'repo_name', link_='#the-link') == expected
407 assert urlify_text(sample, 'repo_name', link_='#the-link') == expected
408
408
409 @parametrize('issue_pat,issue_server,issue_prefix,sample,expected', [
409 @parametrize('issue_pat,issue_server,issue_sub,sample,expected', [
410 (r'#(\d+)', 'http://foo/{repo}/issue/{id}', '#',
410 (r'#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1',
411 'issue #123 and issue#456',
411 'issue #123 and issue#456',
412 """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """
412 """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """
413 """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""),
413 """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""),
414 # following test case shows the result of a backward incompatible change that was made: the
414 (r'(?:\s*#)(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1',
415 # space between 'issue' and '#123' is removed, because the space is part of the pattern.
416 (r'(?:\s*#)(\d+)', 'http://foo/{repo}/issue/{id}', '#',
417 'issue #123 and issue#456',
415 'issue #123 and issue#456',
418 """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """
416 """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """
419 """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""),
417 """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""),
420 # to require whitespace before the issue reference, one may be tempted to use \b...
418 # to require whitespace before the issue reference, one may be tempted to use \b...
421 (r'\bPR(\d+)', 'http://foo/{repo}/issue/{id}', '#',
419 (r'\bPR(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1',
422 'issue PR123 and issuePR456',
420 'issue PR123 and issuePR456',
423 """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """
421 """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """
424 """issuePR456"""),
422 """issuePR456"""),
425 # ... but it turns out that \b does not work well in combination with '#': the expectations
423 # ... but it turns out that \b does not work well in combination with '#': the expectations
426 # are reversed from what is actually happening.
424 # are reversed from what is actually happening.
427 (r'\b#(\d+)', 'http://foo/{repo}/issue/{id}', '#',
425 (r'\b#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1',
428 'issue #123 and issue#456',
426 'issue #123 and issue#456',
429 """issue #123 and """
427 """issue #123 and """
430 """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""),
428 """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/456">#456</a>"""),
431 # ... so maybe try to be explicit? Unfortunately the whitespace before the issue
429 # ... so maybe try to be explicit? Unfortunately the whitespace before the issue
432 # reference is not retained, again, because it is part of the pattern.
430 # reference is not retained, again, because it is part of the pattern.
433 (r'(?:^|\s)#(\d+)', 'http://foo/{repo}/issue/{id}', '#',
431 (r'(?:^|\s)#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1',
434 '#15 and issue #123 and issue#456',
432 '#15 and issue #123 and issue#456',
435 """<a class="issue-tracker-link" href="http://foo/repo_name/issue/15">#15</a> and """
433 """<a class="issue-tracker-link" href="http://foo/repo_name/issue/15">#15</a> and """
436 """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """
434 """issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """
437 """issue#456"""),
435 """issue#456"""),
438 # ... instead, use lookbehind assertions.
436 # ... instead, use lookbehind assertions.
439 (r'(?:^|(?<=\s))#(\d+)', 'http://foo/{repo}/issue/{id}', '#',
437 (r'(?:^|(?<=\s))#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1',
440 '#15 and issue #123 and issue#456',
438 '#15 and issue #123 and issue#456',
441 """<a class="issue-tracker-link" href="http://foo/repo_name/issue/15">#15</a> and """
439 """<a class="issue-tracker-link" href="http://foo/repo_name/issue/15">#15</a> and """
442 """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """
440 """issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> and """
443 """issue#456"""),
441 """issue#456"""),
444 (r'(?:pullrequest|pull request|PR|pr) ?#?(\d+)', 'http://foo/{repo}/issue/{id}', 'PR#',
442 (r'(?:pullrequest|pull request|PR|pr) ?#?(\d+)', 'http://foo/{repo}/issue/\\1', 'PR#\\1',
445 'fixed with pullrequest #1, pull request#2, PR 3, pr4',
443 'fixed with pullrequest #1, pull request#2, PR 3, pr4',
446 """fixed with <a class="issue-tracker-link" href="http://foo/repo_name/issue/1">PR#1</a>, """
444 """fixed with <a class="issue-tracker-link" href="http://foo/repo_name/issue/1">PR#1</a>, """
447 """<a class="issue-tracker-link" href="http://foo/repo_name/issue/2">PR#2</a>, """
445 """<a class="issue-tracker-link" href="http://foo/repo_name/issue/2">PR#2</a>, """
448 """<a class="issue-tracker-link" href="http://foo/repo_name/issue/3">PR#3</a>, """
446 """<a class="issue-tracker-link" href="http://foo/repo_name/issue/3">PR#3</a>, """
449 """<a class="issue-tracker-link" href="http://foo/repo_name/issue/4">PR#4</a>"""),
447 """<a class="issue-tracker-link" href="http://foo/repo_name/issue/4">PR#4</a>"""),
450 (r'#(\d+)', 'http://foo/{repo}/issue/{id}', 'PR',
448 (r'#(\d+)', 'http://foo/{repo}/issue/\\1', 'PR\\1',
451 'interesting issue #123',
449 'interesting issue #123',
452 """interesting issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">PR123</a>"""),
450 """interesting issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">PR123</a>"""),
453 (r'BUG\d{5}', 'https://bar/{repo}/{id}', 'BUG',
451 (r'BUG\d{5}', 'https://bar/{repo}/\\1', '\\1',
454 'silly me, I did not parenthesize the {id}, BUG12345.',
452 'silly me, I did not parenthesize the id, BUG12345.',
455 """silly me, I did not parenthesize the {id}, <a class="issue-tracker-link" href="https://bar/repo_name/">BUG</a>."""),
453 """silly me, I did not parenthesize the id, <a class="issue-tracker-link" href="https://bar/repo_name/\\1">BUG12345</a>."""),
456 (r'BUG(\d{5})', 'https://bar/{repo}/', 'BUG',
454 (r'BUG(\d{5})', 'https://bar/{repo}/', 'BUG\\1',
457 'silly me, the URL does not contain {id}, BUG12345.',
455 'silly me, the URL does not contain id, BUG12345.',
458 """silly me, the URL does not contain {id}, <a class="issue-tracker-link" href="https://bar/repo_name/">BUG12345</a>."""),
456 """silly me, the URL does not contain id, <a class="issue-tracker-link" href="https://bar/repo_name/">BUG12345</a>."""),
459 (r'(PR-\d+)', 'http://foo/{repo}/issue/{id}', '',
457 (r'(PR-\d+)', 'http://foo/{repo}/issue/\\1', '',
460 'interesting issue #123, err PR-56',
458 'interesting issue #123, err PR-56',
461 """interesting issue #123, err <a class="issue-tracker-link" href="http://foo/repo_name/issue/PR-56">PR-56</a>"""),
459 """interesting issue #123, err <a class="issue-tracker-link" href="http://foo/repo_name/issue/PR-56">PR-56</a>"""),
462 (r'#(\d+)', 'http://foo/{repo}/issue/{id}', '#',
460 (r'#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1',
463 "some 'standard' text with apostrophes",
461 "some 'standard' text with apostrophes",
464 """some &#39;standard&#39; text with apostrophes"""),
462 """some &#39;standard&#39; text with apostrophes"""),
465 (r'#(\d+)', 'http://foo/{repo}/issue/{id}', '#',
463 (r'#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1',
466 "some 'standard' issue #123",
464 "some 'standard' issue #123",
467 """some &#39;standard&#39; issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a>"""),
465 """some &#39;standard&#39; issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a>"""),
468 (r'#(\d+)', 'http://foo/{repo}/issue/{id}', '#',
466 (r'#(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1',
469 'an issue #123 with extra whitespace',
467 'an issue #123 with extra whitespace',
470 """an issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> with extra whitespace"""),
468 """an issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> with extra whitespace"""),
471 # Note: whitespace is squashed
469 (r'(?:\s*#)(\d+)', 'http://foo/{repo}/issue/\\1', '#\\1',
472 (r'(?:\s*#)(\d+)', 'http://foo/{repo}/issue/{id}', '#',
473 'an issue #123 with extra whitespace',
470 'an issue #123 with extra whitespace',
474 """an issue <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> with extra whitespace"""),
471 """an issue<a class="issue-tracker-link" href="http://foo/repo_name/issue/123">#123</a> with extra whitespace"""),
475 # invalid issue pattern
472 # invalid issue pattern
476 (r'(PR\d+', 'http://foo/{repo}/issue/{id}', '',
473 (r'(PR\d+', 'http://foo/{repo}/issue/{id}', '',
477 'PR135',
474 'PR135',
478 """PR135"""),
475 """PR135"""),
476 # other character than #
477 (r'(?:^|(?<=\s))\$(\d+)', 'http://foo/{repo}/issue/\\1', '',
478 'empty issue_sub $123 and issue$456',
479 """empty issue_sub <a class="issue-tracker-link" href="http://foo/repo_name/issue/123">$123</a> and """
480 """issue$456"""),
481 # named groups
482 (r'(PR|pullrequest|pull request) ?(?P<sitecode>BRU|CPH|BER)-(?P<id>\d+)', 'http://foo/\g<sitecode>/pullrequest/\g<id>/', 'PR-\g<sitecode>-\g<id>',
483 'pullrequest CPH-789 is similar to PRBRU-747',
484 """<a class="issue-tracker-link" href="http://foo/CPH/pullrequest/789/">PR-CPH-789</a> is similar to """
485 """<a class="issue-tracker-link" href="http://foo/BRU/pullrequest/747/">PR-BRU-747</a>"""),
479 ])
486 ])
480 def test_urlify_issues(self, issue_pat, issue_server, issue_prefix, sample, expected):
487 def test_urlify_issues(self, issue_pat, issue_server, issue_sub, sample, expected):
481 from kallithea.lib.helpers import urlify_text
488 from kallithea.lib.helpers import urlify_text
482 config_stub = {
489 config_stub = {
483 'sqlalchemy.url': 'foo',
490 'sqlalchemy.url': 'foo',
484 'issue_pat': issue_pat,
491 'issue_pat': issue_pat,
485 'issue_server_link': issue_server,
492 'issue_server_link': issue_server,
486 'issue_prefix': issue_prefix,
493 'issue_sub': issue_sub,
487 }
494 }
488 # force recreation of lazy function
495 # force recreation of lazy function
489 with mock.patch('kallithea.lib.helpers._urlify_issues_f', None):
496 with mock.patch('kallithea.lib.helpers._urlify_issues_f', None):
@@ -496,7 +503,7 b' class TestLibs(TestController):'
496 ('pull request7 #', '<a class="issue-tracker-link" href="http://pr/repo_name/pr/7">PR#7</a> #'),
503 ('pull request7 #', '<a class="issue-tracker-link" href="http://pr/repo_name/pr/7">PR#7</a> #'),
497 ('look PR9 and pr #11', 'look <a class="issue-tracker-link" href="http://pr/repo_name/pr/9">PR#9</a> and <a class="issue-tracker-link" href="http://pr/repo_name/pr/11">PR#11</a>'),
504 ('look PR9 and pr #11', 'look <a class="issue-tracker-link" href="http://pr/repo_name/pr/9">PR#9</a> and <a class="issue-tracker-link" href="http://pr/repo_name/pr/11">PR#11</a>'),
498 ('pullrequest#10 solves issue 9', '<a class="issue-tracker-link" href="http://pr/repo_name/pr/10">PR#10</a> solves <a class="issue-tracker-link" href="http://bug/repo_name/bug/9">bug#9</a>'),
505 ('pullrequest#10 solves issue 9', '<a class="issue-tracker-link" href="http://pr/repo_name/pr/10">PR#10</a> solves <a class="issue-tracker-link" href="http://bug/repo_name/bug/9">bug#9</a>'),
499 ('issue FAIL67', 'issue <a class="issue-tracker-link" href="http://fail/repo_name/67">67</a>'),
506 ('issue FAIL67', 'issue <a class="issue-tracker-link" href="http://fail/repo_name/67">FAIL67</a>'),
500 ('issue FAILMORE89', 'issue FAILMORE89'), # no match because absent prefix
507 ('issue FAILMORE89', 'issue FAILMORE89'), # no match because absent prefix
501 ])
508 ])
502 def test_urlify_issues_multiple_issue_patterns(self, sample, expected):
509 def test_urlify_issues_multiple_issue_patterns(self, sample, expected):
@@ -504,19 +511,19 b' class TestLibs(TestController):'
504 config_stub = {
511 config_stub = {
505 'sqlalchemy.url': 'foo',
512 'sqlalchemy.url': 'foo',
506 'issue_pat': 'X(\d+)',
513 'issue_pat': 'X(\d+)',
507 'issue_server_link': 'http://main/{repo}/main/{id}/',
514 'issue_server_link': 'http://main/{repo}/main/\\1/',
508 'issue_prefix': '#',
515 'issue_sub': '#\\1',
509 'issue_pat_pr': '(?:pullrequest|pull request|PR|pr) ?#?(\d+)',
516 'issue_pat_pr': '(?:pullrequest|pull request|PR|pr) ?#?(\d+)',
510 'issue_server_link_pr': 'http://pr/{repo}/pr/{id}',
517 'issue_server_link_pr': 'http://pr/{repo}/pr/\\1',
511 'issue_prefix_pr': 'PR#',
518 'issue_sub_pr': 'PR#\\1',
512 'issue_pat_bug': '(?:BUG|bug|issue) ?#?(\d+)',
519 'issue_pat_bug': '(?:BUG|bug|issue) ?#?(\d+)',
513 'issue_server_link_bug': 'http://bug/{repo}/bug/{id}',
520 'issue_server_link_bug': 'http://bug/{repo}/bug/\\1',
514 'issue_prefix_bug': 'bug#',
521 'issue_sub_bug': 'bug#\\1',
515 'issue_pat_empty_prefix': 'FAIL(\d+)',
522 'issue_pat_empty_prefix': 'FAIL(\d+)',
516 'issue_server_link_empty_prefix': 'http://fail/{repo}/{id}',
523 'issue_server_link_empty_prefix': 'http://fail/{repo}/\\1',
517 'issue_prefix_empty_prefix': '',
524 'issue_sub_empty_prefix': '',
518 'issue_pat_absent_prefix': 'FAILMORE(\d+)',
525 'issue_pat_absent_prefix': 'FAILMORE(\d+)',
519 'issue_server_link_absent_prefix': 'http://failmore/{repo}/{id}',
526 'issue_server_link_absent_prefix': 'http://failmore/{repo}/\\1',
520 }
527 }
521 # force recreation of lazy function
528 # force recreation of lazy function
522 with mock.patch('kallithea.lib.helpers._urlify_issues_f', None):
529 with mock.patch('kallithea.lib.helpers._urlify_issues_f', None):
General Comments 0
You need to be logged in to leave comments. Login now